具有不同WHERE子句的子查询的重用

时间:2019-10-31 13:36:26

标签: sql postgresql

我有一个查询,看起来像这样

WITH sub_base AS (
SELECT type,
CASE WHEN a < 10 THEN 'GOOD'
     WHEN a BETWEEN 10 AND 100 THEN 'OK'
     ELSE 'BAD'
END AS a_q,
...
CASE WHEN z < 5 THEN 'GOOD'
     WHEN z BETWEEN 5 AND 50 THEN 'OK'
     ELSE 'BAD'
END AS z_q
FROM tbl
),

SELECT sub_one AS (
SELECT 'one' AS type,
COUNT(CASE WHEN a_q = 'GOOD' THEN 1) a_good,
COUNT(CASE WHEN a_q = 'OK' THEN 1) a_ok,
COUNT(CASE WHEN a_q = 'BAD' THEN 1) a_bad,
...
COUNT(CASE WHEN z_q = 'GOOD' THEN 1) z_good,
COUNT(CASE WHEN z_q = 'OK' THEN 1) z_ok,
COUNT(CASE WHEN z_q = 'BAD' THEN 1) z_bad,
FROM sub_base
WHERE type = 'one'
),

SELECT sub_two AS (
SELECT 'two' AS type,
COUNT(CASE WHEN a_q = 'GOOD' THEN 1) a_good,
COUNT(CASE WHEN a_q = 'OK' THEN 1) a_ok,
COUNT(CASE WHEN a_q = 'BAD' THEN 1) a_bad,
...
COUNT(CASE WHEN z_q = 'GOOD' THEN 1) z_good,
COUNT(CASE WHEN z_q = 'OK' THEN 1) z_ok,
COUNT(CASE WHEN z_q = 'BAD' THEN 1) z_bad,
FROM sub_base
WHERE type = 'two'
),

SELECT sub_all AS (
SELECT 'all' AS type,
COUNT(CASE WHEN a_q = 'GOOD' THEN 1) a_good,
COUNT(CASE WHEN a_q = 'OK' THEN 1) a_ok,
COUNT(CASE WHEN a_q = 'BAD' THEN 1) a_bad,
...
COUNT(CASE WHEN z_q = 'GOOD' THEN 1) z_good,
COUNT(CASE WHEN z_q = 'OK' THEN 1) z_ok,
COUNT(CASE WHEN z_q = 'BAD' THEN 1) z_bad,
FROM sub_base
)

SELECT * FROM sub_one
UNION
SELECT * FROM sub_two
UNION
SELECT * FROM sub_all

我想重用sub_one, sub_two, sub_all中的子查询,因为除了SELECT和WHERE条件中的type外,它们几乎相同。

如何避免将这些子查询复制粘贴?

2 个答案:

答案 0 :(得分:2)

为什么不使用第二个CTE?

WITH sub_base AS (
    ...
),
second_cte AS (
    SELECT  
        type,  
        COUNT(CASE WHEN a_q = 'GOOD' THEN 1) a_good,
        COUNT(CASE WHEN a_q = 'OK' THEN 1) a_ok,
        COUNT(CASE WHEN a_q = 'BAD' THEN 1) a_bad,
        ...
        COUNT(CASE WHEN z_q = 'GOOD' THEN 1) z_good,
        COUNT(CASE WHEN z_q = 'OK' THEN 1) z_ok,
        COUNT(CASE WHEN z_q = 'BAD' THEN 1) z_bad
    FROM sub_base
)
SELECT
    *
FROM second_cte
WHERE type = 'one'

UNION

SELECT
    *
FROM second_cte
WHERE type = 'two'

UNION

SELECT
    *
FROM second_cte
WHERE type = 'all'

在这种情况下,您甚至不需要两个CTE,因为您不会重复使用第一个。因此,您可以将其放入子查询中:

WITH cte AS (
    SELECT    
        type,
        COUNT(CASE WHEN a_q = 'GOOD' THEN 1) a_good,
        COUNT(CASE WHEN a_q = 'OK' THEN 1) a_ok,
        COUNT(CASE WHEN a_q = 'BAD' THEN 1) a_bad,
        ...
        COUNT(CASE WHEN z_q = 'GOOD' THEN 1) z_good,
        COUNT(CASE WHEN z_q = 'OK' THEN 1) z_ok,
        COUNT(CASE WHEN z_q = 'BAD' THEN 1) z_bad
    FROM (
        SELECT
            <your sub_base query here>
    ) s
)
SELECT
    *
FROM second_cte
WHERE type = 'one'

UNION

SELECT
    *
FROM second_cte
WHERE type = 'two'

UNION

SELECT
    *
FROM second_cte
WHERE type = 'all'

答案 1 :(得分:2)

使用GROUP BY

SELECT type,
       COUNT(CASE WHEN a_q = 'GOOD' THEN 1) as a_good,
       COUNT(CASE WHEN a_q = 'OK' THEN 1) as a_ok,
       COUNT(CASE WHEN a_q = 'BAD' THEN 1) as a_bad,
       ...
       COUNT(CASE WHEN z_q = 'GOOD' THEN 1) as z_good,
       COUNT(CASE WHEN z_q = 'OK' THEN 1) as z_ok,
       COUNT(CASE WHEN z_q = 'BAD' THEN 1) as z_bad,
FROM sub_base
WHERE type IN ('one', 'two', 'three')  -- may not be needed
GROUP BY type;

在Postgres中,我将进一步简化它。您可以使用FILTER或对二进制值求和:

SELECT type,
       SUM( (a_q = 'GOOD')::int ) as a_good,
       SUM( (a_q = 'OK')::int ) as a_ok,
       SUM( (a_q = 'BAD')::int ) as a_bad,
       ...
       SUM( (z_q = 'GOOD')::int ) as z_good,
       SUM( (z_q = 'OK')::int ) as z_ok,
       SUM( (z_q = 'BAD')::int ) as z_bad,
FROM sub_base
WHERE type IN ('one', 'two', 'three')  -- may not be needed
GROUP BY type;

我也省去了CTE,只是将逻辑直接放在外部查询中,但这更多的是样式问题。

编辑:

您可以使用其他CTE:

WITH . . . ,
     t as (
      SELECT type,
             SUM( (a_q = 'GOOD')::int ) as a_good,
             SUM( (a_q = 'OK')::int ) as a_ok,
             SUM( (a_q = 'BAD')::int ) as a_bad,
             ...
             SUM( (z_q = 'GOOD')::int ) as z_good,
             SUM( (z_q = 'OK')::int ) as z_ok,
             SUM( (z_q = 'BAD')::int ) as z_bad,
      FROM sub_base
      WHERE type IN ('one', 'two', 'three')  -- may not be needed
      GROUP BY type
     )
SELECT t.*
FROM t
UNION ALL
SELECT 'Total', SUM(a_good), SUM(a_ok), SUM(a_bad), . . .
FROM t;
相关问题