SQL从generate_series中选择,按用户_id筛选删除序列?

SQL从generate_series中选择,按用户_id筛选删除序列?,sql,join,aggregate-functions,postgresql-9.2,generate-series,Sql,Join,Aggregate Functions,Postgresql 9.2,Generate Series,在postgresql 9.2.4中,我有一个复杂的SQL查询,使用generate_系列和多个联接。我需要从练习表中计算某一天所有练习的重复次数,并确保这些练习属于当前用户完成的训练。最后,我需要将该表连接到一个序列以显示缺失的日期(使用generate_series) 我的想法是在from子句中选择序列,然后将序列左键连接到一个子查询,该子查询的结果在练习和训练表之间进行内部连接。例如,我有以下查询: SELECT DISTINCT date_trunc('day', series

在postgresql 9.2.4中,我有一个复杂的SQL查询,使用generate_系列和多个联接。我需要从练习表中计算某一天所有练习的重复次数,并确保这些练习属于当前用户完成的训练。最后,我需要将该表连接到一个序列以显示缺失的日期(使用generate_series)

我的想法是在from子句中选择序列,然后将序列左键连接到一个子查询,该子查询的结果在练习和训练表之间进行内部连接。例如,我有以下查询:

SELECT 
    DISTINCT date_trunc('day', series.date)::date as date,
    sum(COALESCE(reps, 0)) OVER WIN,
    array_agg(workout_id) OVER WIN as ids     
FROM (
    select generate_series(-22, 0) + current_date as date
) series 
LEFT JOIN (
    exercises INNER JOIN workouts 
    ON exercises.workout_id = workouts.id
) 
ON series.date = exercises.created_at::date 
WINDOW 
   WIN AS (PARTITION BY date_trunc('day', series.date)::date)
ORDER BY date ASC;
这将提供以下输出:

    date    | sum |                           ids                           
------------+-----+---------------------------------------------------------
 2013-04-27 |   0 | {NULL}
 2013-04-28 | 432 | {49,48,47,46,45,44,43,42,41,38,37,36,36,36,36,35,34,33}
 2013-04-29 |   0 | {NULL}
 2013-04-30 |  20 | {50}
 2013-05-01 |   0 | {NULL}
 2013-05-02 |   0 | {NULL}
 2013-05-03 |   0 | {NULL}
 2013-05-04 |   0 | {NULL}
 2013-05-05 |   0 | {NULL}
 2013-05-06 |   0 | {NULL}
 2013-05-07 |  40 | {51,51}
 2013-05-08 |   0 | {NULL}
 2013-05-09 |   0 | {NULL}
 2013-05-10 |   0 | {NULL}
 2013-05-11 |   0 | {NULL}
 2013-05-12 |   0 | {NULL}
 2013-05-13 |   0 | {NULL}
 2013-05-14 |   0 | {NULL}
 2013-05-15 |   0 | {NULL}
 2013-05-16 |  20 | {52}
 2013-05-17 |   0 | {NULL}
 2013-05-18 |   0 | {NULL}
 2013-05-19 |   0 | {NULL}
(23 rows)
但是,我想按某些条件进行过滤:

WHERE workouts.user_id = 5
比如说

但是,如果我在上面的查询中加入WHERE子句并使用该条件,则输出如下:

    date    | sum |                           ids                           
------------+-----+---------------------------------------------------------
 2013-04-28 | 432 | {49,48,47,46,45,44,43,42,41,38,37,36,36,36,36,35,34,33}
 2013-04-30 |  20 | {50}
 2013-05-07 |  40 | {51,51}
 2013-05-16 |  20 | {52}
(4 rows)
这部连续剧结束了


如何按用户id筛选并保留序列?如果有任何帮助,我们将不胜感激。

您不必从训练表中获取所有数据,也可以将此条件作为参考-

SELECT 
    DISTINCT date_trunc('day', series.date)::date as date,
    sum(COALESCE(reps, 0)) OVER WIN,
    array_agg(workout_id) OVER WIN as ids     
FROM (
    select generate_series(-22, 0) + current_date as date
) series 
LEFT JOIN (
    exercises INNER JOIN (select * from workouts where user_id = 5) workout 
    ON exercises.workout_id = workouts.id
) 
ON series.date = exercises.created_at::date 
WINDOW 
   WIN AS (PARTITION BY date_trunc('day', series.date)::date)
ORDER BY date ASC;

我认为这应该给你你想要的输出。

你可以把这个条件也放在那里,而不是从训练表中获取所有数据-

SELECT 
    DISTINCT date_trunc('day', series.date)::date as date,
    sum(COALESCE(reps, 0)) OVER WIN,
    array_agg(workout_id) OVER WIN as ids     
FROM (
    select generate_series(-22, 0) + current_date as date
) series 
LEFT JOIN (
    exercises INNER JOIN (select * from workouts where user_id = 5) workout 
    ON exercises.workout_id = workouts.id
) 
ON series.date = exercises.created_at::date 
WINDOW 
   WIN AS (PARTITION BY date_trunc('day', series.date)::date)
ORDER BY date ASC;
我认为这应该给你你想要的输出

我有一个复杂的SQL查询

的确,你知道。但是它不必是那样的:

SELECT s.day
      ,COALESCE(sum(w.reps), 0) AS sum_reps  -- assuming reps comes from workouts
      ,array_agg(e.workout_id)  AS ids
FROM   exercises e
JOIN   workouts  w ON w.id = e.workout_id AND w.user_id = 5
RIGHT  JOIN (
   SELECT now()::date + generate_series(-22, 0) AS day
   ) s ON s.day = e.created_at::date 
GROUP  BY 1
ORDER  BY 1;
要点:
  • 是左连接的反向孪晶。由于联接是从左到右应用的,因此不需要以这种方式使用括号

  • 切勿将基本类型和函数名
    date
    用作标识符。我用
    代替

  • 更新:要避免聚合/窗口函数
    sum()
    的结果为空,请使用外部
    COALESCE
    如下所示:
    COALESCE(sum(reps),0))

  • 在这种情况下,您只需使用一个简单的
    groupby
    ,而不是复杂且相对昂贵的组合od
    DISTINCT
    +窗口函数

