使用table-valued parameters。第一步是创建你的类型:
CREATE TYPE dbo.ListOfInt AS TABLE (Value INT);
我倾向于为这些使用通用名称以允许重用,而不会混淆名称(例如,如果您将其命名为 LocationIDs,那么在类型中存储属性列表会变得混乱)。
然后你可以在你的存储过程中引用这个类型:
CREATE PROCEDURE dbo.spPropGetSearch @LocationIDs dbo.ListOfInt READONLY
AS
BEGIN
SELECT P.Prop_Id,
P.Prop_Title,
P.Prop_Bedrooms,
P.Prop_Price,
L.Location_Title
FROM tbl_Properties P
INNER JOIN tbl_Locations L
ON L.Location_Id = P.Location_Id
WHERE P.Location_Id IN (SELECT Value FROM @LocationIDs)
OR @Location_Id = 0
ORDER BY P.Prop_DateAdded DESC;
END
然后您可以使用以下方式调用它:
DECLARE @LocationIDs dbo.ListOfInt;
INSERT @LocationIDs (Value)
VALUES (1002), (1005), (1010);
EXECUTE dbo.spPropGetSearch @LocationIDs;
编辑
发现错误,就在这里:
OR @Location_Id = 0
这让我想到了一个新的观点,如果0 被传递,您似乎希望可以选择返回所有内容。我会使用IF/ELSE:
CREATE PROCEDURE dbo.spPropGetSearch @LocationIDs dbo.ListOfInt READONLY
AS
BEGIN
IF EXISTS (SELECT 1 FROM @LocationIDs)
BEGIN
SELECT P.Prop_Id,
P.Prop_Title,
P.Prop_Bedrooms,
P.Prop_Price,
L.Location_Title
FROM tbl_Properties P
INNER JOIN tbl_Locations L
ON L.Location_Id = P.Location_Id
WHERE P.Location_Id IN (SELECT Value FROM @LocationIDs)
ORDER BY P.Prop_DateAdded DESC;
END
ELSE
BEGIN
SELECT P.Prop_Id,
P.Prop_Title,
P.Prop_Bedrooms,
P.Prop_Price,
L.Location_Title
FROM tbl_Properties P
INNER JOIN tbl_Locations L
ON L.Location_Id = P.Location_Id
ORDER BY P.Prop_DateAdded DESC;
END
END
GO
因此,如果传递的表值参数为空,它将返回所有记录,如果它包含记录,它将仅包含提供的 location_ids。将OR 放在这样的查询中会使 SQL Server 几乎不可能使用适当的索引。
附录
回复评论
而不是使用:IF EXISTS (SELECT 1 FROM @LocationIDs) 我们如何在 WHERE 子句中使用 OR 条件
我的回答是,我不建议使用IF/ELSE 是有原因的,不是为了使事情过于复杂,而是为了提高性能。当我说“在这样的查询中放置 OR 使得 SQL Server 几乎不可能使用适当的索引”时,我曾希望阻止您使用这种方法。。
您可以按如下方式重写查询:
SELECT P.Prop_Id,
P.Prop_Title,
P.Prop_Bedrooms,
P.Prop_Price,
L.Location_Title
FROM tbl_Properties P
INNER JOIN tbl_Locations L
ON L.Location_Id = P.Location_Id
WHERE P.Location_Id IN (SELECT Value FROM @LocationIDs)
OR NOT EXISTS (SELECT 1 FROM @LocationIDs)
ORDER BY P.Prop_DateAdded DESC;
这种方法的问题是,在同一个查询中确实有两个选项,而这些选项很可能需要两个不同的执行计划。如果您在p.Location_ID 上有索引,并且在@LocationIDs 中有记录,那么最好的查询计划是在tbl_Properties.Location_ID 上使用索引查找。如果@LocationIDs 为空,则索引查找毫无意义,最好的计划是对tbl_Properties 进行聚集索引扫描(表扫描)。由于 SQL Server 使用缓存计划,它只能缓存在或其他,这意味着如果它存储索引搜索选项,每次传递一个空表时,你都有一个次优计划,或者,如果它缓存表扫描计划,每次您传递位置 ID 的值时,您都没有利用那里的索引。
一种解决方法是OPTION (RECOMPILE):
SELECT P.Prop_Id,
P.Prop_Title,
P.Prop_Bedrooms,
P.Prop_Price,
L.Location_Title
FROM tbl_Properties P
INNER JOIN tbl_Locations L
ON L.Location_Id = P.Location_Id
WHERE P.Location_Id IN (SELECT Value FROM @LocationIDs)
OR NOT EXISTS (SELECT 1 FROM @LocationIDs)
ORDER BY P.Prop_DateAdded DESC
OPTION (RECOMPILE);
这会强制为永远执行重新编译查询,以确保您拥有当前执行的最佳计划。但是由于您实际上只有两个选择,因此这是很多不必要的重新编译。所以最好的选择是有两个查询,每个查询都有自己的缓存执行计划,并使用IF/ELSE 流运算符根据@LocationIDs 传递的内容流向适当的查询。