【问题标题】:My INNER JOIN is filtering out too much我的 INNER JOIN 过滤掉了太多
【发布时间】:2016-07-15 18:30:59
【问题描述】:

我的 INNER JOIN 过滤掉了太多,或者我看不出有什么区别。

我正在尝试在同一台服务器上合并来自 2 个不同数据库的 2 个表。

我的第一步是找到所有匹配的值:

SELECT intl.*
FROM   tblData intl
INNER JOIN [db].dbo.tblData us
ON     intl.DataID = us.DataID
AND    intl.AnotherID = us.AnotherID
AND    intl.DataValue = us.DataValue
AND    intl.TextValue = us.TextValue
AND    intl.DefaultValue = us.DefaultValue
AND    intl.RateValue = us.RateValue
AND    intl.YetAnotherID = us.YetAnotherID

我检查了所有列,它们是相同的类型和相同的 varchar 值。所以这个查询应该带回所有匹配的值。

然而……事实并非如此。它只返回 200 多条记录,如果我在将 200 多条记录插入临时表后运行此查询:

SELECT intl.DataID
FROM   tblData intl
LEFT JOIN TempDataTable TDT
ON     TDT.DataID = intl.DataID
AND    TDT.AnotherID = intl.AnotherID
AND    TDT.DataValue = intl.DataValue
AND    TDT.TextValue = intl.TextValue
AND    TDT.DefaultValue = intl.DefaultValue
AND    TDT.RateValue = intl.RateValue
AND    TDT.YetAnotherID = intl.YetAnotherID
WHERE  TDT.DataID IS NULL

此查询返回 1500 多行。

我对美国数据进行了类似的查询,发现了相同的差异(返回了 1500 多行)。

我查看了 1 条记录,只是想看看我是否能识别出一些奇怪的东西,我发现每个表(INTL 和 US)中的记录完全相同!

 DataID: 1
 AnotherID: 1
 DataValue: NULL
 TextValue: NORMAL
 DefaultValue: 0
 RateValue: NULL
 YetAnotherID: 1

我想我的内部连接在结构上发现了两个表之间的差异,但我看不到它。

有什么想法会导致这种情况吗?

【问题讨论】:

  • 可能是NULL 值。 NULL 不等于任何东西,甚至不等于另一个 NULL。因此,在比较 NULL 列的地方,它将无法通过相等性检查,从而夸大了您的 LEFT JOIN 数字。
  • 真的……我不知道。你有什么建议可以让它工作吗?或者我不应该在可能为 NULL 的列上匹配?
  • 处理这种情况的一种方法是将这些空值转换为实际值:coalesce(TDT.DataValue, '!@#$%') = coalesce(TDT.DataValue, '!@#$%')
  • 成功了!如果您想将其作为解决方案,我会将其标记为答案。
  • @webdad3 谢谢。很高兴你让它工作了。

标签: sql sql-server


【解决方案1】:

这可能是由于表中的 NULL 值未通过 LEFT JOIN 的相等性检查造成的。

NULL 值不等于任何值,甚至不等于相同数据类型的另一个 NULL。

要解决此问题,除了现有的JOIN 条件之外,您还可以在ON 子句中添加检查两侧的NULL 值:

SELECT intl.DataID
FROM   tblData intl
LEFT JOIN TempDataTable TDT
    ON  (
            (TDT.DataID IS NULL AND intl.DataID IS NULL)            
            OR TDT.DataId = intl.DataId
        )
    AND (
            (TDT.AnotherID IS NULL AND intl.AnotherID IS NULL)      
            OR TDT.AnotherId = intl.AnotherId
        )
    AND (
            (TDT.DataValue IS NULL AND intl.DataValue IS NULL)      
            OR TDT.DataValue = intl.DataValue
        )
    AND (
            (TDT.TextValue IS NULL AND intl.TextValue IS NULL)      
            OR TDT.TextValue = intl.TextValue
        )
    AND (
            (TDT.DefaultValue IS NULL AND intl.DefaultValue IS NULL) 
            OR TDT.DefaultValue = intl.DefaultValue
        )
    AND (
            (TDT.RateValue IS NULL AND intl.RateValue IS NULL) 
            OR TDT.RateValue = intl.RateValue
        )
    AND (
            (TDT.YetAnotherID IS NULL AND intl.YetAnotherID IS NULL) 
            OR TDT.YetAnotherID = intl.YetAnotherID
        )
WHERE  TDT.DataID IS NULL

【讨论】:

    【解决方案2】:

    我喜欢这种处理 NULL 相等比较的方法。

    More details about it here

    SELECT intl.*
    FROM   tblData intl
           INNER JOIN [db].dbo.tblData us
             ON EXISTS (SELECT intl.DataID,
                               intl.AnotherID,
                               intl.DataValue,
                               intl.TextValue,
                               intl.DefaultValue,
                               intl.RateValue,
                               intl.YetAnotherID,
                        INTERSECT
                        SELECT us.DataID,
                               us.AnotherID,
                               us.DataValue,
                               us.TextValue,
                               us.DefaultValue,
                               us.RateValue,
                               us.YetAnotherID) 
    

    【讨论】:

      【解决方案3】:
        Values null not take in your query. Use isnull(value, 0) for type value number and isnull(value, '') for type value chararctere
      
        SELECT intl.DataID
        FROM   tblData intl
        inner JOIN TempDataTable TDT
        ON     isnull(TDT.DataID, 0) = isnull(intl.DataID, 0)
        AND    isnull(TDT.AnotherID, 0) = isnull(intl.AnotherID, 0)
        AND    isnull(TDT.DataValue, '') = isnull(intl.DataValue, '')
        AND    isnull(TDT.TextValue, '') = isnull(intl.TextValue, '')
        AND    isnull(TDT.DefaultValue, 0) = isnull(intl.DefaultValue, 0)
        AND    isnull(TDT.RateValue, '') = isnull(intl.RateValue, '')
        AND    isnull(TDT.YetAnotherID, 0) = isnull(intl.YetAnotherID, 0)
      

      【讨论】:

        【解决方案4】:

        你 INNER JOIN 正在做它应该做的事情,那就是从 tblData(美国)和 tblData(国际)返回 only 行,其中 all 你的 tdt = intl condtions申请。如果它是 NULL 值,那么 coalesce() 将是您想要使用的值。考虑一下:

        select 1 where null = null
        select 2 where coalesce(null,2) = coalesce(null,2)
        

        【讨论】:

          【解决方案5】:

          EXISTS 可以很好地用于此查询。

          SELECT 
              intl.DataID
          FROM   
              tblData intl
          WHERE
              NOT EXIST
              (
                  SELECT TOP 1 FROM TempDataTable TDT
                  WHERE
                      TDT.DataID = intl.DataID AND
                      TDT.AnotherID = intl.AnotherID AND
                      TDT.DataValue = intl.DataValue AND
                      TDT.TextValue = intl.TextValue AND
                      TDT.DefaultValue = intl.DefaultValue AND
                      TDT.RateValue = intl.RateValue AND
                      TDT.YetAnotherID = intl.YetAnotherID
               )
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-01-16
            • 2021-08-05
            • 2015-10-31
            • 2023-03-27
            相关资源
            最近更新 更多