Я бачу, що багато людей використовують підзапроси або інші функції для постачальника для цього, але я часто роблю такий запит без підзаписів наступним чином. Він використовує звичайний, стандартний SQL, тому він повинен працювати в будь-якій марці RDBMS.
SELECT t1.*
FROM mytable t1
LEFT OUTER JOIN mytable t2
ON (t1.UserId = t2.UserId AND t1."Date" < t2."Date")
WHERE t2.UserId IS NULL;
Іншими словами: витягніть рядок, t1звідки не існує іншого рядка з такою ж UserIdі більшою датою.
(Я ідентифікатор "Дата" ставлю в роздільники, оскільки це зарезервоване слово SQL.)
У разі, якщо t1."Date" = t2."Date"з'являється подвоєння. Зазвичай таблиці мають auto_inc(seq)ключ, наприклад id. Щоб уникнути подвоєння можна використовувати наступне:
SELECT t1.*
FROM mytable t1
LEFT OUTER JOIN mytable t2
ON t1.UserId = t2.UserId AND ((t1."Date" < t2."Date")
OR (t1."Date" = t2."Date" AND t1.id < t2.id))
WHERE t2.UserId IS NULL;
Re коментар від @Farhan:
Ось більш детальне пояснення:
Зовнішнє з'єднання намагається приєднатися t1до t2. За замовчуванням t1повертаються всі результати , а якщо є збіг t2, він також повертається. Якщо t2для даного рядка немає відповідності t1, то запит все одно повертає рядок t1і використовує NULLяк заповнювач для всіх t2стовпців '. Ось так працюють зовнішні з'єднання загалом.
Трюк у цьому запиті полягає в тому, щоб створити умову узгодження з'єднання таким, яке t2має відповідати однаковому userid та більшому date . Ідея полягає в тому, що якщо рядок існує в t2такому, що має більше date, то рядок у t1ньому порівняно з не може бути найбільшим dateдля цього userid. Але якщо немає відповідності - тобто, якщо немає рядка у t2більшому, dateніж рядок у t1-, ми знаємо, що рядок у t1був ряд з найбільшою dateдля даної userid.
У тих випадках (коли немає відповідності), стовпці t2будуть NULL- навіть стовпці, зазначені в умові приєднання. Тому ми використовуємо це WHERE t2.UserId IS NULL, оскільки ми шукаємо випадки, коли не було знайдено рядків із більшим dateдля даного userid.