Wählen Sie Zeilen basierend auf dem letzten Datum mit mehreren Verknüpfungen aus

8

Ich habe diese Abfrage ( 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
 )

Problem 1

Ich möchte alle Unternehmen auflisten und dem Benutzer und dem Status anzeigen, wo sie zuletzt eine Aktion für das Unternehmen ausgeführt haben. Zeigen Sie gleichzeitig den Unternehmen, wo keine Maßnahmen ergriffen wurden.

Problem 2

Ich muss auch in der Lage sein, den Benutzer anhand seines Namens zu suchen und dabei alle Unternehmen auszuwählen, bei denen dieser Benutzer die letzte Aktivität hatte. Die Abfrage erfolgt aus einem Formular, sodass ich Variablen einfügen kann.


Ich bin derzeit nicht in der Lage, die Datenbank SCHEMA zu ändern, aber Ratschläge für eine zukünftige Migration werden sehr geschätzt.

Ich habe versucht, es zusammenzubinden, INNER JOIN ( SELECT.. ) t ONaber ich kann es nicht verstehen.

Ich habe auch Methoden von hier , hier und hier ausprobiert , aber ich kann die Person mit der neuesten Aktivität nicht richtig finden.

MySQL-Version: 5.5.16. Die Firmentabelle hat ungefähr 1 Million Zeilen, und die Aktionstabelle ist bei 70.000 und wächst. Leistung ist mir hier wichtig.

Wie kann das gelöst werden?

stiq
quelle
2
Siehe diese Frage: MySQL-optimierte Abfrage Anstelle von MAX(Marks)per TaskIDmöchten Sie MAX(ActivityDate)per Company.) Es gibt keinen großen Unterschied, ob Sie eine Tabelle oder einen Join haben.
Ypercubeᵀᴹ

Antworten:

7

Ihre Anfrage kann vereinfacht werden zu:

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

Ein Index auf (deleted, company_id, rtime)würde die abgeleitete Tabellenunterabfrage effizient machen. Ich nehme an, Sie haben bereits Indizes für die Spalten, die für die Joins verwendet werden, und für die Company (name).

SQL-Geige

ypercubeᵀᴹ
quelle
1
Dies ist einfacher und scheint pro 100 Zeilen etwa 0,3 Sekunden schneller zu sein als meine Antwort.
Stiq
2

Nachdem ich die verschiedenen Antworten ausprobiert hatte, funktionierte es, aber ich fand es langsam in meinem Setup.

ypercube hat mich auf eine Antwort hingewiesen, die auf andere ähnliche Fragen zur Gruppierung der Ergebnisse durch MySQL verweist. Dies führte mich zu dieser Ressource, wo ich endlich verstand, was los war.

Das Ergebnis ist eine Abfrage wie folgt:

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

Hier ist eine Geige mit der Arbeitsabfrage

stiq
quelle
1

Sie müssen Benutzer-, Aktions- und Statustabellen wie folgt in eine Unterabfrage verschieben:

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
quelle