tsql联合查询
Posted
技术标签:
【中文标题】tsql联合查询【英文标题】:Tsql union query 【发布时间】:2015-06-03 20:41:28 【问题描述】:我正在寻找一种查询表的有效方法。 表结构为:
CREATE TABLE [dbo].[CaseManager](
[CaseID] [int] IDENTITY(1,1) NOT NULL,
[SystemUserCreatedBy] [int] NULL,
[SystemUserAssignee] [int] NULL,
CONSTRAINT [PK_Case] PRIMARY KEY CLUSTERED
(
[CaseID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY]
查询应返回每个 caseID 和用户 ID(用户 ID 可以是 SystemUserCreatedBy 或 SystemUserAssignee)位列,显示使用是 Createdby 还是 Assignee 我设法这样写:
select CaseID,UserID,
max(CaseUser.IsAssignee) as IsAssignee,
max(CaseUser.IsCreator) as IsCreator
FROM
(
select CMassignee.CaseID,
CMassignee.SystemUserAssignee as UserID,
1 as IsAssignee ,
0 as IsCreator
from CaseManager CMassignee
where CMassignee.SystemUserAssignee is not null
UNION
select CMCreator.CaseID,
CMCreator.SystemUserCreatedBy as UserID,
0 as IsAssignee ,
1 as IsCreator
from CaseManager CMCreator
where CMCreator.SystemUserCreatedBy is not null
) CaseUser
group by CaseID,UserID
我很确定有更好的方法来编写它,只需扫描该表一次。在该示例中,我只显示两列(SystemUserCreatedBy as SystemUserAssignee),但实际上我有五个需要添加。
请看数据示例:
SET IDENTITY_INSERT dbo.casemanager ON;
insert into casemanager(caseid,SystemUserCreatedBy,SystemUserAssignee)
values
(1,2222,3333)
SET IDENTITY_INSERT dbo.casemanager OFF;
在这种情况下,我希望得到:
CaseID UserID IsAssignee IsCreator
----------- ----------- ----------- -----------
1 2222 0 1
1 3333 1 0
(2 row(s) affected)
【问题讨论】:
“SystemUserCreatedBy 或 SystemUserAssignee”这是一个异或,总是只有一个不为空? 很高兴您添加了所需的输出...在您缺席期间对此进行了相当大的辩论:-) SystemUserCreatedBy 和 SystemUserAssignee 是可为空的列。 可空列没有问题,问题是是否应该将像 (1,2222,3333) 这样的行拆分为 2 个结果行。现在你说清楚了。 现在下一个问题是:SystemUserCreatedBy
和 SystemUserAssignee
可以是 CaseID
的同一用户吗?
【参考方案1】:
CaseID
是主键,因此不需要聚合(如果两列中只有一个不为空):
SELECT
CaseID,
COALESCE(SystemUserAssignee, SystemUserCreatedBy) AS UserID,
CASE WHEN SystemUserAssignee IS NOT NULL THEN 1 ELSE 0 end AS IsAssignee,
CASE WHEN SystemUserCreatedBy IS NOT NULL THEN 1 ELSE 0 end AS IsCreator
FROM CaseManager CMassignee
编辑:
基于最新的 cmets,两列都可以有数据并且可能是同一个用户,所以你的原始查询是好的(即使它扫描了两次表),你唯一需要更改的是 UNION ALL
而不是UNION
。
但是对于 5 个用户 ID,@Amit 的答案应该是最好的。
【讨论】:
我喜欢 COALESCE 的使用——这表明我对开发人员的偏见,我从来不记得。我会添加 where 子句以强制执行您在答案中描述(但未实现)的条件。 假设你有 1 行 user1, user2。 op 的查询将返回 2 行:user1 1 0 和 user2 0 1。您的查询将返回 user1 1 1。我认为这不是 op 想要的。 @GiorgiNakeuri:这就是我写“如果两列中只有一个不为空”的原因。 OP 并没有准确说明加上 “实际上我有五个需要添加”... 这是一个过于冗长、复杂、浪费的解决方案。再次阅读 OP - 目的是拥有 5 个“用户 ID”列和 5 个标志。您将获得 32 (2^5) 个选择和联合。非常慢,完全无法维护。 @Amit:我实际上忘记了 5 列,当然最后一个查询很愚蠢,我只是想避免聚合。如果您添加最后的 GROUP BY,我会投票赞成您的答案 :-)【参考方案2】:也许使用 CASE 语句
SELECT CaseID,
CASE
WHEN SystemUserAssignee IS NOT NULL THEN SystemUserAssignee
ELSE SystemUserCreatedBy
END as UserID,
CASE
WHEN SystemUserAssignee IS NOT NULL THEN 1
ELSE 0
END as IsAssignee,
CASE
WHEN SystemUserAssignee IS NOT NULL THEN 0
ELSE 1
END as IsCreator
FROM CaseManager
WHERE SystemUserAssignee IS NOT NULL OR SystemUserCreateBy IS NOT NULL
【讨论】:
你还需要在这里分组。但这根本不正确。如果一行包含 2 个不同的用户,您只需选择一个并丢失第二个用户的信息。 OP 想要在结果集中将SystemUserAssignee
和 SystemUserCreatedBy
的行拆分为 2 行,并为每一行使用适当的标志
查看我对 dnoent 回答的评论
@GiorgiNakeuri 他在问题中说它可能是/或不是两者。
@tvanfosson - 我和 GiorgiNakeuri 在一起。看看 OP 解决方案(据说可以按需要工作)。它拆分行。【参考方案3】:
SELECT CaseID ,
CASE WHEN SystemUserAssignee IS NOT NULL THEN SystemUserAssignee
ELSE SystemUserCreatedBy
END AS UserID ,
CASE WHEN SystemUserAssignee IS NOT NULL THEN 1
ELSE 0
END AS IsAssignee ,
CASE WHEN SystemUserAssignee IS NOT NULL THEN 0
ELSE 1
END AS IsAssignee
FROM CaseManager;
【讨论】:
使用 SSMS 工具格式化后,和@tvanfosson 的回答一模一样【参考方案4】:这可以通过加入一个“静态表”来实现,以将每一行复制必要的次数(本示例中为 2,但可以扩展为任意数量)。
SELECT CaseID, UserID, MAX(IsCreator) IsCreator, MAX(IsAssignee) IsAssignee
FROM (
SELECT CaseID, CASE
WHEN Switcher = 1 THEN SystemUserCreatedBy
WHEN Switcher = 2 THEN SystemUserAssignee
END AS UserID,
IIF(Switcher = 1, 1, 0) AS IsCreator,
IIF(Switcher = 2, 1, 0) AS IsAssignee
FROM dbo.CaseManager CROSS JOIN (VALUES(1),(2)) switcher(Switcher)
WHERE (SystemUserCreatedBy IS NOT NULL AND Switcher = 1) OR
(SystemUserAssignee IS NOT NULL AND Switcher = 2)
) sub
GROUP BY CaseID, UserId
ORDER BY CaseID, UserId
这个想法是切换器表为每个标志都有一行,然后主 SELECT
查询使用该值为每一行选择正确的值。
编辑:正如 dnoeth 正确提到的,我必须使用分组包装查询以匹配 OP。我认为这分散了答案的主要思想,因此解决方案的这一部分被空行“分隔”。
也可用:SQLFiddle
*注意:VALUES(1),(2)
语法仅适用于 SQL SERVER 2008 +
编辑 2:我重新考虑了解决方案,并提出了一个更优雅的查询。我不确定哪个版本会更快出现,并且我相信原始版本在其他情况下可能有用,所以我保留了两者。
版本 2:
SELECT CaseID, UserID, IsCreator=IIF(UserID=SystemUserCreatedBy, 1, 0),
IsAssignee=IIF(UserID=SystemUserAssignee, 1, 0)
FROM (
SELECT DISTINCT *, CASE
WHEN Switcher = 1 THEN SystemUserCreatedBy
WHEN Switcher = 2 THEN SystemUserAssignee
END AS UserID
FROM dbo.CaseManager CROSS JOIN (VALUES(1),(2)) switcher(Switcher)
) sub
WHERE UserID IS NOT NULL
ORDER BY CaseID, UserId
还有更新后的SQLFiddle
【讨论】:
以上是关于tsql联合查询的主要内容,如果未能解决你的问题,请参考以下文章