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

Sql 在两个表之间的多个字段上联接。如何确定哪个字段导致联接失败?,sql,join,view,left-join,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.i

我有以下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
选择顶部(100%)百分比
dbo.cct\u prod\u plc\u log\u data.wc,
dbo.cct\u prod\u plc\u log\u data.loc,
dbo.cct\U产品\U plc\U日志\U数据.ord\U编号,
dbo.cct\U产品\U plc\U日志\U数据.ser\U批号,
dbo.cct\u prod\u plc\u log\u data.line,
ISNULL(dbo.imlsmst_to_sfdtlfil.ItemNo“”)作为ItemNo,
ISNULL(dbo.imlsmst_to_sfdtlfil.BldSeqNo“”)作为BldSeqNo,
ISNULL(dbo.imlsmst_to_sfdtlfil.BldOrdNo“”)为BldOrdNo,
ISNULL(dbo.imlsmst_to_sfdtlfil.StringItemNo“”)作为StringItemNo,
ISNULL(dbo.imlsmst_to_sfdtlfil.StringSerLotNo“”)作为StringSerLotNo,
最大值(dbo.cct\u prod\u plc\u log\u data.InsertDateTime)作为最晚日期时间,
最小值(ISNULL(dbo.cct\u prod\u plc\u log\u data.erp\u transaction\u id,0))作为最小值merpid,
ISNULL(dbo.imlsmst_to_sfdtlfil.QtyOnHand,0)作为QtyOnHand
从…起
dbo.cct\u产品\u plc\u日志\u数据
左外连接dbo.imlsmst_到_sfdtlfil
在dbo.cct\u prod\u plc\u log\u data.ser\u lot\u no=dbo.imlsmst\u to\u sfdtlfil.SerLotNo上
和dbo.cct_prod_plc_log_data.ord_no=dbo.imlsmst_to_sfdtlfil.OrderNo
和dbo.cct_prod_plc_log_data.line=dbo.imlsmst_to_sfdtlfil.Bin
哪里
(dbo.cct\u prod\u plc\u log\u data.erp\u transaction\u id<3或dbo.cct\u prod\u plc\u log\u data.erp\u transaction\u id为空)
和(dbo.cct\u prod\u plc\u log\u data.wc“”)
和(dbo.cct\u prod\u plc\u log\u data.loc“”)
和(dbo.cct\u prod\u plc\u log\u data.line“”)
分组
dbo.cct\u prod\u plc\u log\u data.wc,
dbo.cct\u prod\u plc\u log\u data.loc,
dbo.cct\U产品\U plc\U日志\U数据.ord\U编号,
dbo.cct\U产品\U plc\U日志\U数据.ser\U批号,
dbo.cct\u prod\u plc\u log\u data.line,
dbo.imlsmst_至sfdtlfil.ItemNo,
dbo.imlsmst_to_sfdtlfil.BldSeqNo,
dbo.imlsmst_to_sfdtlfil.BldOrdNo,
dbo.imlsmst_to_sfdtlfil.StringItemNo,
dbo.imlsmst_至_sfdtlfil.StringSerLotNo,
dbo.imlsmst_to_sfdtlfil.QtyOnHand
按dbo.cct\U prod\U plc\U log\U data.ord\U no DESC订购
它包含三个字段上两个表之间的左外部联接。根据当前构造,如果右表(dbo.imlsmst_to_sfdtlfil)中的3个连接字段中的任何字段为null或缺失,则左查询中的字段应返回null

如何确定这3个字段中的哪一个是导致联接失败的字段?我想把这些区别开来。谢谢


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

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

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
运行它,获得预期的行数,然后继续取消注释越来越多的表。如果在任何时候您的行数发生意外变化(预期较少时增加,预期较多时减少),请进行调查。 如果行数增加,它可能是笛卡尔积,应该通过添加额外的连接条件来解决,而不是通过在

其他重要提示:

  • 使用COALESCE而不是ISNULL;提高你的数据库交叉技能
  • 别名表和使用别名,而不是到处重复架构和列名
  • 如果您使用的数据库区分空字符串和空字符串,则按合并结果分组,而不是按列分组,否则,当预期为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中与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值,该值存在于另一个表的D、E和F列中。”


这就是您应该用来确保连接工作正常(关系完整性),不允许任何旧的垃圾进入数据库,然后尝试确定哪些行可能已连接到哪些其他行,前提是A列中没有一些拼写错误,这意味着它与D不完全匹配,即使B/C与E/F匹配..

但不知道数据的先决条件,这是不可能的:想象一下左表中的(a,B,C)和右表中的(a,d,C),(E,B,C),哪个字段是失败的?@Turo这就是我试图弄清楚的,不应该有先决条件。(a,b,c)在左表中,以及(a,b,c)在右表中。它们通过LEF连接
A.id
1
2
3

B.id
3
4
5
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