SQL:field = other_field即使它们相同也返回false(NULL值)

时间:2013-11-27 08:45:36

标签: sql postgresql null comparison

我遇到了一个困难,因为在比较子查询中的两个字段时,虽然字段相同,即它们都具有NULL值,但比较返回FALSE结果

Therfore NULL = NULL返回FALSE

现在我知道NULL应该与IS运算符进行比较,但是当我比较两个字段时,我应该知道它们是否包含空值?如果值为NULL,我需要比较两个相同数据的字段。

考虑这个SQL:

SELECT 
*
FROM
fts.fts_customers_data_50360001
WHERE 
    fts.fts_customers_data_50360001.record_type = 15
    AND
    fts.fts_customers_data_50360001.mid = 103650360001
    AND NOT EXISTS
    (
        SELECT 
            fts.temp_fees_50360001.record_type
        FROM 
            fts.temp_fees_50360001
        WHERE 
            fts.temp_fees_50360001.record_type      = fts.fts_customers_data_50360001.record_type    
            AND 
            fts.temp_fees_50360001.merch_id         = fts.fts_customers_data_50360001.mid
            AND 
            fts.temp_fees_50360001.fee_curr         = fts.fts_customers_data_50360001.currency
            AND 
            fts.temp_fees_50360001.card_scheme      = fts.fts_customers_data_50360001.card_scheme
            AND 
            fts.temp_fees_50360001.tran_type        = fts.fts_customers_data_50360001.fee_type
            AND 
            fts.temp_fees_50360001.area             = fts.fts_customers_data_50360001.region
            AND 
            fts.temp_fees_50360001.srvc_type        = fts.fts_customers_data_50360001.card_type
    );

在上面的查询中,

fts.temp_fees_50360001.card_scheme = fts.fts_customers_data_50360001.card_scheme

内部都有NULL值,但比较返回false ..太糟糕了

任何想法都会得到很多赞赏

4 个答案:

答案 0 :(得分:6)

正如其他人所指出的那样,NULL无法与NULL进行比较。

在Postgres中,您可以使用运算符IS DISTINCT FROM缩短表达式,<>IS NOT DISTINCT FROM的空值安全替代品。在您的情况下,您需要使用IS EQUAL TO来比较相等性(看起来有点错误的方法,但不幸的是,SQL标准中没有定义相应的(fts.temp_fees_50360001.record_type = fts.fts_customers_data_50360001.record_type OR (fts.temp_fees_50360001.record_type IS NULL AND fts.fts_customers_data_50360001.record_type IS NULL) )

From the manual

  

  当任一输入为空时,普通比较运算符产生null(表示“未知”),而不是true或false。例如,7 = NULL产生null,7&lt;&gt;空值。如果此行为不合适,请使用IS [NOT] DISTINCT FROM构造:

所以,而不是

(fts.temp_fees_50360001.record_type IS NOT DISTINCT FROM  fts.fts_customers_data_50360001.record_type)

你可以使用:

{{1}}

自动处理NULL值。如果你想比较相等但是它仍然很短,那么这个条件看起来有点奇怪。

答案 1 :(得分:4)

首先,为表使用别名,您的查询将更具可读性:

select *
from fts.fts_customers_data_50360001 as d
where
    d.record_type = 15 and
    d.mid = 103650360001 and
    not exists 
    (
        select *
        from fts.temp_fees_50360001 as f
        where
            f.record_type = d.record_type and 
            f.merch_id = d.mid and
            f.fee_curr = d.currency and
            f.card_scheme = d.card_scheme and
            f.tran_type = d.fee_type and
            f.area = d.region and
            f.srvc_type = d.card_type
    )

至于你的问题,有几种方法可以做到这一点,例如,你可以使用这样的语法:

...
(
    f.card_scheme is null and d.card_scheme is null or
    f.card_scheme = d.card_scheme
)
...

或者使用coalesce一些无法存储在您的列中的值:

...
coalesce(f.card_scheme, -1) = coalesce(d.card_scheme, -1)
...

最近我也喜欢将existsintersect一起用于此类比较:

...
exists (select f.card_scheme, f.tran_type intersect select d.card_scheme, d.tran_type)
...

只是旁注 - 在编写此类查询时必须小心,并检查查询计划以确保使用索引。

答案 2 :(得分:1)

在SQL中,null永远不会等于null。获得与null进行比较的真实结果的唯一方法是通过特殊测试:

IS NULL
IS NOT NULL

在您的情况下,您必须专门为&#34;两个空值&#34;案件被认为是平等的:

AND (fts.temp_fees_50360001.card_scheme = fts.fts_customers_data_50360001.card_scheme
  OR (fts.temp_fees_50360001.card_scheme IS NULL
    AND fts.fts_customers_data_50360001.card_scheme IS NULL)
)

没有处理它(虽然有一些变化)。

答案 3 :(得分:0)

以下内部SELECT工作(但我不保证性能):

    SELECT 
        fts.temp_fees_50360001.record_type
    FROM 
        fts.temp_fees_50360001
    WHERE 
        (fts.temp_fees_50360001.record_type      = fts.fts_customers_data_50360001.record_type
        OR (fts.temp_fees_50360001.record_type IS NULL AND fts.fts_customers_data_50360001.record_type IS NULL))
        AND 
        (fts.temp_fees_50360001.merch_id         = fts.fts_customers_data_50360001.mid
        OR (fts.temp_fees_50360001.merch_id IS NULL AND fts.fts_customers_data_50360001.mid IS NULL))
        AND 
        (fts.temp_fees_50360001.fee_curr         = fts.fts_customers_data_50360001.currency
        OR (fts.temp_fees_50360001.fee_curr IS NULL AND fts.fts_customers_data_50360001.currency IS NULL))
        AND 
        (fts.temp_fees_50360001.card_scheme      = fts.fts_customers_data_50360001.card_scheme
        OR (fts.temp_fees_50360001.card_scheme IS NULL AND fts.fts_customers_data_50360001.card_scheme IS NULL))
        AND 
        (fts.temp_fees_50360001.tran_type        = fts.fts_customers_data_50360001.fee_type
        OR (fts.temp_fees_50360001.tran_type IS NULL AND fts.fts_customers_data_50360001.fee_type IS NULL))
        AND 
        (fts.temp_fees_50360001.area             = fts.fts_customers_data_50360001.region
        OR (fts.temp_fees_50360001.area IS NULL AND fts.fts_customers_data_50360001.region IS NULL))
        AND 
        (fts.temp_fees_50360001.srvc_type        = fts.fts_customers_data_50360001.card_type 
        OR (fts.temp_fees_50360001.srvc_type IS NULL AND fts.fts_customers_data_50360001.card_type))