Sélectionnez les lignes en fonction de la dernière date avec plusieurs jointures

8

J'ai cette requête ( SQLFiddle ):

SELECT
c.name,
a.user_id,
a.status_id,
a.title,
a.rtime,
u.user_name,
s.status_name

FROM company c

LEFT JOIN action a ON a.company_id=c.id
LEFT JOIN user u ON u.id=a.user_id
LEFT JOIN status s ON s.id=a.status_id



WHERE u.user_name='Morgan'

-- WHERE c.name='Fiddle'

GROUP BY c.id

HAVING a.rtime IS NULL OR a.rtime = (
 SELECT max(rtime)
 FROM action a2
 WHERE deleted IS NULL
 AND a2.company_id = c.id
 )

Problème 1

Je souhaite répertorier toutes les sociétés et afficher l'utilisateur et le statut de sa dernière action sur la société. En même temps, montrez aux entreprises où aucune action n'a été entreprise.

Problème 2

Je dois également pouvoir rechercher l'utilisateur par son nom, sélectionnant ainsi toutes les entreprises où cet utilisateur a eu la dernière activité. La requête est faite à partir d'un formulaire, donc je peux injecter des variables.


Je ne suis pas en mesure de modifier la base de données SCHEMA pour le moment, mais les conseils pour une future migration sont très appréciés.

J'ai essayé de le lier ensemble, INNER JOIN ( SELECT.. ) t ONmais je ne peux pas m'en sortir.

J'ai également essayé des méthodes d' ici , ici et ici, mais je n'arrive pas à obtenir la bonne personne avec la dernière activité.

Version MySQL: 5.5.16. La table d'entreprise a environ 1 million de lignes et la table d'action est à 70K, en croissance. La performance est importante pour moi ici.

Comment résoudre ce problème?

stiq
la source
2
Voir cette question: Requête optimisée MySQL Au lieu de MAX(Marks)per TaskID, vous voulez MAX(ActivityDate)per Company.) Il n'y a pas beaucoup de différence si vous avez une table ou une jointure.
ypercubeᵀᴹ

Réponses:

7

Votre requête peut être simplifiée pour:

SELECT
    c.id,
    c.name,
    a.rtime,
    s.status_name,
    u.user_name
FROM company c
    LEFT JOIN
      ( SELECT 
            company_id,
            MAX(rtime) AS maxdate
        FROM action
        WHERE deleted IS NULL
        GROUP BY company_id
      ) AS x ON x.company_id = c.id
    LEFT JOIN action a ON  a.deleted IS NULL
                       AND a.company_id = x.company_id 
                       AND a.rtime = x.maxdate 
    LEFT JOIN user u ON u.id = a.user_id
    LEFT JOIN status s ON s.id = a.status_id
WHERE 
    c.name LIKE 'Company%' ;

Un index sur (deleted, company_id, rtime)rendrait la sous-requête de table dérivée efficace. Je suppose que vous avez déjà des index sur les colonnes utilisées pour les jointures et sur le Company (name).

SQL-Fiddle

ypercubeᵀᴹ
la source
1
C'est plus simple et semble environ 0,3 seconde plus rapide pour 100 lignes que ma réponse.
stiq
2

Après avoir essayé les différentes réponses, je l'ai fait fonctionner, mais je l'ai trouvé lent dans ma configuration.

ypercube m'a pointé vers une réponse qui a pointé d'autres questions similaires sur la façon dont MySQL regroupe les résultats. Cela m'a conduit à cette ressource où j'ai finalement compris ce qui se passait.

Le résultat étant une requête comme celle-ci:

SELECT
c.id,
c.name,

a.rtime,

s.status_name,

u.user_name


FROM company c

LEFT JOIN (
    SELECT 
        a.company_id,
        a.rtime,
        a.user_id,
        a.status_id
        FROM
            (
            SELECT company_id,
 MAX(rtime) as maxdate
            FROM action
            WHERE deleted IS NULL
            GROUP BY company_id
            ) as x

        LEFT JOIN action a ON a.rtime=x.maxdate AND a.company_id=x.company_id

) as a ON a.company_id=c.id

LEFT JOIN user u ON u.id=a.user_id
LEFT JOIN status s ON s.id=a.status_id

WHERE (1)

-- Search by user
-- AND u.user_name LIKE 'Johnny'

-- Seach by company name
AND c.name LIKE 'Company%'

Voici un violon avec la requête de travail

stiq
la source
1

Vous devez déplacer les tableaux d'utilisateurs, d'actions et d'état dans une sous-requête, comme ceci:

SELECT
c.name,
a.user_id,
a.status_id,
a.title,
a.max_rtime,
a.user_name,
a.status_name

FROM company c

LEFT JOIN
(select company_id, user_id, 
 max(rtime) max_rtime, 
 title, status_id, s.status_name,
 u.user_name
 from action INNER JOIN
  status s ON s.id=action.status_id
  INNER JOIN user u ON u.id=action.user_id
 where action.deleted IS NULL
 group by company_id, user_id, status_id, s.status_name, title, u.user_name
) a ON a.company_id=c.id AND a.user_name='Morgan'


GROUP BY c.id

SQL Fiddle

cha
la source