SQL-Server左外连接,大小写时首先没有停止

时间:2018-01-19 23:40:15

标签: sql sql-server case

我希望得到一行与左外连接的'on'子句中的case表达式中的第一个匹配“when”相匹配,但是我得到 EVERY 中的行匹配。
互联网告诉我这是不可能的,一个案例将始终在第一次匹配时停止。

SELECT MILL_ORDER_NUMBER
       ,SHORTY_NAME
       ,PRIMARY_DEST
       ,ALT_DESTINATION
       ,CB.CDE_CNSUM_LOC as CB4V_CNSUM_LOC
       ,CB.CDE_DEST
       ,CB.NAM_CUST_SHTY
FROM HLFOR01A OA 

left outer join (select CDE_CNSUM_LOC, CDE_DEST, NAM_CUST_SHTY from CSAR_CB4V0023) CB

on case
when ((OA.SHORTY_NAME = CB.NAM_CUST_SHTY) and (substring(OA.PRIMARY_DEST,1,1) < 'A') and (OA.PRIMARY_DEST = CB.CDE_DEST)) then 1

when ((OA.SHORTY_NAME = CB.NAM_CUST_SHTY) and (CB.CDE_DEST = (select min(dd.CDE_DEST) from CSAR_CB4V0023 dd where dd.NAM_CUST_SHTY = OA.SHORTY_NAME))) then 1
else 0  end = 1
where MILL_ORDER_NUMBER = '84220631'

如果两个条款都存在,我得到

MILL_ORDER_NUMBER SHORTY_NAME PRIMARY_DEST ALT_DESTINATION CB4V_CNSUM_LOC CDE_DEST NAM_CUST_SHTY

84220631      CMPNY1    5U              1641        00      CMPNY1 <-- matches 2nd when clause

84220631      CMPNY1    5U              1627        5U      CMPNY1 <-- matches 1st when clause

如果我注释掉第1段时间,我只会得到第一行。 如果我注释掉第二个条款,我只会得到第二行 我不明白为什么它不会停留在匹配的第一个if子句?

3 个答案:

答案 0 :(得分:1)

连接时,连接一侧的所有行都将根据连接另一侧的所有行进行评估。

您的案例陈述在第一场比赛时停止,对于任何一方的每对行。只是因为左侧的其中一行已经匹配了右侧的一行,并没有阻止它匹配右侧的另一行,使用case语句中的任何一种情况,因为每对都是独立于任何现有匹配进行评估的。您的案例陈述实际上等同于:但效率低于:

