SQL Server UNION ALL 合并连接(连接)太慢
Posted
技术标签:
【中文标题】SQL Server UNION ALL 合并连接(连接)太慢【英文标题】:SQL Server UNION ALL Merge Join (Concatenation) too slow 【发布时间】:2017-08-17 15:56:58 【问题描述】:我有一个选择查询,它在两个具有相同结构的表(列和主键,它们具有不同的非聚集索引)上使用 UNION ALL
关键字。这两个表包含 3900 万行,其中一个为 100 万行,另一个为 3800 万行。仅在具有一百万行的 table1
上运行查询时,大约需要 0.2 秒,在 table2
上,我们有不同的情况,最多需要 0.5 到 1.2 秒,具体取决于 DB 的压力。
实际上,为了显示,我需要联合这两个表,但问题是联合查询需要 8 秒才能运行。查看执行计划时,最繁重的操作是 Merge Join (Concatenation)
,成本为 91%,我有点担心,因为我正在运行的 WHERE
子句从 table1
中选择 51 个条目,从table2
(更大的表格)。
我无法理解它,过去两天我一直在努力寻找解决问题的方法,但我发现要么使用 UNION
而不是 UNION ALL
,要么使用不必要的子句就像GROUP BY
或LEFT/INNER JOINS
一样。 Query 也执行分页,使用此命令 ORDER BY [Id] DESC OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY;
,所有测试(在单个表上和使用 UNION ALL
)都是使用分页(OFFSET
和 FETCH NEXT
)关键字执行的。
如果需要,我可以提供表格详细信息和查询详细信息。这是一个简单的选择查询,包含 2 个INNER JOINS
和 2 个LEFT JOINS
,所有连接的表都包含非常少量的数据(从 50 个条目到 20K 个条目)。
这是查询
SELECT *
FROM (SELECT tr.Id,
tr.Amount,
tr.TypeId,
t.Name AS [Type],
tr.Date,
tr.ExternalKey,
tr.ExternalDescription,
tr.GameId,
tr.GameProviderId,
gp.Name AS GameProvider,
u.Username,
u.Pincode,
gp.Name,
g.GameName,
u.OperatorId,
tr.BalanceBefore,
tr.BalanceAfter,
tr.UserId
FROM (
SELECT *
FROM dbo.ActiveTransactions at
WHERE ( 1 = 1 )
AND ( [Date] >= '2017-07-17 20:00:00' )
AND ( [TypeId] != 10 )
AND ( [UserId] = 29041 )
UNION ALL
SELECT *
FROM dbo.TransactionHistory th --WITH(INDEX(IX_TransactionHistory_DateType_UserId))
WHERE ( 1 = 1 )
AND ( [Date] >= '2017-07-17 20:00:00' )
AND ( [TypeId] != 10 )
AND ( [UserId] = 29041 )
) AS tr
INNER JOIN dbo.Users u ON tr.UserId = u.Id
LEFT JOIN dbo.GameProviders gp ON tr.GameProviderId = gp.Id
LEFT JOIN dbo.Games g ON tr.GameId = g.GameId AND tr.GameProviderId = g.ProviderId
INNER JOIN dbo.Types t ON tr.TypeId = t.Id ) AS t
ORDER BY [Id] DESC OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY;
【问题讨论】:
什么是段落?我将包括查询。 考虑在联合之前分别重复连接和过滤 activetransactions 和 transactionhistory,然后联合结果。这应该需要 ..2 + .5 到 1.2 并在 2 秒内完成。它会保存联合操作,直到数据被过滤。 【参考方案1】:这是一个有根据的猜测:不了解索引或执行计划,引擎实际正在追赶什么。
考虑:在连接/过滤器之后而不是之前进行联合:您必须重复连接,但您可能会在联合集处理中获得一些索引效率损失。
在这种情况下,我创建了两个 CTE,然后将它们联合起来。
因为我无法真正测试这个,我可能有一些语法错误。
WITH cte1 AS
(SELECT tr.Id,
tr.Amount,
tr.TypeId,
t.Name AS [Type],
tr.Date,
tr.ExternalKey,
tr.ExternalDescription,
tr.GameId,
tr.GameProviderId,
gp.Name AS GameProvider,
u.Username,
u.Pincode,
gp.Name,
g.GameName,
u.OperatorId,
tr.BalanceBefore,
tr.BalanceAfter,
tr.UserId
FROM (
SELECT *
FROM dbo.ActiveTransactions at
WHERE ( 1 = 1 )
AND ( [Date] >= '2017-07-17 20:00:00' )
AND ( [TypeId] != 10 )
AND ( [UserId] = 29041 )) AS tr
INNER JOIN dbo.Users u ON tr.UserId = u.Id
LEFT JOIN dbo.GameProviders gp ON tr.GameProviderId = gp.Id
LEFT JOIN dbo.Games g ON tr.GameId = g.GameId AND tr.GameProviderId = g.ProviderId
INNER JOIN dbo.Types t ON tr.TypeId = t.Id ) AS t),
CTE2 as (
SELECT tr.Id,
tr.Amount,
tr.TypeId,
t.Name AS [Type],
tr.Date,
tr.ExternalKey,
tr.ExternalDescription,
tr.GameId,
tr.GameProviderId,
gp.Name AS GameProvider,
u.Username,
u.Pincode,
gp.Name,
g.GameName,
u.OperatorId,
tr.BalanceBefore,
tr.BalanceAfter,
tr.UserId
FROM (SELECT *
FROM dbo.TransactionHistory th --WITH(INDEX(IX_TransactionHistory_DateType_UserId))
WHERE ( 1 = 1 )
AND ( [Date] >= '2017-07-17 20:00:00' )
AND ( [TypeId] != 10 )
AND ( [UserId] = 29041 ) as tr
INNER JOIN dbo.Users u ON tr.UserId = u.Id
LEFT JOIN dbo.GameProviders gp ON tr.GameProviderId = gp.Id
LEFT JOIN dbo.Games g ON tr.GameId = g.GameId AND tr.GameProviderId = g.ProviderId
INNER JOIN dbo.Types t ON tr.TypeId = t.Id) AS t
)
SELECT * from CTE1
UNION ALL
SELECT * from CTE2
ORDER BY [Id] DESC OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY;
【讨论】:
以上是关于SQL Server UNION ALL 合并连接(连接)太慢的主要内容,如果未能解决你的问题,请参考以下文章