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的答案:)