使用GROUP BY日期时缺少日期

时间:2018-02-07 17:15:29

标签: sql postgresql

在我的PostgreSQL数据库中,我有survey_results表:

CREATE TABLE survey_results (
    id integer NOT NULL,
    scores jsonb DEFAULT '{}'::jsonb,
    created_at timestamp without time zone,
    updated_at timestamp without time zone  
);

我在此表中有以下记录:

INSERT INTO survey_results (id, scores, created_at, updated_at)
    VALUES (1, '{"medic": { "social": { "total": "high" } } }', '2018-01-11', '2018-01-10');

INSERT INTO survey_results (id, scores, created_at, updated_at)
    VALUES (2, '{"medic": { "social": { "total": "high" } } }', '2018-01-12', '2018-01-12');

以及以下查询:

SELECT
  distinct(date(survey_results.created_at)),

  ROUND(
    COUNT(*) FILTER (WHERE (
      scores#>>'{medic,social,total}' in('high'))) OVER(order by date(survey_results.created_at)
    ) * 1.0 /

    (
      GREATEST(
        COUNT(*) FILTER (WHERE (scores#>>'{medic,social,total}' in('high','medium','low')
    )
  ) OVER(order by date(survey_results.created_at)), 1.0))* 100, 2
)
 AS positive

  FROM survey_results
  WHERE  
    survey_results.created_at::date >= '2018-01-10'
    AND survey_results.created_at::date <= '2018-01-12'
  GROUP BY date, scores
  ORDER BY date ASC;

返回:

date        positive
2018-01-11  100
2018-01-12  100

问题是查询省略2018-01-10因为没有记录,这是因为group by。有没有办法更新此查询以返回没有记录的日期:

date        positive
2018-01-10  0
2018-01-11  100
2018-01-12  100

这是sqlfiddle:

http://sqlfiddle.com/#!17/5e007/1

2 个答案:

答案 0 :(得分:1)

使用功能This answer

SELECT date::date, coalesce(positive, 0.00) as positive
FROM generate_series('2018-01-10'::date, '2018-01-12', '1d') s(date)
LEFT JOIN (
    -- your query
    SELECT
      distinct(date(survey_results.created_at)),
      ROUND(
        COUNT(*) FILTER (WHERE (
          scores#>>'{medic,social,total}' in('high'))) OVER(order by date(survey_results.created_at)
        ) * 1.0 /
        (
          GREATEST(
            COUNT(*) FILTER (WHERE (scores#>>'{medic,social,total}' in('high','medium','low')
        )
      ) OVER(order by date(survey_results.created_at)), 1.0))* 100, 2
    )
     AS positive
      FROM survey_results
      WHERE  
        survey_results.created_at::date >= '2018-01-10'
        AND survey_results.created_at::date <= '2018-01-12'
      GROUP BY date, scores
    -- your query
    ) q USING(date)
ORDER BY date ASC;

    date    | positive 
------------+----------
 2018-01-10 |     0.00
 2018-01-11 |   100.00
 2018-01-12 |   100.00
(3 rows)    

答案 1 :(得分:0)

日期不是&#34;缺少&#34;。可以这样想:您的查询要求服务器返回所有数据,其中created_at值为&gt; =&#39; 2018-01-10&#39;和created_at值是&lt; =&#39; 2018-01-12&#39;。查询正确执行。

如果要将数据集与任意日期集进行对比,则必须生成该集。一些数据仓库从业者使用日期维度,其中包含所有相关日期,以及财政年度,IsHoliday等便利属性。

或者,您可以自己生成这样的序列,并使用它们(作为视图,派生表等),并将日期序列LEFT JOIN到您的实际查询。然后,您可以将日期谓词应用于序列,并且外部查询中缺少数据的任何日期都将填充为NULL。您也可以选择将这些列转换为具体的值,例如&#39;&#39;&#39;或0(零)。

以下是生成任意日期序列的示例查询。您可以想象如何修改它以适合您的环境。这适用于MSSQL 2017,但任何平台的想法都是一样的。

DECLARE @dtStartDate datetime
DECLARE @dtEndDate datetime
SET @dtStartDate = '2018-01-10'
SET @dtEndDate = '2018-01-12'

SELECT
  T.DateVal,
  CASE DATEPART(weekday, T.DateVal)
    WHEN 1 THEN 'Sunday'
    WHEN 2 THEN 'Monday'
    WHEN 3 THEN 'Tuesday'
    WHEN 4 THEN 'Wednesday'
    WHEN 5 THEN 'Thursday'
    WHEN 6 THEN 'Friday'
    WHEN 7 THEN 'Saturday'
  END AS WeekDay,
  DATEPART(day, T.DateVal) AS [Date],
  DATEPART(month, T.DateVal) AS [Month],
  DATEPART(year, T.DateVal) AS [Year]
FROM
(
  SELECT
      DATEADD(day, SEQ.SeqValue, @dtStartDate) DateVal
  FROM
  (
  SELECT
      (HUNDREDS.SeqValue + TENS.SeqValue + ONES.SeqValue) SeqValue
  FROM
      (
      SELECT 0  SeqValue
      UNION ALL
      SELECT 1 SeqValue
      UNION ALL
      SELECT 2 SeqValue
      UNION ALL
      SELECT 3 SeqValue
      UNION ALL
      SELECT 4 SeqValue
      UNION ALL
      SELECT 5 SeqValue
      UNION ALL
      SELECT 6 SeqValue
      UNION ALL
      SELECT 7 SeqValue
      UNION ALL
      SELECT 8 SeqValue
      UNION ALL
      SELECT 9 SeqValue
      ) ONES
  CROSS JOIN
      (
      SELECT 0 SeqValue
      UNION ALL
      SELECT 10 SeqValue
      UNION ALL
      SELECT 20 SeqValue
      UNION ALL
      SELECT 30 SeqValue
      UNION ALL
      SELECT 40 SeqValue
      UNION ALL
      SELECT 50 SeqValue
      UNION ALL
      SELECT 60 SeqValue
      UNION ALL
      SELECT 70 SeqValue
      UNION ALL
      SELECT 80 SeqValue
      UNION ALL
      SELECT 90 SeqValue
      ) TENS
  CROSS JOIN
      (
      SELECT 0 SeqValue
      UNION ALL
      SELECT 100 SeqValue
      UNION ALL
      SELECT 200 SeqValue
      UNION ALL
      SELECT 300 SeqValue
      UNION ALL
      SELECT 400 SeqValue
      UNION ALL
      SELECT 500 SeqValue
      UNION ALL
      SELECT 600 SeqValue
      UNION ALL
      SELECT 700 SeqValue
      UNION ALL
      SELECT 800 SeqValue
      UNION ALL
      SELECT 900 SeqValue
      ) HUNDREDS
  ) SEQ
) T
WHERE
  T.DateVal <= @dtEndDate
ORDER BY
  T.DateVal ASC