【问题标题】:Why does CASE in WHERE clause return different results than when using AND/OR operators instead?为什么 WHERE 子句中的 CASE 与使用 AND/OR 运算符时返回的结果不同?
【发布时间】:2021-09-28 02:01:38
【问题描述】:

我有两个问题。下面是查询 #1,它在 WHERE 子句中使用了 CASE:

SELECT
    customer.id AS "Account",
    customer.country AS "Country",
    (
        CASE
            WHEN customer.country = 'US' THEN customer.state
        END
    ) AS "State",
    TO_CHAR(i.datepaid, 'Month') AS "Month",
    ROUND((SUM(i.subtotal - i.credit)):: NUMERIC, 2) AS "Sales",
    ROUND((SUM(i.tax)):: NUMERIC, 2) AS "Tax",
    ROUND(
        SUM((i.subtotal - i.credit + i.tax) - refunded_amount):: NUMERIC,
        2
    ) AS "Gross"
FROM
    invoices i
    JOIN customer ON i.customer_id = customer.id
    JOIN (
        SELECT
            ii.invoices_id,
            SUM(ii.refunded_amount) AS refunded_amount
        FROM
            invoices i
            JOIN customer ON i.customer_id = customer.id
            JOIN invoice_items ii ON i.id = ii.invoices_id
        WHERE
            i.status = 'Paid'
            AND i.datepaid BETWEEN '2015-01-01' AND '2015-02-01'
            AND customer.billing_day <> 0
            AND customer.register_date < '2015-02-01'
            AND customer.account_exempt = 'f'
            AND customer.country <> ''
        GROUP BY
            ii.invoices_id
    ) ii ON i.id = ii.invoices_id
WHERE
    i.status = 'Paid'
    AND i.datepaid BETWEEN '2015-01-01' AND '2015-02-01'
    AND customer.billing_day <> 0
    AND customer.register_date < '2015-02-01'
    AND customer.account_exempt = 'f'
    AND customer.country <> ''
    AND (
        CASE
            WHEN customer.country = 'US' THEN customer.tax_exempt <> 'f'
        END
    )
GROUP BY
    customer.id,
    TO_CHAR(i.datepaid, 'Month')
ORDER BY
    customer.country,
    (
        CASE
            WHEN customer.country = 'US' THEN customer.state
        END
    );

下面是查询#2,它与查询#1 相同,只是它使用AND customer.country &lt;&gt; 'US' OR customer.tax_exempt &lt;&gt; 'f' 代替了CASE。

SELECT
    customer.id AS "Account",
    customer.country AS "Country",
    (
        CASE
            WHEN customer.country = 'US' THEN customer.state
        END
    ) AS "State",
    TO_CHAR(i.datepaid, 'Month') AS "Month",
    ROUND((SUM(i.subtotal - i.credit)):: NUMERIC, 2) AS "Sales",
    ROUND((SUM(i.tax)):: NUMERIC, 2) AS "Tax",
    ROUND(
        SUM((i.subtotal - i.credit + i.tax) - refunded_amount):: NUMERIC,
        2
    ) AS "Gross"
FROM
    invoices i
    JOIN customer ON i.customer_id = customer.id
    JOIN (
        SELECT
            ii.invoices_id,
            SUM(ii.refunded_amount) AS refunded_amount
        FROM
            invoices i
            JOIN customer ON i.customer_id = customer.id
            JOIN invoice_items ii ON i.id = ii.invoices_id
        WHERE
            i.status = 'Paid'
            AND i.datepaid BETWEEN '2015-01-01' AND '2015-02-01'
            AND customer.billing_day <> 0
            AND customer.register_date < '2015-02-01'
            AND customer.account_exempt = 'f'
            AND customer.country <> ''
        GROUP BY
            ii.invoices_id
    ) ii ON i.id = ii.invoices_id
WHERE
    i.status = 'Paid'
    AND i.datepaid BETWEEN '2015-01-01' AND '2015-02-01'
    AND customer.billing_day <> 0
    AND customer.register_date < '2015-02-01'
    AND customer.account_exempt = 'f'
    AND customer.country <> ''
    AND customer.country <> 'US'
    OR customer.tax_exempt <> 'f'
GROUP BY
    customer.id,
    TO_CHAR(i.datepaid, 'Month')
ORDER BY
    customer.country,
    (
        CASE
            WHEN customer.country = 'US' THEN customer.state
        END
    );

我希望这两个查询返回相同的结果。但是,查询 #1 仅返回美国客户的行,而查询 #2 返回与查询 #1 相同的行,以及来自其他国家/地区的客户的行。这是怎么回事?

【问题讨论】:

    标签: sql postgresql


    【解决方案1】:

    这是你的 #1 和 #2 之间的区别:

    #1:

    (
        CASE
            WHEN customer.country = 'US' THEN customer.tax_exempt <> 'f'
        END
    )
    

    #2:

     customer.country <> 'US' OR customer.tax_exempt <> 'f'
    

    直截了当,我可以看到它们在语义上并不相似,这可以解释两者如何产生不同的输出。你可以用一个更简单的语句来演示它们的不同之处:

    相当于你的#1:

    select 1 where CASE WHEN 'A' = 'US' THEN 'B' <> 'f' END
    

    相当于你的#2:

    select 1 where 'A' <> 'US' or 'B' <> 'f'
    

    当然,您所要做的就是修改AB 以测试其他州,看看您“向南”去了哪里。

    TLDR;您将= 运算符替换为&lt;&gt;,并且错误地将case 谓词解读为“this or that”而不是“this and that”。 p>

    【讨论】:

      猜你喜欢
      • 2021-05-02
      • 2020-03-06
      • 1970-01-01
      • 1970-01-01
      • 2020-01-05
      • 1970-01-01
      • 1970-01-01
      • 2018-07-21
      相关资源
      最近更新 更多