一个SQLite查询中有两个sum和三个表

时间:2014-01-24 09:22:49

标签: sql sqlite join count aggregate-functions

我有三个表:活动,动作(每个动作是一个活动的执行)和照片(每个动作都可以附加照片)。

这是an SQL Fiddle for this

现在我想按降序检索活动,对于每项活动,我想要花费在其上的总时间和附加到它的总照片。使用其最后一次操作的停止时间计算的活动顺序。

例如,对于以下数据

activities
------------------
 _id |   title
------------------
   1 | Activity 1
   2 | Activity 2
   3 | Activity 3
   4 | Activity 4

actions
-------------------------------------------------------------
_id | activity_id |    date_started     |     date_stopped
-------------------------------------------------------------
  1 |           1 | 2014-01-23 20:45:03 | 2014-01-23 20:45:24
  2 |           2 | 2014-01-23 20:45:27 | 2014-01-23 20:45:29
  3 |           3 | 2014-01-23 20:45:31 | 2014-01-23 20:45:43
  4 |           1 | 2014-01-23 20:45:46 | 2014-01-23 20:45:48
  5 |           4 | 2014-01-23 20:45:50 | 2014-01-23 20:46:19

photos
--------------------------------------------------------
_id | action_id |      date_taken     |     path
--------------------------------------------------------
  1 |         1 | 2014-01-23 20:45:11 | 758712034.jpg
  2 |         1 | 2014-01-23 20:45:21 | 537444469.jpg
  3 |         3 | 2014-01-23 20:45:39 | 28884579.jpg
  4 |         5 | 2014-01-23 20:45:58 | 1519722792.jpg
  5 |         5 | 2014-01-23 20:46:08 | 298808374.jpg
  6 |         5 | 2014-01-23 20:46:15 | 2059925529.jpg

我希望通过此查询获取所需数据:

SELECT
    activityId, title, sum(seconds) AS totalSeconds, sum(cnt) AS totalPhotos 
FROM
    (
        SELECT
            activities._id AS activityId, activities.title AS title,
            actions._id AS actionId,
            strftime("%s", ifnull(actions.date_stopped, 'now')) -
            strftime("%s", actions.date_started) AS seconds,
            count(photos._id) AS cnt
        FROM
            activities JOIN actions ON activities._id = actions.activity_id
            LEFT OUTER JOIN photos ON photos.action_id = actions._id
        GROUP BY 1,2,3,4
        ORDER BY actionId DESC
    )
GROUP BY 1

但不幸的是,它给出了这个结果:

activityId |   title    | totalSeconds | totalPhotos 
--------------------------------------------------------
         1 | Activity 1 |           23 |           2
         2 | Activity 2 |            2 |           0
         3 | Activity 3 |           12 |           1
         4 | Activity 4 |           29 |           3

我试图得到这个(请参阅操作表中activity_id的顺序):

activityId |   title    | totalSeconds | totalPhotos 
--------------------------------------------------------
         4 | Activity 4 |           29 |           3
         1 | Activity 1 |           23 |           2
         3 | Activity 3 |           12 |           1             
         2 | Activity 2 |            2 |           0

如何更改查询以获得我想要的内容?

2 个答案:

答案 0 :(得分:3)

感谢您设置SQL小提琴。这会让事情变得更轻松。)

您正朝着正确的方向前进 - 可能,您需要添加ORDER BY totalSeconds DESC到查询的末尾。但是,您的查询有几个问题,并且在这些方面可能会更好:

SELECT Activities._id, Activities.title, Actions.totalSeconds, Actions.totalPhotos
FROM Activities
JOIN (SELECT Actions.activity_id, 
             SUM(STRFTIME("%s", COALESCE(Actions.date_stopped, 'now')) 
                            - STRFTIME("%s", Actions.date_started)) AS totalSeconds, 
             SUM(COALESCE(Photos.photoCount, 0)) as totalPhotos,
             MAX(COALESCE(Actions.date_stopped, DATETIME('now'))) as mostRecent
      FROM Actions
      LEFT JOIN (SELECT action_id, COUNT(*) as photoCount
                 FROM Photos
                 GROUP BY action_id) Photos
             ON Photos.action_id = Actions._id
      GROUP BY Actions.activity_id) Actions
   ON Actions.activity_id = Activities._id
ORDER BY Actions.mostRecent DESC

(和working result fiddle

具体做法是:

  1. 您按所有列进行分组(在内部查询中)。在这种情况下,您要么DISTINCT(概念上/逻辑上),要么更好地将查询更改为更小的聚合。请注意,通过像我这里的表一样进行聚合,更有可能使用索引。
  2. 您按编号列进行分组:始终拼写出您想要的列。在极端情况下,如果有人更改了SELECT列表中列的排序,但不是 GROUP BY,那么您的结果可能会以您不期望的方式发生变化......并且会收到错误。
  3. 您的内部查询有一个ORDER BY。这是非常不必要的,并且迫使引擎做额外的工作。
  4. 您的外部GROUP BY仅引用了一列,但有一列未汇总/分组。在这种情况下,它给出了正确的结果,但这是一个危险的特征;如果可能存在多个值,则选择哪个值是不可确定的。默认情况下避免这种情况。
  5. 首选SQL Standard功能(除非出于特定的性能原因) - IFNULL()并非在所有平台上,但COALESCE 。除了日期/时间数学(通常依赖于RDBMS),此查询将适用于所有平台。
  6. (顺便说一句,我对SQLite缺少日期/时间/时间戳类型感到恼火,但这几乎不是你的错......)

答案 1 :(得分:1)

SELECT
    activityId, title, sum(seconds) AS totalSeconds, sum(cnt) AS totalPhotos 
FROM
    (
        SELECT
            activities._id AS activityId, activities.title AS title,
            actions._id AS actionId,
            strftime("%s", ifnull(actions.date_stopped, 'now')) -
            strftime("%s", actions.date_started) AS seconds,
            count(photos._id) AS cnt
        FROM
            activities JOIN actions ON activities._id = actions.activity_id
            LEFT OUTER JOIN photos ON photos.action_id = actions._id
        GROUP BY 1,2,3,4
        ORDER BY actionId DESC
    )
GROUP BY 1
ORDER BY seconds DESC;

返回:

4|Activity 4|29|3
1|Activity 1|23|2
3|Activity 3|12|1
2|Activity 2|2|0

但我可能误解了这个问题,因为我添加的唯一内容是 ORDER BY秒DESC 行。如果您从秒改为 cnt ,那么您将收到相同的结果。