在两个表之间的多个字段上联接。如何确定哪个字段导致连接失败?

时间:2019-01-17 14:42:21

标签: sql join view left-join

我有以下SQL Server查询:

SELECT TOP (100) PERCENT 
    dbo.cct_prod_plc_log_data.wc, 
    dbo.cct_prod_plc_log_data.loc, 
    dbo.cct_prod_plc_log_data.ord_no, 
    dbo.cct_prod_plc_log_data.ser_lot_no, 
    dbo.cct_prod_plc_log_data.line, 
    ISNULL(dbo.imlsmst_to_sfdtlfil.ItemNo, '') AS ItemNo, 
    ISNULL(dbo.imlsmst_to_sfdtlfil.BldSeqNo, '') AS BldSeqNo, 
    ISNULL(dbo.imlsmst_to_sfdtlfil.BldOrdNo, '') AS BldOrdNo, 
    ISNULL(dbo.imlsmst_to_sfdtlfil.StringItemNo, '') AS StringItemNo, 
    ISNULL(dbo.imlsmst_to_sfdtlfil.StringSerLotNo, '') AS StringSerLotNo, 
    MAX(dbo.cct_prod_plc_log_data.InsertDateTime) AS LatestDateTime, 
    MIN(ISNULL(dbo.cct_prod_plc_log_data.erp_transaction_id, 0)) AS MinimumErpID, 
    ISNULL(dbo.imlsmst_to_sfdtlfil.QtyOnHand, 0) AS QtyOnHand
FROM            
    dbo.cct_prod_plc_log_data 
    LEFT OUTER JOIN dbo.imlsmst_to_sfdtlfil 
        ON  dbo.cct_prod_plc_log_data.ser_lot_no = dbo.imlsmst_to_sfdtlfil.SerLotNo 
        AND dbo.cct_prod_plc_log_data.ord_no = dbo.imlsmst_to_sfdtlfil.OrderNo 
        AND dbo.cct_prod_plc_log_data.line = dbo.imlsmst_to_sfdtlfil.Bin
WHERE        
    ( dbo.cct_prod_plc_log_data.erp_transaction_id < 3 OR dbo.cct_prod_plc_log_data.erp_transaction_id IS NULL ) 
    AND (dbo.cct_prod_plc_log_data.wc <> '') 
    AND (dbo.cct_prod_plc_log_data.loc <> '') 
    AND (dbo.cct_prod_plc_log_data.line <> '')
GROUP BY 
    dbo.cct_prod_plc_log_data.wc, 
    dbo.cct_prod_plc_log_data.loc, 
    dbo.cct_prod_plc_log_data.ord_no, 
    dbo.cct_prod_plc_log_data.ser_lot_no, 
    dbo.cct_prod_plc_log_data.line, 
    dbo.imlsmst_to_sfdtlfil.ItemNo, 
    dbo.imlsmst_to_sfdtlfil.BldSeqNo, 
    dbo.imlsmst_to_sfdtlfil.BldOrdNo, 
    dbo.imlsmst_to_sfdtlfil.StringItemNo, 
    dbo.imlsmst_to_sfdtlfil.StringSerLotNo, 
    dbo.imlsmst_to_sfdtlfil.QtyOnHand
ORDER BY dbo.cct_prod_plc_log_data.ord_no DESC

它在3个字段的两个表之间包含一个左外部联接。根据当前结构,如果右表(dbo.imlsmst_to_sfdtlfil)中的3个Joined字段中的任何一个为空或丢失,则左查询中的字段应返回null。

如何确定导致连接失败的3个字段中的哪个?我想将它们彼此区分开。谢谢。

(例如,存在ser_lot_no和ord_no,但bin为null,而存在bin和ord_no,但ser_lot_no为null。)

1 个答案:

答案 0 :(得分:0)

将其更改为内部联接,并注释掉除其中一种情况以外的所有情况,然后一次取消对它们的注释,直到数据再次消失-这是错误的情况。如果即使只有一种情况也没有数据,那就是错误的情况:

SELECT 
    c.wc, 
    c.loc, 
    c.ord_no, 
    c.ser_lot_no, 
    c.line, 
    COALESCE(i.ItemNo, '') AS ItemNo, 
    COALESCE(i.BldSeqNo, '') AS BldSeqNo, 
    COALESCE(i.BldOrdNo, '') AS BldOrdNo, 
    COALESCE(i.StringItemNo, '') AS StringItemNo, 
    COALESCE(i.StringSerLotNo, '') AS StringSerLotNo, 
    MAX(c.InsertDateTime) AS LatestDateTime, 
    MIN(COALESCE(c.erp_transaction_id, 0)) AS MinimumErpID, 
    COALESCE(i.QtyOnHand, 0) AS QtyOnHand
