Seleccione filas según la última fecha con múltiples combinaciones

8

Tengo 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

Quiero enumerar todas las empresas y mostrarle al usuario y el estado en el que realizaron la última acción en la empresa. Al mismo tiempo, muestre a las compañías dónde no se han realizado acciones.

Problema 2

También necesito poder buscar al usuario por nombre, seleccionando así todas las compañías donde este usuario tuvo la última actividad. La consulta se realiza desde un formulario, por lo que puedo inyectar variables.


No puedo cambiar la base de datos SCHEMA en este momento, pero los consejos para una futura migración son muy apreciados.

He intentado unirlo, INNER JOIN ( SELECT.. ) t ONpero no puedo entenderlo.

También probé métodos de aquí , de aquí y de aquí, pero no puedo entender bien a la persona con la última actividad.

Versión de MySQL: 5.5.16. La mesa de la compañía tiene aproximadamente 1 millón de filas, y la mesa de acción está en 70K, creciendo. El rendimiento es importante para mí aquí.

¿Cómo se puede resolver esto?

stiq
fuente
2
Vea esta pregunta: consulta optimizada de MySQL En lugar de MAX(Marks)por TaskID, usted quiere MAX(ActivityDate)por Company.) No hay mucha diferencia si tiene una tabla o una combinación.
ypercubeᵀᴹ

Respuestas:

7

Su consulta se puede simplificar 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%' ;

Un índice (deleted, company_id, rtime)activado haría eficiente la subconsulta de la tabla derivada. Supongo que ya tiene índices en las columnas utilizadas para las uniones y en el Company (name).

SQL-Fiddle

ypercubeᵀᴹ
fuente
1
Esto es más simple y parece aproximadamente 0.3 segundos más rápido por cada 100 filas que mi respuesta.
stiq
2

Después de probar las diferentes respuestas, lo puse a funcionar, pero lo encontré lento en mi configuración.

ypercube me señaló una respuesta que apuntaba a otras preguntas similares sobre cómo MySQL está agrupando los resultados. Esto me llevó a este recurso donde finalmente entendí lo que estaba pasando.

El resultado es una 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%'

Aquí hay un violín con la consulta de trabajo

stiq
fuente
1

Debe mover las tablas de usuario, acción y estado a una subconsulta, como esta:

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

Violín de SQL

cha
fuente