Присоединение к ограниченному подзапросу? - PullRequest
1 голос
/ 10 сентября 2009

У меня есть эта 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    |

Я хочу отобразить название приложения и новейшую версию, где «самая новая» означает (а) новейшую дату выпуска и, если есть дубликаты, (б) наивысший идентификатор выпуска.

Я могу сделать это для индивидуального применения:

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.

Ответы [ 5 ]

1 голос
/ 10 сентября 2009

Это проблема "наибольшего N на группу". Это происходит несколько раз в неделю на StackOverflow.

Я обычно использую решение, подобное тому, которое было в @Steve Kass ' answer , но я делаю это без подзапросов (я привык к 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;
1 голос
/ 10 сентября 2009

Это легко сделать с помощью аналитической функции 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, чтобы получить более сложное значение «последний».

0 голосов
/ 10 сентября 2009

Ошибка второй попытки. Предполагая, что идентификаторы увеличиваются монотонно и переполнение не является вероятным явлением, вы можете игнорировать дату и просто сделать:

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);
0 голосов
/ 10 сентября 2009

Попробуйте:

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
0 голосов
/ 10 сентября 2009

Это некрасиво, но я думаю, что это сработает

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

Я надеюсь, что есть какой-то способ объединить оба этих столбца в один встроенный выбор, но я этого не знаю.

...