Sql 查找使用或时我加入的列

Sql 查找使用或时我加入的列,sql,sql-server,Sql,Sql Server,在我的数据库中,有一行包含多个代码,例如: ID,Code1,Code2,Code3,Code4 这些代码引用另一个表中的程序名称,例如: Code1 = 'Procedure one' Code2 = 'Procedure two' ect. 我需要将这一行转换为每个代码显示一行,以及相应的过程名称,例如: ID,ProcedureName 1,'Procedure One' 1,'Procedure two' 2,'Procedure one' 为了实现这一点,我在OR语句中使用了一个

在我的数据库中,有一行包含多个代码,例如:

ID,Code1,Code2,Code3,Code4
这些代码引用另一个表中的程序名称,例如:

Code1 = 'Procedure one'
Code2 = 'Procedure two'
ect.
我需要将这一行转换为每个代码显示一行,以及相应的过程名称,例如:

ID,ProcedureName
1,'Procedure One'
1,'Procedure two'
2,'Procedure one'
为了实现这一点,我在OR语句中使用了一个外部连接,这不是最有效的性能,但是由于ProcedureName表没有那么大,所以我现在不太担心开销,更多的是让它工作

FROM Events EV
LEFT JOIN ProcedureName PN
        ON (PN.CODE = Ev.Code1)
           OR (PN.CODE = Ev.Code2)
           OR (PN.CODE = Ev.Code3)
           OR (PN.CODE = Ev.Code4)
这是可行的,但是现在我有一个问题,那就是我能分辨出什么程序是主要的,什么是次要的。通常,主/次代码完全由第一个代码中的任何代码表示。即主要是代码1中的内容,次要是代码2中的内容,等等

但是,由于我现在使用OR加入,我现在不知道该过程加入了什么代码

我想做一个案例陈述

CASE
           WHEN PN.CODE = Ev.Code1 THEN
               '(Primary) ' + ISNULL(PN.NAME, '')
           WHEN PN.CODE = Ev.Code2 THEN
               '(Secondary) ' + ISNULL(PN.NAME, '')
           WHEN PN.CODE = Ev.Code3 THEN
               '(Tertiary) ' + ISNULL(PN.NAME, '')
           WHEN PN.CODE = Ev.Code4 THEN
               '(Quaternary) ' + ISNULL(PN.NAME, '')
       END AS ProcedureName,
然而,这有一个主要问题,即代码1和代码2很可能都是相同的代码。这意味着它们都将显示为主


有谁能给我一些提示,告诉我如何找出OR join实际上加入了什么?它加入了代码1,代码2吗?是否有更好的方法来编写联接,使我可以在每个ID上有多行代码(取决于代码的数量),同时仍然可以找出代码1或代码2的位置?

我认为
应用
可以满足您的要求:

select e.id, v.which, v.code
from Events e cross apply
     (values ('procedure1', code1), ('procedure2', code2), ('procedure3', code3), ('procedure4', code4)
     ) v(which, code)
如果要过滤掉
NULL
,请添加:

where v.code is null

我会稍微改写一下这个问题。实际上,它不是“在列上联接”,而是在布尔表达式的结果上联接

所以,你想要的是找出布尔表达式的哪些部分是真的或不是

SELECT
  *,
  CASE WHEN PN.CODE = Ev.Code1 THEN 1 ELSE 0 END   AS MatchingCode1,
  CASE WHEN PN.CODE = Ev.Code2 THEN 1 ELSE 0 END   AS MatchingCode2,
  CASE WHEN PN.CODE = Ev.Code3 THEN 1 ELSE 0 END   AS MatchingCode3,
  CASE WHEN PN.CODE = Ev.Code4 THEN 1 ELSE 0 END   AS MatchingCode4
FROM
  Events EV
LEFT JOIN
  ProcedureName PN
    ON  PN.CODE IN (Ev.Code1, Ev.Code2, Ev.Code3, Ev.Code4)
如果希望将其作为单个列,可以使用二进制算术

SELECT
  *,
  CASE WHEN PN.CODE = Ev.Code1 THEN 1 ELSE 0 END +
  CASE WHEN PN.CODE = Ev.Code2 THEN 2 ELSE 0 END +
  CASE WHEN PN.CODE = Ev.Code3 THEN 4 ELSE 0 END +
  CASE WHEN PN.CODE = Ev.Code4 THEN 8 ELSE 0 END    AS MatchingCodes
FROM
  Events EV
LEFT JOIN
  ProcedureName PN
    ON  PN.CODE IN (Ev.Code1, Ev.Code2, Ev.Code3, Ev.Code4)
这里,MatchingCodes中的值为1表示Code1是匹配项。类似地,值3表示代码1和代码2都匹配,或值15表示所有代码都匹配

编辑:(明确需要多行后)

这与戈登的答案相似,但行为略有不同;每次匹配都会得到1行,而不是一直有4行,如果没有匹配,则会有一行
NULL
s

SELECT
  *
FROM
  Events   EV
OUTER APPLY
(
  SELECT 1 AS MatchedCode, * FROM ProcedureName WHERE CODE = EV.Code1
  UNION ALL
  SELECT 2 AS MatchedCode, * FROM ProcedureName WHERE CODE = EV.Code2
  UNION ALL
  SELECT 3 AS MatchedCode, * FROM ProcedureName WHERE CODE = EV.Code3
  UNION ALL
  SELECT 4 AS MatchedCode, * FROM ProcedureName WHERE CODE = EV.Code4
)
  PN

如果您只是想将固定数量的列转置到行中,您可以使用UNPIVOT:我反对UNPIVOT凌乱的语法,并建议按照Gordon的答案之一交叉应用!是的,交叉申请取消!试试这个,学习这个,这是很好的练习!我越来越接近使用这种技术,不幸的是,它将始终显示4行,即使只有1或2个连接,这并不理想:(我解决了这个问题,将整个语句包装在另一个select中,然后过滤掉过程描述为空的任何列。虽然不是最优雅的,但它可以工作。因此我将此标记为我的答案:)谢谢。@Sean-那就把你的
左连接
改为
内部连接
?这是同样的行为。(请注意,无论哪种方式,
事件
中任何在
过程重命名
中没有匹配项的行都会被这种方法过滤掉。)这是一个高科技的答案,但我喜欢二进制部分。不幸的是,这似乎并不能解决如果有多个过程使用相同的代码(即Code1=10,code2=10)会发生什么的问题。这只会导致显示一行。不可否认,OR连接导致了相同的问题,我在测试此方法后发现了这一问题。@Sean是的,您可以t一行有多个标志列,告诉您哪些代码匹配。如果您只想知道哪些代码匹配,您只需要一行。如果您需要多行,您应该在问题中说明这一点,并查看Gordon的答案:)