Я бачу, що багато людей використовують підзапроси або інші функції для постачальника для цього, але я часто роблю такий запит без підзаписів наступним чином. Він використовує звичайний, стандартний 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
.