Виждам, че много хора използват подзаявки или прозоречни функции, за да направят това, но често правя този вид заявка без подзаявки по следния начин. Той използва обикновен, стандартен 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;
Повторен коментар от @Farhan:
Ето по-подробно обяснение:
Външно присъединяване се опитва да се присъедини към t1
с t2
. По подразбиране всички резултати от t1
се връщат и if има съвпадение в 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
.