我有两个表tbl_Properties和tbl_Locations
tbl_Properties有属性列表(包括FK Location_Id) tbl_Location有一个位置列表
我正在使用以下存储过程来搜索某个位置的属性:
spPropGetSearch
(
@Location_Id int
)
AS
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 = @Location_Id OR @Location_Id = '0')
ORDER BY P.Prop_DateAdded DESC
我传递了location_id(例如' 1002')并且它工作正常并返回位于该位置/区域内的属性。 现在,我想传递多个location_Ids,例如' 1002',' 1005',' 1010'搜索位于所有这些区域的房产。
我该怎么做? 由于我不是数据库专家,我将非常感谢您的回复。
我找到了以下示例,它运行正常。请您查看并检查是否存在任何漏洞
USE AdventureWorks2012
GO
CREATE PROCEDURE usp_Employeelist
@Cities NVARCHAR(30)
AS
DECLARE @CitiesXML AS XML
SET @CitiesXML = cast(('<a>'+replace(@Cities,',' ,'</a><a>')+'</a>') AS XML)
SELECT
BusinessEntityID
, FirstName
, JobTitle
, City
FROM HumanResources.vEmployee
WHERE City IN
(
SELECT
A.value('.', 'varchar(max)')
FROM @CitiesXML.nodes('A') AS FN(A)
)
ORDER BY BusinessEntityID
GO
--Execute the stored procedure using multiple values
--through one parameter in this stored procedure
USE AdventureWorks2012
GO
EXEC usp_Employeelist
'Cambridge,Newport Hills,Berlin,Bordeaux'
GO
答案 0 :(得分:2)
使用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
作为理由,不要过于复杂化,而是要提高性能。当我说&#34;在这样的查询中放置OR使得SQL Server几乎不可能使用适当的索引时,我曾希望阻止你采用这种方法。&#34; 。
您可以按如下方式重写查询:
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
流向相应的查询
答案 1 :(得分:0)
spPropGetSearch
(
@Location_Id varchar(500)
)
AS
DECLARE @strQuery varchar(MAX)
SET @strQuery='
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 ('+ @Location_Id+') OR @Location_Id = ''0''
ORDER BY P.Prop_DateAdded DESC'
EXEC (@strQuery)
在存储过程中传递参数,如
EXEC spPropGetSearch '1002,1005,1010'
答案 2 :(得分:0)
使用动态qry。
create procedure dropsp
@no varchar(100)
as
begin
declare @nsql nvarchar(1000)
Set @nsql = ' SELECT *
FROM db.dbo.tmptable
WHERE no in (' + @no +')'
exec sp_executesql @nsql
end
go
exec dropsp 'aaaaaaaa,bbbb'