【问题标题】:SQL breakup string for use with WHERE IN用于 WHERE IN 的 SQL 分解字符串
【发布时间】:2017-07-13 03:52:27
【问题描述】:

我在 SQL Server 中有一个表,其中有一列存储一系列字符。我希望能够将一系列字符发送到存储过程并将其用于WHERE IN。下面的例子

ID | series
---+-------
 1 | U
 2 | B
 3 | R
 4 | UB
 5 | BR

我想要的回报如下:

Parameter | Return IDs
----------+------------
 U        | 1
 R        | 3
 U, R     | 1, 3

我可以以任何方式格式化参数,UR 或 U、R 或其他任何方式。我可以在应用程序中将其分解并调用该过程 N 次,但我宁愿不这样做,以便可以在查询中使用 order by。

【问题讨论】:

  • SQL Server 可以接受表类型作为 SPROC 参数 - 然后 SPROC 可以利用 JOIN 和其他 RA/SET 操作。它也将是“更多 SQL”,并且更容易期望输出本身被规范化。例如,使用集合{(U),(R)}(指定为表类型)调用将产生({R,3},{U,1})(两个规范化行)。然后,应用程序可以将结果转换为适合其自身目的的输出格式。
  • stackoverflow.com/questions/3476186/… 与“填充”到非标准化输出相关(我仍然建议不要这样做)。也可以编写一个 UDF 聚合函数。 IIRC,SQL Server 2016 有这样一个聚合,虽然没有想到..
  • 如果您的应用程序是用支持它的语言编写的,那么表值参数是可行的方法。

标签: sql sql-server where-in


【解决方案1】:

如果您不能使用表值参数,这是一种方法。

declare @table table (ID int, series varchar(16))
insert into @table
values
(1,'U'),
(2,'B'),
(3,'R'),
(4,'UB'),
(5,'BR')


declare @input varchar(4000) = 'U,R'

select
    t.*
from @table t
cross apply dbo.DelimitedSplit8K(@input,',') x 
where
x.Item = t.series

OR 没有交叉应用

;with cte as
(select * 
from dbo.DelimitedSplit8K(@input,',') x )

select
    t.*
from @table t
inner join cte on Item = t.series 

Here is the function 已被证明是一种快速的字符串拆分方法:

CREATE FUNCTION [dbo].[DelimitedSplit8K] (@pString VARCHAR(8000), @pDelimiter CHAR(1))
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE!  IT WILL KILL PERFORMANCE!

RETURNS TABLE WITH SCHEMABINDING AS
RETURN

/* "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
enough to cover VARCHAR(8000)*/

  WITH E1(N) AS (
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
                ),                          --10E+1 or 10 rows
       E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
       E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
 cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
                     -- for both a performance gain and prevention of accidental "overruns"
                 SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
                ),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
                 SELECT 1 UNION ALL
                 SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
                ),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
                 SELECT s.N1,
                        ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
                   FROM cteStart s
                )
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
 SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
        Item       = SUBSTRING(@pString, l.N1, l.L1)
   FROM cteLen l
;
GO

【讨论】:

    【解决方案2】:

    实现这一点的方法是传入一个分隔字符串,然后创建一个函数来接收您的参数,拆分值并返回一个表值。那么你需要做的就是创建一个类似的存储过程。

    CREATE PROCEDURE MyProc
    (
        @MyParameter NVARCHAR(3000)
    )
    AS
    
    SELECT 
        ID,
        Series
    FROM
        MyTable
    WHERE
        Series IN (SELECT ID FROM dbo.MyFunctionToSplitDelimitedString(@MyParameter,',')
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-20
      • 1970-01-01
      • 2012-07-16
      • 1970-01-01
      • 2013-07-03
      • 2021-05-07
      • 2011-04-06
      • 2017-04-24
      相关资源
      最近更新 更多