2017-05-19 30 views
0

我有两个存储过程,当前选择从Users (U)其中U有一个预订匹配一些条件(IsPaid = 1和MonthNo匹配传递给存储过程)。动态IN与SQL

--users(U) -

UserID  Name 
1   John 
2   Bill 
3   Tom 

--Bookings(B) -

BookingID  UserID   MonthNo  IsPaid 
5    1    2   1 
6    1    3   1 
7    1    4   0 
8    2    2   1 
9    2    3   1 
10    2    4   1 
11    3    4   1 

ALTER PROCEDURE FindUsers... 
    @MonthNo 
AS 
    ... 
    WHERE B.IsPaid = 1 AND B.MonthNo = @MonthNo 

所以目前,如果@MonthId = 3,用户#2和#3返回

我现在需要通过一个列表过程来返回用户的预订范围匹配。即:

ALTER PROCEDURE FindUsers... 
    @MonthNosCsv 
AS 
    ... 
    -- split month numbers somehow, then check whether all matching `Booking` rows match. 

伪代码....

WHERE ALL(B.IsPaid = 1 AND B.MonthNo IN(CsvSplit(@MonthNoCsv))) 

所以,如果@MonthNoCsv是 '2,3,4',只有用户2返回,因为他们已经在2,3个月内支付预订和4.

这可能在SQL中,还是最好在消费应用程序中进行二次处理?

+0

此链接http://www.sommarskog.se/arrays-in-sql-2008.html可能让你开始 - 但我怀疑它需要技巧的水平超出了你的实施能力。在http://www.sommarskog.se/arrays-in-sql.html – SMor

回答

0

每个人都应该有一个好的分离器。有很多选项可用。然而,我确实提供了我使用的那个。

下面列出两个选项,第一个是内嵌版本,第二个使用解析函数。两者都会返回相同的结果。

选项1 - 如果没有一个解析函数

Declare @MonthNoCsv varchar(50) = '2,3,4' 

;with cte as (
    Select RetSeq = Row_Number() over (Order By (Select null)) 
      ,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)'))) 
    From (Select x = Cast('<x>' + replace((Select replace(@MonthNoCsv,',','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A 
    Cross Apply x.nodes('x') AS B(i) 
) 
Select U.* 
from cte M 
Join Bookings B on M.RetVal=B.MonthNo and B.IsPaid=1 
Join Users U on U.UserID=B.UserID 
Group By U.UserID,U.Name 
Having Count(Distinct B.MonthNo)=(Select max(RetSeq) from cte) 

选项2 - 对于parse函数

Declare @MonthNoCsv varchar(50) = '2,3,4' 
;with cte as (
    Select * from [dbo].[udf-Str-Parse](@MonthNoCsv,',') 
) 
Select U.* 
from cte M 
Join Bookings B on M.RetVal=B.MonthNo and B.IsPaid=1 
Join Users U on U.UserID=B.UserID 
Group By U.UserID,U.Name 
Having Count(Distinct B.MonthNo)=(Select max(RetSeq) from cte) 

都返回

UserID Name 
2  Bill 

的UDF如果有兴趣

CREATE FUNCTION [dbo].[udf-Str-Parse] (@String varchar(max),@Delimiter varchar(10)) 
Returns Table 
As 
Return ( 
    Select RetSeq = Row_Number() over (Order By (Select null)) 
      ,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)'))) 
    From (Select x = Cast('<x>' + replace((Select replace(@String,@Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A 
    Cross Apply x.nodes('x') AS B(i) 
); 
--Thanks Shnugo for making this XML safe 
--Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',') 
--Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ') 
--Select * from [dbo].[udf-Str-Parse]('this,is,<test>,for,< & >',',') 
+2

上,Erland也使用CSV字符串(这是您开始下载的路径)进行了更简单的讨论“每个人都应该拥有一个好的分离器” - 我不同意。我宁愿所有人都转而使用数据类型*设计*来保存多个值(表格,xml,json,如果在2016或更高版本中),而不是继续乱七八糟的字符串。 –

+0

@Damien_The_Unbeliever很难去争辩......但是,我相信你必须从超出你的控制范围的来源消费数据。考虑到这一点,即使MS“加油”,并在2016年添加了Sting_Split() –