on OA.SHORTY_NAME = CB.NAM_CUST_SHTY
    and ((substring(OA.PRIMARY_DEST,1,1) < 'A' and OA.PRIMARY_DEST = CB.CDE_DEST) 
    or (CB.CDE_DEST = (select min(dd.CDE_DEST) from CSAR_CB4V0023 dd where dd.NAM_CUST_SHTY = OA.SHORTY_NAME))

将其视为嵌套在另一个for循环中的for循环,在每对可能的行上执行case语句。

DECLARE @Table (MILL_ORDER_NUMBER {type}, SHORTY_NAME {type}, PRIMARY_DEST {type}, ALT_DESTINATION {type}, CB4V_CNSUM_LOC {type}, CDE_DEST {type}, NAM_CUST_SHTY {type})

INSERT INTO @Table (MILL_ORDER_NUMBER ,SHORTY_NAME ,PRIMARY_DEST ,ALT_DESTINATION ,CB4V_CNSUM_LOC, CDE_DEST, NAM_CUST_SHTY)
SELECT MILL_ORDER_NUMBER, SHORTY_NAME, PRIMARY_DEST, ALT_DESTINATION, CB.CDE_CNSUM_LOC, CB.CDE_DEST, CB.NAM_CUST_SHTY
FROM HLFOR01A OA 
JOIN (select CDE_CNSUM_LOC, CDE_DEST, NAM_CUST_SHTY from CSAR_CB4V0023) CB
on ((OA.SHORTY_NAME = CB.NAM_CUST_SHTY) and (CB.CDE_DEST = (select min(dd.CDE_DEST) from CSAR_CB4V0023 dd where dd.NAM_CUST_SHTY = OA.SHORTY_NAME)))
where MILL_ORDER_NUMBER = '84220631'

INSERT INTO @Table (MILL_ORDER_NUMBER ,SHORTY_NAME ,PRIMARY_DEST ,ALT_DESTINATION ,CB4V_CNSUM_LOC, CDE_DEST, NAM_CUST_SHTY)
SELECT MILL_ORDER_NUMBER, SHORTY_NAME, PRIMARY_DEST, ALT_DESTINATION, CB.CDE_CNSUM_LOC, CB.CDE_DEST, CB.NAM_CUST_SHTY
FROM HLFOR01A OA 
JOIN (select CDE_CNSUM_LOC, CDE_DEST, NAM_CUST_SHTY from CSAR_CB4V0023) CB
on ((OA.SHORTY_NAME = CB.NAM_CUST_SHTY) and (substring(OA.PRIMARY_DEST,1,1) < 'A') and (OA.PRIMARY_DEST = CB.CDE_DEST))
where MILL_ORDER_NUMBER = '84220631' AND NOT EXISTS (SELECT top 1 1 FROM @table t where t.MILL_ORDER_NUMBER=OA.MILL_ORDER_NUMBER)

INSERT INTO @Table (MILL_ORDER_NUMBER ,SHORTY_NAME ,PRIMARY_DEST ,ALT_DESTINATION)
SELECT MILL_ORDER_NUMBER, SHORTY_NAME, PRIMARY_DEST, ALT_DESTINATION
FROM HLFOR01A OA 
where MILL_ORDER_NUMBER = '84220631' AND NOT EXISTS (SELECT top 1 1 FROM @table t where t.MILL_ORDER_NUMBER=OA.MILL_ORDER_NUMBER)

SELECT MILL_ORDER_NUMBER ,SHORTY_NAME ,PRIMARY_DEST ,ALT_DESTINATION ,CB4V_CNSUM_LOC, CDE_DEST, NAM_CUST_SHTY FROM @Table

答案 1 :(得分:1)

这种情况会产生一组记录,这些记录与导致1的时间相匹配。要获得第一个匹配的记录,你可以做一个前n:

SELECT TOP 1 MILL_ORDER_NUMBER ...

一个分组可以让你在CB4V_CNSUM_LOC,CB.CDE_DEST,CB.NAM_CUST_SHTY上以最小或最大为一个结果行,但你可能会混合多个记录中的这些,所以这可能不是你想要的。

对第一个选项进行调整是为了“加权”你案件的每一个,以便你得到一个与第一个匹配的行(如果它存在的话):

SELECT TOP 1 MILL_ORDER_NUMBER ...
...
ORDER BY
case
when ((OA.SHORTY_NAME = CB.NAM_CUST_SHTY) and (substring(OA.PRIMARY_DEST,1,1) < 'A') and (OA.PRIMARY_DEST = CB.CDE_DEST)) 
then 1
when ((OA.SHORTY_NAME = CB.NAM_CUST_SHTY) and (CB.CDE_DEST = (select min(dd.CDE_DEST) from CSAR_CB4V0023 dd where dd.NAM_CUST_SHTY = OA.SHORTY_NAME))) 
then 2
else 99  end

答案 2 :(得分:0)

这对你有用吗?

SELECT TOP 1 MILL_ORDER_NUMBER
       ,SHORTY_NAME
       ,PRIMARY_DEST
       ,ALT_DESTINATION
       ,CB.CDE_CNSUM_LOC as CB4V_CNSUM_LOC
       ,CB.CDE_DEST
       ,CB.NAM_CUST_SHTY
FROM HLFOR01A OA 
left outer join (select CDE_CNSUM_LOC, CDE_DEST, NAM_CUST_SHTY from CSAR_CB4V0023) CB on 
    ((OA.SHORTY_NAME = CB.NAM_CUST_SHTY) and (substring(OA.PRIMARY_DEST,1,1) < 'A') and (OA.PRIMARY_DEST = CB.CDE_DEST)) 
    or
    ((OA.SHORTY_NAME = CB.NAM_CUST_SHTY) and (CB.CDE_DEST = (select min(dd.CDE_DEST) from CSAR_CB4V0023 dd where dd.NAM_CUST_SHTY = OA.SHORTY_NAME)))
where MILL_ORDER_NUMBER = '84220631'

我意识到有些parens是多余的,但为了保持一致而保留它们。我不是100%确定这是你所追求的,但是......

TOP 1将为您提供第一个结果(虽然没有ORDER BY子句,但是任意结果将是第一个)。

新的ON条款有点清洁,我认为代表了你的目标;如果不是,它至少应该更容易可视化和操纵。

我希望这会有所帮助。