【问题标题】:Three-way FULL OUTER JOIN / table coalesce三路 FULL OUTER JOIN / 表合并
【发布时间】:2011-06-10 21:24:02
【问题描述】:

我想在三个表上执行FULL OUTER JOIN,合并公共行。

SELECT * FROM Users

id  Username  Fullname
==  ========  =====================
 7  iboyd     Ian Boyd
 8  nicholle  Nicholle Kuzniak
10  jamie     Jamie Bellaire

3 row(s) affected


SELECT * FROM GrobUsers

id  Username  Fullname
==  ========  =====================
 7  iboyd     Ian Alexander Boyd 
 8  nicholle  Nicholle Bachand
 9  chris     Chris Windibank

3 row(s) affected


SELECT * FROM FrobUsers

id  Username  Fullname
==  ========  =====================
 7  ian       Ian
 9  chris     Chris W.
10  jamie     James Bellaire

3 row(s) affected

我想根据id 列合并表格。

这带来了我该如何做的问题 希望在对方解决冲突时 列值不同。算法 可用于解决用户名和全名之间冲突的方法是:

 if (id's are equal) then 
    pick one; i don't care

我尝试过类似的方法:

SELECT
   COALESCE(Users.id, GrobUsers.id, FrobUsers.id) AS id,
   COALESCE(Users.Username, GrobUsers.Username, FrobUsers.Username) AS Username,
   COALESCE(Users.FullName, GrobUsers.FullName, FrobUsers.FullName) AS Fullname
FROM Users
   FULL OUTER JOIN GrobUsers ON GrobUsers.id = Users.id

   FULL OUTER JOIN FrobUsers ON FrobUsers.id = .....something......

【问题讨论】:

  • 我在这台计算机上没有开发工具,所以没有尝试过,但是你可以使用CROSS APPLY,然后使用由 ID 字段之一分区的ROW_NUMBER 重新查询并返回行ROW_NUMBER 是 1?

标签: sql-server sql-server-2000 full-outer-join


【解决方案1】:

一个典型的技巧——使用没有意义的聚合函数。

select id, min(username), min(fullname) from (
  SELECT * FROM Users 
  union
  SELECT * FROM FrobUsers
  union
  SELECT * FROM GrobUsers  
) as foo
group by foo.id

嗯...但是它可能会从一个表中选择用户名,从另一个表中选择全名。如果您仍然不在乎,请使用它,否则...也许

select id, username, fullname from (
  select id, username, fullname, takeme = row_number() over (partition by id)
  from (
    SELECT * FROM Users 
    union
    SELECT * FROM FrobUsers
    union
    SELECT * FROM GrobUsers  
  ) as foo
) as bar
where bar.takeme = 1

【讨论】:

  • 如果你UNION将表放在一起,然后用ID字段分区的ROW_NUMBER()查询结果,你可以得到匹配的usernamefullname,其中ROW_NUMBER是1.
  • @Duncan 这正是我同时输入的内容:)
  • 我认为您在 select 子句中的意思是 id 而不是 if。我尝试编辑它,但编辑需要超过 6 个字符...
【解决方案2】:

在您提供的示例中,您根本不需要连接。我希望这是一个真实的例子,而不是人为的例子。在这里你想做的很简单,你根本不需要join,也不需要row_number。你可以这样做:

select id,Username,Fullname from Users
UNION ALL
select id,Username,Fullname from GrobUsers 
where id not in (select id from Users)
UNION ALL
select id,Username,Fullname from FrobUsers  
where id not in (select id from Users) and id not in (select id from GrobUsers)

它会给你这个:

id          Username   Fullname        
----------- ---------- -----------------
7           iboyd      Ian Boyd         
8           nicholle   Nicholle Kuzniak 
9           chris      Chris Windibank  
10          jamie      Jamie Bellaire   

(4 row(s) affected)

这是我使用的测试用例:

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Users]') AND type in (N'U'))
DROP TABLE [dbo].[Users]
GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[GrobUsers]') AND type in (N'U'))
DROP TABLE [dbo].[GrobUsers]
GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[FrobUsers]') AND type in (N'U'))
DROP TABLE [dbo].[FrobUsers]
GO

CREATE TABLE [dbo].[Users](
    [Id] [int] NOT NULL,
    [Username] [nchar](50) NULL,
    [Fullname] [nchar](50) NULL,
 CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

CREATE TABLE [dbo].[GrobUsers](
    [Id] [int] NOT NULL,
    [Username] [nchar](50) NULL,
    [Fullname] [nchar](50) NULL,
 CONSTRAINT [PK_GrobUsers] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

CREATE TABLE [dbo].[FrobUsers](
    [Id] [int] NOT NULL,
    [Username] [nchar](50) NULL,
    [Fullname] [nchar](50) NULL,
 CONSTRAINT [PK_FrobUsers] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

INSERT INTO Users Values (7,'iboyd','Ian Boyd' )
INSERT INTO Users Values (8,'nicholle','Nicholle Kuzniak' )
INSERT INTO Users Values (10,'jamie','Jamie Bellaire' )


INSERT INTO [GrobUsers] Values (7,'iboyd','Ian Alexander Boyd ' )
INSERT INTO [GrobUsers] Values (8,'nicholle','Nicholle Bachand' )
INSERT INTO [GrobUsers] Values (9,'chris','Chris Windibank' )

INSERT INTO [FrobUsers] Values (7,'iboyd','Ian' )
INSERT INTO [FrobUsers] Values (9,'nicholle','Chris W.' )
INSERT INTO [FrobUsers] Values (10,'jamie','James Bellaire' )
GO

【讨论】:

  • 当使用where id not in时,你可以通过使用union all而不是union来节省服务器的一些工作。
  • 我要把它交给 zespri,因为他的解决方案适用于 2000 年(这是我正在使用的),并从同一行获取用户名和全名。但是@GSerg,你的想法也不错!
  • @IanBoyd,您的编辑令人费解。你试图完成什么?
  • @IanBoyd,好的,有道理。我会在几分钟后删除我的这条评论和之前的评论。
猜你喜欢
  • 2017-11-17
  • 2019-01-27
  • 1970-01-01
  • 2013-03-10
  • 1970-01-01
  • 2012-02-13
  • 2019-08-07
  • 2011-09-28
  • 2013-04-16
相关资源
最近更新 更多