如何更快地使用多值参数 运行 进行查询?
How to make query with multi value parameter run faster?
我有以下查询 运行 非常慢:
SELECT
DISTINCT a.Role as Role
FROM
[Table_A] a
JOIN
[Table_B] b ON (a.Key = b.Key)
WHERE
b.Date BETWEEN @StartDate AND @EndDate
AND ISNULL(a.ID, -1) IN (@People)
变量@StartDate
和@EndDate
和@People
的值来自SSRS报告中的参数。日期参数只是日期。 @People
参数是一个多值参数。
问题是 @People
包含超过 3000 个值。所以查询必须使用 IN
子句来完成所有的查询。当 运行 在 SSRS 中查询时,这真的、真的减慢了我的查询速度。
我想使用 exists 子句来替换 IN
子句,但我似乎无法在这种情况下使用它。我需要以某种方式 select EXISTS
子句中 @People
变量的值并将其连接回第一个 table,但我什至不知道是否这是可能的。
也许我在这种情况下尝试使用 EXISTS
时走错了方向。但我仍然需要修复查询以使其运行得更快。
有人可以帮忙吗?
ISNULL(a.ID, -1)
将使查询不可 SARGable。您最好使用 (a.ID IN (@People) OR a.ID IS NULL)
,但是,带有那么多参数的 IN
不太可能 运行 好。
我运行记忆在这里(我家里没有SSRS)但是如果我记得SSRS做了一些"magic"多值参数和IN
没有很好地扩展。也许您最好尝试使用 EXISTS
和拆分器(例如 DelimitedSplit8k
)。此具体示例依赖于 @People
少于 8000 个字符。
SELECT DISTINCT a.Role
FROM [Table_A] a
JOIN [Table_B] b ON a.Key = b.Key
WHERE b.Date BETWEEN @StartDate AND @EndDate
AND (EXISTS (SELECT 1
FROM dbo.DelimitedSplit8K(@People,',') DS
WHERE DS.Item = a.ID)
OR a.ID IS NULL);
然而,考虑到这里的序数位置并不重要,那么可以使用其他分离器。例如 XML Splitter.
为了完整起见,快速编写了一个 XML 拆分器函数:
CREATE FUNCTION dbo.XMLSplitter (@DelimitedString varchar(MAX))
RETURNS TABLE AS RETURN
SELECT n.d.value('.','varchar(MAX)') AS Item
FROM (VALUES(CONVERT(xml,'<d>'+ REPLACE(@DelimitedString,',','</d><d>') + '</d>'))) V(X)
CROSS APPLY V.X.nodes('d') n(d);
GO
添加了没有函数的完整示例:
SELECT DISTINCT a.Role
FROM [Table_A] a
JOIN [Table_B] b ON a.Key = b.Key
WHERE b.Date BETWEEN @StartDate AND @EndDate
AND (EXISTS (SELECT 1
FROM (VALUES(CONVERT(xml,'<d>'+ REPLACE(@DelimitedString,',','</d><d>') + '</d>'))) V(X)
CROSS APPLY V.X.nodes('d') n(d)
WHERE n.d.value('.','varchar(MAX)') = a.ID)
OR a.ID IS NULL);
我有以下查询 运行 非常慢:
SELECT
DISTINCT a.Role as Role
FROM
[Table_A] a
JOIN
[Table_B] b ON (a.Key = b.Key)
WHERE
b.Date BETWEEN @StartDate AND @EndDate
AND ISNULL(a.ID, -1) IN (@People)
变量@StartDate
和@EndDate
和@People
的值来自SSRS报告中的参数。日期参数只是日期。 @People
参数是一个多值参数。
问题是 @People
包含超过 3000 个值。所以查询必须使用 IN
子句来完成所有的查询。当 运行 在 SSRS 中查询时,这真的、真的减慢了我的查询速度。
我想使用 exists 子句来替换 IN
子句,但我似乎无法在这种情况下使用它。我需要以某种方式 select EXISTS
子句中 @People
变量的值并将其连接回第一个 table,但我什至不知道是否这是可能的。
也许我在这种情况下尝试使用 EXISTS
时走错了方向。但我仍然需要修复查询以使其运行得更快。
有人可以帮忙吗?
ISNULL(a.ID, -1)
将使查询不可 SARGable。您最好使用 (a.ID IN (@People) OR a.ID IS NULL)
,但是,带有那么多参数的 IN
不太可能 运行 好。
我运行记忆在这里(我家里没有SSRS)但是如果我记得SSRS做了一些"magic"多值参数和IN
没有很好地扩展。也许您最好尝试使用 EXISTS
和拆分器(例如 DelimitedSplit8k
)。此具体示例依赖于 @People
少于 8000 个字符。
SELECT DISTINCT a.Role
FROM [Table_A] a
JOIN [Table_B] b ON a.Key = b.Key
WHERE b.Date BETWEEN @StartDate AND @EndDate
AND (EXISTS (SELECT 1
FROM dbo.DelimitedSplit8K(@People,',') DS
WHERE DS.Item = a.ID)
OR a.ID IS NULL);
然而,考虑到这里的序数位置并不重要,那么可以使用其他分离器。例如 XML Splitter.
为了完整起见,快速编写了一个 XML 拆分器函数:
CREATE FUNCTION dbo.XMLSplitter (@DelimitedString varchar(MAX))
RETURNS TABLE AS RETURN
SELECT n.d.value('.','varchar(MAX)') AS Item
FROM (VALUES(CONVERT(xml,'<d>'+ REPLACE(@DelimitedString,',','</d><d>') + '</d>'))) V(X)
CROSS APPLY V.X.nodes('d') n(d);
GO
添加了没有函数的完整示例:
SELECT DISTINCT a.Role
FROM [Table_A] a
JOIN [Table_B] b ON a.Key = b.Key
WHERE b.Date BETWEEN @StartDate AND @EndDate
AND (EXISTS (SELECT 1
FROM (VALUES(CONVERT(xml,'<d>'+ REPLACE(@DelimitedString,',','</d><d>') + '</d>'))) V(X)
CROSS APPLY V.X.nodes('d') n(d)
WHERE n.d.value('.','varchar(MAX)') = a.ID)
OR a.ID IS NULL);