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 个结果行。现在你说清楚了。 现在下一个问题是:SystemUserCreatedBySystemUserAssignee 可以是 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 想要在结果集中将 SystemUserAssigneeSystemUserCreatedBy 的行拆分为 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联合查询的主要内容,如果未能解决你的问题,请参考以下文章

Django怎么多表联合查询

MYSQL 多表联合查询问题

sql联合查询语句(两张表)

mysql 三表联合查询

sql如何一对多联合查询

mysql 多表联合查询啥用