加入有限的子查询?

时间:2009-09-10 00:43:24

标签: sql join sqlite subquery greatest-n-per-group

我在SQLite3数据库中有这个releases表,列出了每个发布的应用程序版本:

|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    |

我想显示应用程序的名称和最新版本,其中“最新”表示(a)最新的release_date,如果有重复项,(b)最高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方式来实现这一点。

5 个答案:

答案 0 :(得分:1)

使用分析函数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子查询以捕获“最新”的更复杂含义。

答案 1 :(得分:1)

这是“每组最大的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;

答案 2 :(得分:0)

这很难看,但我认为它会起作用

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

我希望有一些方法可以在一个嵌入式选择中获得这两个列,但我不知道。

答案 3 :(得分:0)

尝试:

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

答案 4 :(得分:0)

第二次尝试。假设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 IN 
(SELECT Max(release_id) FROM releases
GROUP BY app_id);