Присоединение к ограниченному подзапросу?
-
05-07-2019 - |
Вопрос
у меня есть это releases
таблица в базе данных SQLite3, в которой перечислены все выпущенные версии приложения:
|release_id|release_date|app_id|
|==========|============|======|
| 1001| 2009-01-01 | 1|
| 1003| 2009-01-01 | 1|
| 1004| 2009-02-02 | 2|
| 1005| 2009-01-15 | 1|
Таким образом, для каждого app_id будет несколько строк.У меня есть еще один стол, apps
:
|app_id|name |
|======|========|
| 1|Everest |
| 2|Fuji |
Я хочу отобразить название приложения и новейшую версию, где «новейшая» означает (а) новейшую дату_выпуска, а если есть дубликаты, (б) самый высокий Release_id.
Я могу сделать это для отдельного приложения:
SELECT apps.name,releases.release_id,releases.release_date
FROM apps
INNER JOIN releases
ON apps.app_id = releases.app_id
WHERE releases.release_id = 1003
ORDER BY releases.release_date,releases.release_id
LIMIT 1
но, конечно, этот ORDER BY применяется ко всему запросу SELECT, и если я оставлю предложение WHERE, он все равно вернет только одну строку.
Это однократный запрос к небольшой базе данных, поэтому медленные запросы, временные таблицы и т. д.все в порядке - я просто не могу понять, как это сделать с помощью SQL.
Решение
Это легко сделать с помощью аналитической функции ROW_NUMBER (), которую, я думаю, sqlite3 не поддерживает. Но вы можете сделать это более гибким способом, чем в предыдущих ответах:
SELECT
apps.name,
releases.release_id,
releases.release_date
FROM apps INNER JOIN releases
ON apps.app_id = releases.app_id
WHERE NOT EXISTS (
-- // where there doesn't exist a more recent release for the same app
SELECT * FROM releases AS R
WHERE R.app_id = apps.app_id
AND R.release_data > releases.release_data
)
Например, если у вас было несколько столбцов заказа, которые определяют " последний, " MAX не будет работать для вас, но вы можете изменить подзапрос EXISTS, чтобы он улавливал более сложное значение "Quest; latest".
Другие советы
Это "наибольшее N на группу" проблема. Это происходит несколько раз в неделю на StackOverflow. Р>
Обычно я использую решение, подобное тому, которое есть в ответе @Steve Kass <, но я делаю это без подзапросов (я привык к MySQL 4.0, который не поддерживал подзапросы):
SELECT a.name, r1.release_id, r1.release_date
FROM apps a
INNER JOIN releases r1
LEFT OUTER JOIN releases r2 ON (r1.app_id = r2.app_id
AND (r1.release_date < r2.release_date
OR r1.release_date = r2.release_date AND r1.release_id < r2.release_id))
WHERE r2.release_id IS NULL;
Внутренне это, вероятно, оптимизирует идентично синтаксису NOT EXISTS
. Вы можете проанализировать запрос с помощью EXPLAIN
, чтобы убедиться в этом. р>
<Ч>
Повторяя свой комментарий, вы можете просто пропустить тест для release_date
, потому что release_id
так же полезен для установления хронологического порядка выпусков, и я предполагаю, что он гарантированно будет уникальный, так что это упрощает запрос:
SELECT a.name, r1.release_id, r1.release_date
FROM apps a
INNER JOIN releases r1
LEFT OUTER JOIN releases r2 ON (r1.app_id = r2.app_id
AND r1.release_id < r2.release_id)
WHERE r2.release_id IS NULL;
Это некрасиво, но я думаю, что это сработает
select apps.name, (select releases.release_id from releases where releases.app_id=apps.app_id order by releases.release_date, releases.release_id), (select releases.release_date from releases where releases.app_id=apps.app_id order by releases.release_date, releases.release_id) from apps order by apps.app_id
Я надеюсь, что есть какой-то способ объединить оба этих столбца в один встроенный выбор, но я этого не знаю.
Пытаться:
SELECT a.name,
t.max_release_id,
t.max_date
FROM APPS a
JOIN (SELECT t.app_id,
MAX(t.release_id) 'max_release_id',
t.max_date
FROM (SELECT r.app_id,
r.release_id,
MAX(r.release_date) 'max_date'
FROM RELEASES r
GROUP BY r.app_id, r.release_id)
GROUP BY t.app_id, t.max_date) t
Ошибка второй попытки. Предполагая, что идентификаторы увеличиваются монотонно и переполнение не является вероятным явлением, вы можете игнорировать дату и просто сделать:
SELECT apps.name, releases.release_id, releases.release_date
FROM apps INNER JOIN releases on apps.app_id = releases.app_id
WHERE releases.release_id IN
(SELECT Max(release_id) FROM releases
GROUP BY app_id);