代码之家  ›  专栏  ›  技术社区  ›  Dan

空值上的SQL“连接”

  •  21
  • Dan  · 技术社区  · 15 年前

    出于我无法控制的原因,我需要连接两个表,并且需要空值来匹配。我能想到的最好的选择是吐出一个UUID并将其用作我的比较值,但它看起来很难看

    SELECT * FROM T1 JOIN T2 ON nvl(T1.SOMECOL,'f44087d5935dccbda23f71f3e9beb491') = 
       nvl(T2.SOMECOL,'f44087d5935dccbda23f71f3e9beb491')
    

    我怎样才能做得更好?这是在Oracle上进行的,如果有必要的话,上下文是一个应用程序,其中一批用户上传的数据必须与一批现有数据进行比较,以查看是否有行匹配。回想起来,我们本应该阻止这两个数据集中的任何联接列包含null,但我们没有这样做,现在我们不得不接受它。

    编辑:说清楚,我不是 只有

    12 回复  |  直到 15 年前
        1
  •  47
  •   Eric Petroelje    15 年前

    也许这会管用,但我从未真正尝试过:

    SELECT * 
    FROM T1 JOIN T2 
    ON T1.SOMECOL = T2.SOMECOL OR (T1.SOMECOL IS NULL AND T2.SOMECOL IS NULL)
    
        2
  •  6
  •   Cade Roux    15 年前

    WHERE (a.col = b.col OR COALESCE(a.col, b.col) IS NULL)
    

    由于OR,显然效率不高,但除非有一个保留值,您可以将空值映射到两侧,而不会产生歧义或折叠,这是您所能做的最好的事情(如果有,为什么在您的设计中甚至允许空值…)

        3
  •  3
  •   Ken White    15 年前

    您不能做得更好,但是您拥有的连接不会以任何方式进行实际的“连接”(T1.SOMECOL和T2.SOMECOL之间不会有任何关联,除非它们对该列都有空值)。基本上,这意味着您将无法使用null上的连接来查看行是否匹配。

    NULL永远不等于另一个NULL。一个未知值的东西怎么能与另一个未知值的东西相等?

        4
  •  3
  •   David Aldridge    15 年前

    SELECT *
    FROM T1 JOIN T2 ON sys_op_map_nonnull(T1.SOMECOL) = sys_op_map_nonnull(T2.SOMECOL)
    

    没有证件,所以走这条路要小心。

        5
  •  3
  •   Michael Hays    13 年前

    简单,利用 COALESCE

    SELECT * FROM T1 JOIN T2 ON 
      COALESCE(T1.Field, 'magic string') = 
         COALESCE(T2.Field, 'magic string')
    

    您唯一需要担心的是,‘magic string’不能在两个表中的join字段的合法值中。

        6
  •  3
  •   Tamás Bárász    3 年前

    decode :

        SELECT * FROM T1 JOIN T2 ON DECODE(T1.SOMECOL, T2.SOMECOL, 1, 0) = 1
    

    解码 将空值视为相等,因此这在没有“神奇”数字的情况下有效。这两列必须具有相同的数据类型。

    它不会生成最可读的代码,但可能仍然比 t1.id = t2.id or (t1.id is null and t2.id is null)

        7
  •  1
  •   Josh Smeaton    15 年前

    如果值为null,是否确实希望能够联接表?你不能在连接谓词中排除可能的空值吗?我发现很难发现两个表中的行可以由空值关联。如果table1.colu_a中有100个空值,table2.colu_b中有100个空值,那么仅对于带有空值的行,将返回10000行。听起来不对。

    然而,你确实说过你需要它。我可以建议将空列合并成一个较小的字符串,因为字符比较相对昂贵。更好的是,如果列中的数据是文本,则将空值合并为整数。然后,您可以进行非常快速的“比较”,并且不太可能与现有数据发生冲突。

        8
  •  0
  •   Jim Dagg    15 年前

    把它扔出去——有没有办法把这些空值合并成一个已知值,比如一个空字符串?不太了解您的表是如何布置的,这意味着我无法确定您是否会以这种方式失去意义——例如,空字符串表示“用户拒绝输入电话号码”,空字符串表示“我们忘了询问”,或者诸如此类?

    我敢肯定,这很可能是不可能的,但如果是,您将有已知的值进行比较,并且您可以通过这种方式获得合法的连接。

        9
  •  0
  •   Gambler    15 年前

    这与检查两列中是否存在空值不一样吗?

    SELECT * FROM T1, T2 WHERE T1.SOMECOL IS NULL and T2.SOMECOL IS NULL
    

    SELECT * FROM T1 CROSS JOIN T2 WHERE T1.SOMECOL IS NULL and T2.SOMECOL IS NULL
    
        10
  •  0
  •   Alexis Dufrenoy    8 年前

    为什么不这样做呢:

    在nvl(T1.SOMECOL,'null')上从T1连接T2选择*=

    我不知道你为什么要用UUID。您可以使用列中不存在的任何字符串,例如字符串“null”,以降低内存占用。以及使用 nvl or ... is null 例如,由埃里克·彼得罗耶提出来的。

        11
  •  0
  •   Sarath Subramanian    8 年前

    您可以尝试使用以下查询。

    SELECT *
    FROM TABLEA TA
    JOIN TABLEB TB ON NVL(TA.COL1,0)=NVL(TB.COL2,0);
    
        12
  •  0
  •   Cloud    7 年前

    我相信您仍然可以使用nvl()进行加入:

    SELECT *
    FROM T1
    JOIN T2 ON NVL(T2.COL1,-1)=NVL(T1.COL1,-1);
    

    但您需要在col1列上添加基于函数的索引

    CREATE INDEX IND_1 ON T1 (NVL(COL1,-1));
    CREATE INDEX IND_2 ON T2 (NVL(COL1,-1));
    

    索引应该显著提高NVL(..)上的连接速度。

        13
  •  0
  •   Dharman TechyTech    5 年前

    这不是最好的方法。如果TA.COL1保留值0,而TB.COL2为空,它将加入这些记录,这是不正确的。

    SELECT *
    FROM TABLEA TA
    JOIN TABLEB TB ON NVL(TA.COL1,0)=NVL(TB.COL2,0);
    
        14
  •  -2
  •   ENYCX    12 年前

    SELECT T1.COL1 FROM
    (
       (SELECT (CASE WHEN COL1 IS NULL THEN 'X' ELSE COL1 END) AS COL1 FROM TABLE1) T1
       JOIN
       (SELECT (CASE WHEN COL1 IS NULL THEN 'X' ELSE COL1 END) AS COL1 FROM TABLE2) T2
    )
    ON T1.COL1=T2.COL1