FROM            
    dbo.cct_prod_plc_log_data c 
    INNER JOIN dbo.imlsmst_to_sfdtlfil i
    ON  
      c.ser_lot_no = i.SerLotNo 
      --AND c.ord_no = i.OrderNo 
      --AND c.line = i.Bin
WHERE        
    ( c.erp_transaction_id < 3 OR c.erp_transaction_id IS NULL ) 
    AND (c.wc <> '') 
    AND (c.loc <> '') 
    AND (c.line <> '')
GROUP BY 
    c.wc, 
    c.loc, 
    c.ord_no, 
    c.ser_lot_no, 
    c.line, 
    COALESCE(i.ItemNo, ''), 
    COALESCE(i.BldSeqNo, '')
    COALESCE(i.BldOrdNo, '')
    COALESCE(i.StringItemNo, '')
    COALESCE(i.StringSerLotNo, '')
    COALESCE(i.QtyOnHand, 0)
ORDER BY c.ord_no DESC

使用INNER JOIN比OUTER JOIN更为明显,因为大多数查询工具都提供行数,并且更容易看到行数从99990变为100000,而不是盯着100000的行寻找不应该为空的10行。会

如果您有2个以上的表,请注释掉您的选择块,加上*,除2个表之外的所有表:

SELECT *
 /* columns,list,here,blah,blah */
FROM
  table1
  JOIN table2 ON ...
  --JOIN table3 on ...
  --JOIN table4 on ...

运行它,获取预期的行数,然后继续取消注释越来越多的表。如果在任何时候您的行计数发生意外变化(当您期望更少时,行数就会增加,或者当您期望更高时,行数会减少)。 如果行数增加,则可能是笛卡尔积,应该通过添加额外的连接条件来解决,而不是通过乱打DISTINCT来解决

其他重要提示:

  • 使用COALESCE而不是ISNULL;提高您的数据库交叉技能
  • 别名表并使用别名,而不是在各处重复模式和列名
  • GROUP BY合并结果而不是列,如果您使用的是在空字符串和空字符串之间进行区分的数据库,那么当您期望1时,结果将以两行结尾>

编辑:您说过:

  

感谢您的见解和提示。但是,我的问题更多的是一个问题,即如何合并有关哪个字段导致联接失败的信息,这是永久添加而不是一次性审核。有什么见解吗? –

我说:

您无法做到这一点,数据库无法告诉您“哪个字段”没有解决,因为其中大多数没有解决。要了解我的意思,请运行以下命令:

SELECT
  -- replace .id with the name of the pk column
  CONCAT('Cannot join c[', c.id, '] to i[', i.id, '] because: ',
    CASE 
      WHEN COALESCE(c.ser_lot_no, 'null') != COALESCE(i.SerLotNo, 'null ') THEN 'c.ser_lot_no != i.SerLotNo, '
    END,
    CASE 
      WHEN COALESCE(c.ord_no, 'null') != COALESCE(i.OrderNo, 'null ') THEN 'c.ord_no != i.OrderNo, '
    END,
    CASE 
      WHEN COALESCE(c.line, 'null') != COALESCE(i.Bin, 'null ') THEN 'c.line !=  i.Bin, '
    END
  )
FROM
dbo.cct_prod_plc_log_data c 
CROSS JOIN dbo.imlsmst_to_sfdtlfil i

它要求数据库将每一行连接到其他每一行,然后查看该行上的值并计算出是否可以连接。如果表c有1000行,表i有2000行(和c中的每一行最多与i)中的2行匹配,您将获得200万行的结果集,其中1998000行是“因为...而无法将该行加入该行”

A.id
1
2
3

B.id
3
4
5

A中唯一与B连接的行是“ 3”,即使如此,A中的“ 3”也不会与B中的4或5连接,B中的3也不会与A中的1或2连接。 。对于一组匹配的行,您有8条抱怨说这些行不匹配(总共3x3行,减去一个匹配项)

因此,不能,因为条件X,您不可能使数据库告诉您该表中的哪些行与该表中的哪些行不匹配,因为答案是“几乎所有这些行都不匹配”而“全部”可能是数亿

如果您有一些应该一直有效的联接列,而有些有时却不可行,则变得稍微可行一点。

SELECT CASE WHEN a.something != b.other THEN 'this row would fail because something != other' END 
FROM a JOIN b ON a.id = b.id --and a.something = b.other

再想一想;关系数据库以数据相关的思想为中心,您甚至可以在约束条件下实施它:“除非在行X中存在A,B和C值,否则不允许在其中插入X行表的D,E和F列”

这就是确保连接成功(关系完整性)时应该使用的方法,不允许任何旧的废话进入数据库,然后尝试确定哪些行可能已经连接到了其他行(如果仅存在一些行) A列中的拼写错误,即使B / C与E / F匹配,也不能与D完全匹配。