SQL 服务器将前两行合并为两列
SQL Server Join first two rows as two columns
我有两个表:
Table User
:
Id, Name, Email,...
Table Images
:
UserId, Priority, Path,...
我想创建一个用户视图,其中包含每个具有最高优先级的用户的两张图片,结果如下:
User.Id, User.Name, User.Email, Image1, Image2
-------------------------------------------------
我想,如果用户没有图片,Image1
和Image2
两列都会是NULL
,如果只有一张图片,那么Image1
将被设置并且 Image2
将是 NULL
.
我怎样才能最有效地做到这一点?我的意思是数据库性能。
使用这些示例架构/数据作为模型:
CREATE TABLE #User (Id INT, Name VARCHAR(MAX), Email VARCHAR(MAX))
CREATE TABLE #Images (UserId INT, [Priority] INT, [Path] VARCHAR(MAX))
INSERT INTO #User VALUES
(1, 'Bob', 'bob@foo.com'),
(2, 'Jim', 'jim@foo.com')
INSERT INTO #Images VALUES
(1, 10, 'path1'),
(1, 5, 'path2'),
(1, 7, 'path3')
你可以使用 PIVOT
:
SELECT UserId, [1] AS PathOfImage1, [2] AS PathOfImage2
FROM
(SELECT UserId, [Path],
ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY [Priority] DESC) AS rn
FROM #Images ) AS source
PIVOT
(
MAX([Path])
FOR rn IN ([1], [2])
) AS pvt
要在单个记录中为每个用户获取 top 两张图片:
UserId PathOfImage1 PathOfImage2
------------------------------------
1 path1 path3
请注意,Id=2
的用户没有出现在上面的结果集中,因为他没有任何相关图片。
您现在可以对上述查询生成的 table 表达式执行 OUTER APPLY
:
SELECT u.Id, u.Name, Images.PathOfImage1, Images.PathOfImage2
FROM #User u
OUTER APPLY (
SELECT UserId, [1] AS PathOfImage1, [2] AS PathOfImage2
FROM (
SELECT UserId, [Path],
ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY [Priority] DESC) AS rn
FROM #Images ) AS source
PIVOT
(
MAX([Path])
FOR rn IN ([1], [2])
) AS pvt
WHERE UserId = u.Id
) Images
为了得到想要的输出:
Id Name PathOfImage1 PathOfImage2
----------------------------------------
1 Bob path1 path3
2 Jim NULL NULL
编辑:
如果应从 Image
table 中提取多个列,则 PIVOT
必须替换为旧式条件聚合,因为 SQL 服务器不在 PIVOT
:
上支持多个聚合
SELECT t.UserId,
[PathOfImage1] = MAX(CASE WHEN rn = 1 THEN [Path] ELSE NULL END),
[PathOfImage2] = MAX(CASE WHEN rn = 2 THEN [Path] ELSE NULL END),
[PriorityOfImage1] = MAX(CASE WHEN rn = 1 THEN [Priority] ELSE NULL END),
[PriorityOfImage2] = MAX(CASE WHEN rn = 2 THEN [Priority] ELSE NULL END)
FROM (
SELECT UserId, [Path], [Priority],
ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY [Priority] DESC) AS rn
FROM #Images ) t
GROUP BY t.UserId
我有两个表:
Table User
:
Id, Name, Email,...
Table Images
:
UserId, Priority, Path,...
我想创建一个用户视图,其中包含每个具有最高优先级的用户的两张图片,结果如下:
User.Id, User.Name, User.Email, Image1, Image2
-------------------------------------------------
我想,如果用户没有图片,Image1
和Image2
两列都会是NULL
,如果只有一张图片,那么Image1
将被设置并且 Image2
将是 NULL
.
我怎样才能最有效地做到这一点?我的意思是数据库性能。
使用这些示例架构/数据作为模型:
CREATE TABLE #User (Id INT, Name VARCHAR(MAX), Email VARCHAR(MAX))
CREATE TABLE #Images (UserId INT, [Priority] INT, [Path] VARCHAR(MAX))
INSERT INTO #User VALUES
(1, 'Bob', 'bob@foo.com'),
(2, 'Jim', 'jim@foo.com')
INSERT INTO #Images VALUES
(1, 10, 'path1'),
(1, 5, 'path2'),
(1, 7, 'path3')
你可以使用 PIVOT
:
SELECT UserId, [1] AS PathOfImage1, [2] AS PathOfImage2
FROM
(SELECT UserId, [Path],
ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY [Priority] DESC) AS rn
FROM #Images ) AS source
PIVOT
(
MAX([Path])
FOR rn IN ([1], [2])
) AS pvt
要在单个记录中为每个用户获取 top 两张图片:
UserId PathOfImage1 PathOfImage2
------------------------------------
1 path1 path3
请注意,Id=2
的用户没有出现在上面的结果集中,因为他没有任何相关图片。
您现在可以对上述查询生成的 table 表达式执行 OUTER APPLY
:
SELECT u.Id, u.Name, Images.PathOfImage1, Images.PathOfImage2
FROM #User u
OUTER APPLY (
SELECT UserId, [1] AS PathOfImage1, [2] AS PathOfImage2
FROM (
SELECT UserId, [Path],
ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY [Priority] DESC) AS rn
FROM #Images ) AS source
PIVOT
(
MAX([Path])
FOR rn IN ([1], [2])
) AS pvt
WHERE UserId = u.Id
) Images
为了得到想要的输出:
Id Name PathOfImage1 PathOfImage2
----------------------------------------
1 Bob path1 path3
2 Jim NULL NULL
编辑:
如果应从 Image
table 中提取多个列,则 PIVOT
必须替换为旧式条件聚合,因为 SQL 服务器不在 PIVOT
:
SELECT t.UserId,
[PathOfImage1] = MAX(CASE WHEN rn = 1 THEN [Path] ELSE NULL END),
[PathOfImage2] = MAX(CASE WHEN rn = 2 THEN [Path] ELSE NULL END),
[PriorityOfImage1] = MAX(CASE WHEN rn = 1 THEN [Priority] ELSE NULL END),
[PriorityOfImage2] = MAX(CASE WHEN rn = 2 THEN [Priority] ELSE NULL END)
FROM (
SELECT UserId, [Path], [Priority],
ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY [Priority] DESC) AS rn
FROM #Images ) t
GROUP BY t.UserId