Postgresql

為每個選擇具有不同條件的多行

  • March 23, 2018

我正在嘗試組合一個查詢將返回多個日期範圍內的銷售資訊。我打算做這樣的事情:

SELECT count(*) AS thirty_day_sales FROM "MySchema"."MyTable" MT
WHERE MT.date > now()::date - interval '30 days'
UNION
SELECT count(*) AS sixty_day_sales FROM "MySchema"."MyTable" MT
WHERE MT.date > now()::date - interval '60 days'
UNION
SELECT count(*) AS ninety_day_sales FROM "MySchema"."MyTable" MT
WHERE MT.date > now()::date - interval '90 days'

有沒有另一種更有效的方法來做到這一點UNION?我不想一遍又一遍地重複SELECTWHERE行,因為實際的標準要長得多,而且比這更複雜,多個表連接在一起。

這將返回數據透視。它可能會也可能不會更快。

SELECT
 count(*) AS '90 days',
 count(*) FILTER (WHERE mt.date > now()::date - interval '60 days')
   AS '60 days'
 count(*) FILTER (WHERE mt.date > now()::date - interval '30 days')
   AS '30 days'
FROM "MySchema"."MyTable" AS mt
WHERE mt.date > now()::date - interval '90 days';

如果需要在一列中返回計數結果,這是一種避免聯合的方法。

首先,用代表日期所在儲存桶的值標記每一行(0 到 30 天的儲存桶為 30,30 到 60 天的儲存桶為 60,60 到 90 的儲存桶為 90):

SELECT
 ...
FROM
 "MySchema"."MyTable" AS mt
 CROSS JOIN LATERAL
 (
   SELECT
     CASE
       WHEN mt.date > now()::date - interval '30 days' THEN 30
       WHEN mt.date > now()::date - interval '60 days' THEN 60
       ELSE                                                 90
     END
 ) AS x (PeriodLength)
WHERE
 mt.date > now()::date - interval '90 days'
;

以上將為您提供這樣的數據集:

*some columns*  PeriodLength
------------  ------------
*some values*   90
...           90
...           ...
...           60
...           60
...           ...
...           30
...           30
...           ...

您現在可以分組PeriodLength併計算每個組中的行數:

SELECT
 x.PeriodLength,
 COUNT(*) AS BucketCount
FROM
 "MySchema"."MyTable" AS mt
 CROSS JOIN LATERAL
 (
   SELECT
     CASE
       WHEN mt.date > now()::date - interval '30 days' THEN 30
       WHEN mt.date > now()::date - interval '60 days' THEN 60
       ELSE                                                 90
     END
 ) AS x (PeriodLength)
WHERE
 mt.date > now()::date - interval '90 days'
GROUP BY
 x.PeriodLength
;

這會給你一個這樣的輸出(為了一個例子顯示任意數字):

PeriodLength  BucketCount
------------  -----------
90            45
60            30
30            35

但是,當您希望從現在開始計算期間的計數時,結果將適用於儲存桶。因此,最後一步應該是按 的升序獲取計數的總和PeriodLength,如下所示:

SELECT
 x.PeriodLength,
 SUM(COUNT(*)) OVER (ORDER BY x.PeriodLength ASC) AS BucketCount
FROM
 "MySchema"."MyTable" AS mt
 CROSS JOIN LATERAL
 (
   SELECT
     CASE
       WHEN mt.date > now()::date - interval '30 days' THEN 30
       WHEN mt.date > now()::date - interval '60 days' THEN 60
       ELSE                                                 90
     END
 ) AS x (PeriodLength)
WHERE
 mt.date > now()::date - interval '90 days'
GROUP BY
 x.PeriodLength
;

現在結果將如下所示(基於之前的輸出):

PeriodLength  BucketCount
------------  -----------
90            110
60            65
30            35

如果您在 SELECT 語句本身上省略 ORDER BY,則這些行可能會以不同的順序返回,但數字仍將與各自的周期長度匹配。

如果您更喜歡適當的標籤,例如90 days而不是 just 90,您可以進一步格式化 的輸出PeriodLength,例如:

SELECT
 x.PeriodLength || ' days' AS PeriodLength,
...

我只想建議您僅對輸出執行此操作,而不是在 CASE 表達式計算中執行此操作x.PeriodLength。保持 CASE 結果為數字,以便您可以使用它們進行排序,並在盡可能高的級別格式化它們(最好在客戶端中,但如果您必須在 SQL 中執行,則讓它成為最頂層)。

引用自:https://dba.stackexchange.com/questions/202022