我尝试通过在此处发布其他相关问题来解决此问题,这些问题主要集中在查询的某些部分。但是,我不妨发布整个事情,看看是否有人可以提供帮助。我有以下表格,其中包含以下字段:
tblPerson - PersonID,PersonName
tblGroup - GroupID,名称
tblGroupMembership - PersonID,GroupID
tblPersonCities - CityID,PersonID,City
这是一个非常简单的设置。我们有Person和Group,GroupMembership是两者之间的多对多联盟。然后人们可以拥有多个城市。
我要做的是编写一个存储过程,用于根据几个不同的参数搜索这些数据。存储过程仅返回Person信息。存储过程应该采用3个参数:
@PersonName - 一个人的姓名或一个人姓名的一部分
@GroupIDList - 以逗号分隔的GroupID列表
@City - 城市名称或城市名称的一部分
我希望存储过程能够不需要任何参数的值。因此,如果所有参数都为NULL,那么它应该返回所有Person记录。如果传入GroupID列表,则它应仅返回与传入列表中的所有组匹配的Person记录。我希望我已经正确解释了这一点。我知道这是一个很长的问题,但我无法用其他方式解释。我有一些ALMOST工作的代码。唯一的问题是,如果所有参数都为NULL,它似乎不起作用。 (我还没弄明白如何引入城市)这是我的代码示例。 (注意:fnSplit是一个自定义函数,它使用逗号分隔的字符串并返回具有不同值的表)
declare @name varchar(50)
declare @city varchar(50)
declare @grouplist varchar(50)
set @name = null
set @city = null
set @grouplist = null
select distinct
p.PersonID,
p.PersonName,
c.City
from
tblPerson p left join tblCities c on p.PersonID = c.PersonID
join
(
select m.PersonID
from tblGroupMembership m
where (m.GroupID in (select item from fnSplit(@grouplist, ',')))
group by m.PersonID
having (count(*) = (select count(*) from fnSplit(@grouplist, ',')))
) as filter
on (@grouplist is not null) and (p.PersonID = filter.PersonID)
where
((@name is null) or (p.PersonName like '%' + @name + '%')) and
((@city is null) or (c.City like '%' + @city + '%'))
答案 0 :(得分:3)
我倾向于在这些情况下使用动态sql,因为使用OR
来容纳这种逻辑对于性能和可攻击性来说是可怕的。以下示例适用于SQL Server 2005 +:
DECLARE @SQL NVARCHAR(4000)
SET @SQL = 'SELECT DISTINCT
p.personid,
p.personname,
c.city
FROM TBLPERSON p
LEFT JOIN TBLCITIES c ON c.personid = p.personid '
SET @SQL = @SQL + CASE
WHEN @grouplist IS NOT NULL THEN
' JOIN (SELECT m.PersonID
FROM TBLGROUPMEMBERSHIP m
WHERE m.GroupID IN (SELECT item FROM fnSplit(@grouplist, ',')))
GROUP BY m.PersonID
HAVING COUNT(*) = (SELECT COUNT(*) FROM fnSplit(@grouplist, ',')))) g ON g.personid = p.personid '
ELSE
' '
END
SET @SQL = @SQL + ' WHERE 1 = 1 ' --trick to make contatentating WHERE clause easier
IF @name IS NOT NULL
SET @SQL = @SQL + ' AND p.personname LIKE '%' + @name + '% '
IF @city IS NOT NULL
SET @SQL = @SQL + ' AND c.city LIKE '%' + @city + '% '
BEGIN
EXEC sp_executesql @SQL N'@grouplist varchar(50), @grouplist varchar(50), @name varchar(50), @city varchar(50)',
@grouplist, @grouplist, @name, @city
END
请注意,sp_executesql 将缓存查询计划 - 每The curse and blessings of dynamic SQL。
答案 1 :(得分:1)
试试这个:
首先是将逗号分隔的GroupIds列表转换为表变量的函数...
CREATE FUNCTION [dbo].[ParseString] (@S Text, @delim VarChar(5))
Returns @tOut Table
(ValNum Integer Identity Primary Key,
sVal VarChar(8000))
As
Begin
Declare @dLLen TinyInt -- Length of delimiter
Declare @sWin VarChar(8000)-- Will Contain Window into text string
Declare @wLen Integer -- Length of Window
Declare @wLast TinyInt -- Boolean to indicate processing Last Window
Declare @wPos Integer -- Start Position of Window within Text String
Declare @sVal VarChar(8000)-- String Data to insert into output Table
Declare @BtchSiz Integer -- Maximum Size of Window
Set @BtchSiz = 7900 -- (Reset to smaller values to test routine)
Declare @dPos Integer -- Position within Window of next Delimiter
Declare @Strt Integer -- Start Position of each data value within Window
-- -------------------------------------------------------------------------
-- ---------------------------
If @delim is Null Set @delim = '|'
If DataLength(@S) = 0 Or
Substring(@S, 1, @BtchSiz) = @delim Return
-- ---------------------------
Select @dLLen = Len(@delim),
@Strt = 1, @wPos = 1,
@sWin = Substring(@S, 1, @BtchSiz)
Select @wLen = Len(@sWin),
@wLast = Case When Len(@sWin) = @BtchSiz
Then 0 Else 1 End,
@dPos = CharIndex(@delim, @sWin, @Strt)
-- ----------------------------
While @Strt <= @wLen
Begin
If @dPos = 0 Begin -- No More delimiters in window
If @wLast = 1 Set @dPos = @wLen + 1
Else Begin
Set @wPos = @wPos + @Strt - 1
Set @sWin = Substring(@S, @wPos, @BtchSiz)
-- ----------------------------------------
Select @wLen = Len(@sWin), @Strt = 1,
@wLast = Case When Len(@sWin) = @BtchSiz
Then 0 Else 1 End,
@dPos = CharIndex(@delim, @sWin, 1)
If @dPos = 0 Set @dPos = @wLen + 1
End
End
-- -------------------------------
Set @sVal = LTrim(Substring(@sWin, @Strt, @dPos - @Strt))
Insert @tOut (sVal) Values (@sVal)
-- -------------------------------
-- Move @Strt to char after last delimiter
Set @Strt = @dPos + @dLLen
Set @dPos = CharIndex(@delim, @sWin, @Strt)
End
Return
End
然后是存储过程
Create Procedure GetPersons
@PersonName varChar(50) = nULL,
@City varChar(50) = Null,
@GroupIDList varChar(5000)
As
Set NoCOunt On
Declare @Groups Table (GId Integer Primary Key Not Null)
If Len(@GroupIDList) = 0
Insert @Groups(GId)
Select GroupId From tblGroup
Else
Insert @Groups(GId)
Select Cast(sVal as Integer)
From dbo.ParseString(@GroupIDList, ',')
Select PersonId, PersonName
From tblPerson p
Where Exists (Select * From tblGroupMembership gm
Join @Groups g On g.GId = gm.GroupId
Where PersonId = p.PersonId)
And Exists (Select * From tblPersonCities
Where PersonId = p.PersonId
And City = IsNull(@City, City))
答案 2 :(得分:0)
如果您说“any parameter”为null,那还包括 @grouplist 吗?
如果是这样,可能是因为您正在对过滤器表进行内部联接。如果 @grouplist 为null,则不会有任何行来满足该连接,因为我假设fnSplit将不返回任何行,因此 in 语句将永远不会为true 。我现在只是在眼睛看它...