聚合函数和
COALESCE()
最近在一些问题上,人们对此感到困惑

通常,
sum()
或其他聚合函数忽略
NULL
值。结果与值根本不存在时的结果相同。然而,也有一些特殊情况

应该注意的是,除了
计数
,这些函数返回 未选择任何行时为空值。特别是,没有行的sum 返回null,而不是预期的零,
array\u agg
返回null 而不是没有输入行时的空数组。
合并
函数可用于将null替换为零或空数组 必要时

本演示应通过演示以下案例来澄清:

  • 1个没有行的表
  • 3个表,其中1行保留(
    NULL
    /
    0
    /
    1
  • 3个表,其中2行包含
    NULL
    和(
    NULL
    /
    0
    /
    1
测试设置 查询 结果
tbl | ct|u all | ct|u i | simple|u sum |内部|合并|外部|合并
---------+--------+------+------------+----------------+----------------
t|u empty | 0 | 0 | | 0
t|0 | 1 | 1 | 0 | 0
图1 | 1 | 1 | 1 | 1 | 1
t|n | 1 | 0 | 0 | 0
t|0n | 2 | 1 | 0 | 0
t|1n | 2 | 1 | 1 | 1
t|nn | 2 | 0 | 0 | 0

因此,我最初的建议是草率的。您可能需要将
sum()合并

但如果您这样做,请使用外部
合并
。原始查询中的内部
COALESCE
,并没有覆盖所有角落的情况,很少有用

我有一个复杂的SQL查询

的确,你知道。但是它不必是那样的:

SELECT s.day
      ,COALESCE(sum(w.reps), 0) AS sum_reps  -- assuming reps comes from workouts
      ,array_agg(e.workout_id)  AS ids
FROM   exercises e
JOIN   workouts  w ON w.id = e.workout_id AND w.user_id = 5
RIGHT  JOIN (
   SELECT now()::date + generate_series(-22, 0) AS day
   ) s ON s.day = e.created_at::date 
GROUP  BY 1
ORDER  BY 1;
要点:
  • 是左连接的反向孪晶。由于联接是从左到右应用的,因此不需要以这种方式使用括号

  • 切勿将基本类型和函数名
    date
    用作标识符。我用
    代替

  • 更新:要避免聚合/窗口函数
    sum()
    的结果为空,请使用外部
    COALESCE
    如下所示:
    COALESCE(sum(reps),0))

  • 在这种情况下,您只需使用一个简单的
    groupby
    ,而不是复杂且相对昂贵的组合od
    DISTINCT
    +窗口函数

聚合函数和
COALESCE()
最近在一些问题上,人们对此感到困惑

通常,
sum()
或其他聚合函数忽略
NULL
值。结果与值根本不存在时的结果相同。然而,也有一些特殊情况

应该注意的是,除了
计数
,这些函数返回 未选择任何行时为空值。特别是,没有行的
sum
返回null,而不是预期的零,
array\u agg
返回null 而不是没有输入行时的空数组。
合并
函数可用于将null替换为零或空数组 必要时

本演示应通过演示以下案例来澄清:

  • 1个没有行的表
  • 3张桌子
    -- no rows
    CREATE TABLE t_empty (i int);
    -- INSERT nothing
    
    CREATE TABLE t_0 (i int);
    CREATE TABLE t_1 (i int);
    CREATE TABLE t_n (i int);
    
    -- 1 row
    INSERT INTO t_0 VALUES (0);
    INSERT INTO t_1 VALUES (1);
    INSERT INTO t_n VALUES (NULL);
    
    CREATE TABLE t_0n (i int);
    CREATE TABLE t_1n (i int);
    CREATE TABLE t_nn (i int);
    
    -- 2 rows
    INSERT INTO t_0n VALUES (0),    (NULL);
    INSERT INTO t_1n VALUES (1),    (NULL);
    INSERT INTO t_nn VALUES (NULL), (NULL);
    
    SELECT 't_empty'           AS tbl
          ,count(*)            AS ct_all
          ,count(i)            AS ct_i
          ,sum(i)              AS simple_sum
          ,sum(COALESCE(i, 0)) AS inner_coalesce
          ,COALESCE(sum(i), 0) AS outer_coalesce
    FROM   t_empty
    
    UNION ALL
    SELECT 't_0',  count(*), count(i)
          ,sum(i), sum(COALESCE(i, 0)), COALESCE(sum(i), 0) FROM t_0
    UNION ALL
    SELECT 't_1',  count(*), count(i)
          ,sum(i), sum(COALESCE(i, 0)), COALESCE(sum(i), 0) FROM t_1
    UNION ALL
    SELECT 't_n',  count(*), count(i)
          ,sum(i), sum(COALESCE(i, 0)), COALESCE(sum(i), 0) FROM t_n
    
    UNION ALL
    SELECT 't_0n', count(*), count(i)
          ,sum(i), sum(COALESCE(i, 0)), COALESCE(sum(i), 0) FROM t_0n
    UNION ALL
    SELECT 't_1n', count(*), count(i)
          ,sum(i), sum(COALESCE(i, 0)), COALESCE(sum(i), 0) FROM t_1n
    UNION ALL
    SELECT 't_nn', count(*), count(i)
          ,sum(i), sum(COALESCE(i, 0)), COALESCE(sum(i), 0) FROM t_nn;
    
       tbl   | ct_all | ct_i | simple_sum | inner_coalesce | outer_coalesce
    ---------+--------+------+------------+----------------+----------------
     t_empty |      0 |    0 |     <NULL> |         <NULL> |              0
     t_0     |      1 |    1 |          0 |              0 |              0
     t_1     |      1 |    1 |          1 |              1 |              1
     t_n     |      1 |    0 |     <NULL> |              0 |              0
     t_0n    |      2 |    1 |          0 |              0 |              0
     t_1n    |      2 |    1 |          1 |              1 |              1
     t_nn    |      2 |    0 |     <NULL> |              0 |              0