【问题标题】:How to make this CTE query print rows with 0 value?如何使此 CTE 查询打印值为 0 的行?
【发布时间】:2012-10-06 13:39:51
【问题描述】:

我有一个基于 CTE 的查询,我使用连接传递了大约 2600 个 4 元组纬度/经度值 - 这些纬度经度 4 元组已被 ID 标记并保存在名为坐标的第二个表中。这些左上角和右下角的纬度/经度值被传递到 CTE,以显示给定两个时间戳在这些坐标内发出的请求数量(每小时)。-我能够在时间戳内获得每天的总请求数给定,即每个指定日期的用户请求总数。 (例如,用户选择每周三或周三和周四等查看 - 在 2012 年 1 月 1 日至 31 日期间的 11:55 至 22:04 小时之间,对于我通过的每个纬度/经度 4 元组。)但我无法获得行结果中 zcount 为 0 时,我只得到 zcount > 0 的行。我的查询如下:(给Erwin Brandstetter 的注释如果他看到了这个,我检查了关于我之前的问题的聊天室讨论并设置了@ 987654322@ 值 NOT NULL 因为你说坐标可以设计为null,但我仍然有同样的问题。-虽然我可能已经理解了你的意思,因为我遇到了一个糟糕的情况那时候发烧-

WITH v AS (
   SELECT '2012-1-1 11:55:11'::timestamp AS _from -- provide times once
         ,'2012-1-31 22:02:21'::timestamp AS _to
   )
, q AS (
   SELECT c.coordinates_id
        , date_trunc('hour', t.calltime) AS stamp
        , count(*) AS zcount
   FROM   v
   JOIN   mytable t ON  t.calltime BETWEEN v._from AND v._to
                   AND (t.calltime::time >= v._from::time AND
                        t.calltime::time <= v._to::time) AND 
(extract(DOW from t.calltime) = 3)
   JOIN   coordinates c ON (t.lat, t.lon) 
                   BETWEEN (c.bottomrightlat, c.topleftlon)
                       AND (c.topleftlat, c.bottomrightlon)
   GROUP BY c.coordinates_id, date_trunc('hour', t.calltime)
   )
, cal AS (
   SELECT generate_series('2012-1-1 11:00:00'::timestamp
                        , '2012-1-31 23:00:00'::timestamp
                        , '1 hour'::interval) AS stamp)
SELECT q.coordinates_id, cal.stamp::date, sum(q.zcount) AS zcount
FROM   v, cal
LEFT   JOIN q USING (stamp)
WHERE  extract(hour from cal.stamp) BETWEEN extract(hour from v._from)
                                        AND extract(hour from v._to)
AND    extract(DOW from cal.stamp) = 3 
AND    cal.stamp >= v._from
AND    cal.stamp <= v._to
GROUP  BY 1,2
ORDER  BY 1,2;

我在执行这个查询时得到的输出基本上是这样的(通常我有大约 10354 行返回,不包括 zcount 为 0 的行,为了相似,只提供了两个坐标):

coordinates_id  | stamp      | zcount
1               ;"2012-01-04";      2
1               ;"2012-01-11";      3
1               ;"2012-01-18";      2
2               ;"2012-01-04";      2
2               ;"2012-01-11";      3
2               ;"2012-01-18";      2

但是,应该像这样,所有 zcount 为 0 的行也应该与 zcount 非零的行一起打印出来 - 例如1 月 25 日,对于 ID 为 1 和 2 的两个坐标,zcount 为 0 也应打印在示例的这一小部分中-:

coordinates_id  | stamp      | zcount
1               ;"2012-01-04";      2
1               ;"2012-01-11";      3
1               ;"2012-01-18";      2
1               ;"2012-01-25";      0
2               ;"2012-01-04";      2
2               ;"2012-01-11";      3
2               ;"2012-01-18";      2
2               ;"2012-01-25";      0

zcount 值比实际值大得多的更新版本。 -zcount 为 0 的行仍然没有显示-

WITH v AS (
   SELECT '2012-1-1 11:55:11'::timestamp AS _from -- provide times once
         ,'2012-1-31 22:02:21'::timestamp AS _to
   )
, q AS (
   SELECT c.coordinates_id
        , date_trunc('hour', t.calltime) AS stamp
        , count(*) AS zcount
   FROM   v
   JOIN   mytable t ON  t.calltime BETWEEN v._from AND v._to
                   AND (t.calltime::time >= v._from::time AND
                        t.calltime::time <= v._to::time) AND 
(extract(DOW from t.calltime) = 3)
   JOIN   coordinates c ON (t.lat, t.lon) 
                   BETWEEN (c.bottomrightlat, c.topleftlon)
                       AND (c.topleftlat, c.bottomrightlon)
   GROUP BY c.coordinates_id, date_trunc('hour', t.calltime)
   )
, cal AS (
   SELECT generate_series('2012-1-1 11:00:00'::timestamp
                        , '2012-1-31 23:00:00'::timestamp
                        , '1 hour'::interval) AS stamp)
, coordst AS ( 
   SELECT coordinates_id FROM coordinates)
SELECT q.coordinates_id, cal.stamp::date, COALESCE(sum(q.zcount),0) AS zcount
FROM   v, coordst, cal
LEFT   JOIN q USING (stamp)
WHERE  extract(hour from cal.stamp) BETWEEN extract(hour from v._from)
                                        AND extract(hour from v._to)
AND    extract(DOW from cal.stamp) = 3 
AND    cal.stamp >= v._from
AND    cal.stamp <= v._to
GROUP  BY 1,2
ORDER  BY 1,2;

【问题讨论】:

    标签: sql postgresql join aggregate-functions common-table-expression


    【解决方案1】:

    您需要一个不同的坐标 ID 列表来执行正确的 CROSS JOIN。 1. 在 WITH 中添加另一个条目。 2. 将其添加到您的 JOIN(FROM v,cal, coords)中。 3. 你的 zcount 会显示为 NULL,所以 COALESCE 吧。

    WITH v AS (
       SELECT '2012-1-1 11:55:11'::timestamp AS _from -- provide times once
             ,'2012-1-31 22:02:21'::timestamp AS _to
       )
    , q AS (
       SELECT c.coordinates_id
            , date_trunc('hour', t.calltime) AS stamp
            , count(*) AS zcount
       FROM   v
       JOIN   mytable t ON  t.calltime BETWEEN v._from AND v._to
                       AND (t.calltime::time >= v._from::time AND
                            t.calltime::time <= v._to::time) AND 
    (extract(DOW from t.calltime) = 3)
       JOIN   coordinates c ON (t.lat, t.lon) 
                       BETWEEN (c.bottomrightlat, c.topleftlon)
                           AND (c.topleftlat, c.bottomrightlon)
       GROUP BY c.coordinates_id, date_trunc('hour', t.calltime)
       )
    , cal AS (
       SELECT generate_series('2012-1-1 11:00:00'::timestamp
                            , '2012-1-31 23:00:00'::timestamp
                            , '1 hour'::interval) AS stamp)
    , coordst AS ( 
       SELECT DISTINCT coordinates_id FROM coordinates)
    SELECT coordst.coordinates_id, cal.stamp::date, COALESCE(sum(q.zcount),0) AS zcount
    FROM   v CROSS JOIN coordst CROSS JOIN cal
    LEFT   JOIN q USING q.stamp = cal.stamp AND coordst.coordinates_id = q.coordinates_id
    WHERE  extract(hour from cal.stamp) BETWEEN extract(hour from v._from)
                                            AND extract(hour from v._to)
    AND    extract(DOW from cal.stamp) = 3 
    AND    cal.stamp >= v._from
    AND    cal.stamp <= v._to
    GROUP  BY 1,2
    ORDER  BY 1,2;
    

    【讨论】:

    • 我添加了一个coords AS (SELECT coordinates_id FROM coordinates ORDER BY coordinates_id ASC),将JOIN 上方的FROM 更改为FROM v, cal, coords,但是当我运行它时,现在我收到了ERROR: column "stamp" specified in USING clause does not exist in left table 消息。 -另外,我在最后的SELECT 声明中通过COALESCE(sum(q.zcount),0) AS zcount 合并了zcount
    • USING 假设使用先前的结果集。这可能只是一个progresssql错误,翻转你的FROM中的坐标和校准。
    • 我做了,现在可以了,但结果是错误的。我没有得到我在问题中指定的输出,而是得到了比应有的更大的 zcount 值 - 而且我仍然看不到 zcount 为 0 的行并且查询需要更长的时间才能完成 - 我编辑了这个问题您的解决方案建议,您能再看一下吗?
    • 1.将 DISTINCT 放在您的 coords 子查询中。 2. 将您的 FROM 更改为 v CROSS JOIN coordst CROSS JOIN cal LEFT JOIN q ON q.stamp = cal.stamp AND coordst.coordinates_id = q.coordinates_id。 3. 0 zcount 在那里,你只是跳过它,因为你使用 q(它是 null)作为坐标 ID 的来源,改为使用 coordst。
    • 假设坐标表体积小很多,可以先roll up mytable再加入。 v 在您的主连接中不是必需的,但它对运行时间的贡献是微不足道的。你只能提高 q。
    猜你喜欢
    • 2013-02-20
    • 1970-01-01
    • 2014-03-20
    • 2020-12-25
    • 1970-01-01
    • 2011-09-10
    • 1970-01-01
    • 2021-01-20
    • 1970-01-01
    相关资源
    最近更新 更多