Pilih baris berdasarkan tanggal terbaru dengan banyak gabungan

8

Saya punya pertanyaan ini ( 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
 )

Masalah 1

Saya ingin mendaftar semua perusahaan, dan menunjukkan kepada pengguna dan status di mana mereka terakhir melakukan tindakan pada perusahaan. Pada saat yang sama tunjukkan kepada perusahaan di mana tidak ada tindakan telah dilakukan.

Masalah 2

Saya juga harus dapat mencari pengguna dengan nama, sehingga memilih semua perusahaan jika pengguna ini memiliki aktivitas terakhir. Kueri dibuat dari formulir, jadi saya bisa menyuntikkan variabel.


Saya tidak dapat mengubah database SCHEMA saat ini, tetapi saran untuk migrasi di masa mendatang jauh lebih disukai.

Saya sudah mencoba mengikatnya INNER JOIN ( SELECT.. ) t ONtetapi saya tidak bisa mengatasinya.

Saya juga sudah mencoba metode dari sini , di sini , dan di sini, tetapi saya tidak dapat membuat orang tersebut melakukan aktivitas terbaru dengan benar.

Versi MySQL: 5.5.16. Tabel perusahaan memiliki sekitar 1 baris baris, dan tabel aksi berada pada 70K, tumbuh. Kinerja penting bagi saya di sini.

Bagaimana ini bisa diselesaikan?

stiq
sumber
2
Lihat pertanyaan ini: Kueri dioptimalkan MySQL Alih-alih MAX(Marks)per TaskID, Anda ingin MAX(ActivityDate)per Company.) Tidak ada banyak perbedaan jika Anda memiliki satu tabel atau bergabung.
ypercubeᵀᴹ

Jawaban:

7

Permintaan Anda dapat disederhanakan menjadi:

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

Indeks pada (deleted, company_id, rtime)akan membuat subquery tabel turunan efisien. Saya kira Anda sudah memiliki indeks pada kolom yang digunakan untuk bergabung dan Company (name).

SQL-Fiddle

ypercubeᵀᴹ
sumber
1
Ini lebih sederhana, dan sepertinya sekitar 0,3 detik lebih cepat per 100 baris daripada jawaban saya.
stiq
2

Setelah mencoba jawaban yang berbeda, saya membuatnya berfungsi, tetapi saya merasa lambat dalam pengaturan saya.

ypercube menunjuk saya ke jawaban yang menunjuk ke pertanyaan serupa lainnya tentang bagaimana MySQL mengelompokkan hasil. Ini membawa saya ke sumber daya ini di mana saya akhirnya mengerti apa yang sedang terjadi.

Hasilnya berupa kueri seperti ini:

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

Berikut biola dengan kueri yang berfungsi

stiq
sumber
1

Anda perlu memindahkan tabel pengguna, tindakan, dan status ke dalam sub-kueri, seperti ini:

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
sumber