Selecionar linhas com base na data mais recente com várias junções

8

Eu tenho esta consulta ( 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
 )

Problema 1

Quero listar todas as empresas e mostrar o usuário e o status de onde eles fizeram uma ação pela última vez. Ao mesmo tempo, mostre às empresas onde nenhuma ação foi realizada.

Problema 2

Também preciso procurar o usuário pelo nome, selecionando todas as empresas em que esse usuário teve a última atividade. A consulta é feita a partir de um formulário, para que eu possa injetar variáveis.


Não estou conseguindo alterar o SCHEMA do banco de dados no momento, mas os conselhos para uma migração futura são muito apreciados.

Eu tentei vinculá-lo, INNER JOIN ( SELECT.. ) t ONmas não consigo entender.

Eu também tentei métodos daqui , aqui e aqui, mas não consigo acertar a pessoa com as atividades mais recentes.

Versão do MySQL: 5.5.16. A tabela da empresa possui cerca de 1 milhão de linhas e a tabela de ações está em 70K, crescendo. O desempenho é importante para mim aqui.

como isso pode ser resolvido?

stiq
fonte
2
Veja esta pergunta: Consulta otimizada do MySQL Em vez de MAX(Marks)por TaskID, você deseja MAX(ActivityDate)por Company.) Não há muita diferença se você tiver uma tabela ou uma junção.
usar o seguinte comando

Respostas:

7

Sua consulta pode ser simplificada para:

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%' ;

Um índice (deleted, company_id, rtime)ativado tornaria a subconsulta da tabela derivada eficiente. Suponho que você já tenha índices nas colunas usadas para as junções e no Company (name).

SQL-Fiddle

ypercubeᵀᴹ
fonte
11
Isso é mais simples e parece cerca de 0,3 segundos mais rápido por 100 linhas do que minha resposta.
Stiq
2

Depois de experimentar as diferentes respostas, consegui que funcionasse, mas achei lento na minha configuração.

O ypercube me indicou uma resposta que apontou para outras perguntas semelhantes sobre como o MySQL está agrupando resultados. Isso me levou a esse recurso, onde finalmente entendi o que estava acontecendo.

O resultado é uma consulta como esta:

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%'

Aqui está um violino com a consulta de trabalho

stiq
fonte
1

Você precisa mover as tabelas de usuário, ação e status para uma subconsulta, assim:

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
fonte