函数在where子句中

时间:2014-08-19 20:56:06

标签: sql sql-server sql-server-2008

我有两个场景。第一个是快速的,但不是一个选择。第二个使用函数并杀死索引。这些值看起来类似于这个' ww,tt,tt,bb'。他们可以根据需要添加尽可能多的代码。有没有比使用该功能更好的方法?这是使用server 2008的存储过程。

没有功能:

WHERE  date BETWEEN '20140701' AND '20140731' and 
LEFT(id, 2) IN ('wp')

使用功能:

WHERE  date BETWEEN '20140701' AND '20140731' and 
LEFT(id, 2) IN (SELECT* FROM Toolbox.dbo.Split_DelimitedString_fn(@string,',') )

4 个答案:

答案 0 :(得分:0)

我的建议是先将字符串拆分并将结果插入临时表:

INSERT INTO #tmp
   SELECT   *
   FROM     Toolbox.dbo.Split_DelimitedString_fn(@string,',')

然后将您的查询重写为

WHERE  date BETWEEN '20140701' AND '20140731' and 
LEFT(id, 2) IN (SELECT * FROM #tmp)

答案 1 :(得分:0)

因此,如果我正确理解了这个问题,你想让'in'子句由另一个表中的内容动态确定。

你试过了吗?

WHERE  date BETWEEN '20140701' AND '20140731' and 
LEFT(id, 2) IN (SELECT <column> FROM Toolbox.dbo.tblname)

第二次尝试: 将Left(id,2)移动到select子句中,然后对那些列进行过滤

select 
   <current query>
   ,Left(id,2) as sort
from
   <current query>
WHERE  date BETWEEN '20140701' AND '20140731' and 
sort IN (SELECT* FROM Toolbox.dbo.Split_DelimitedString_fn(@string,',') )

答案 2 :(得分:0)

声明一个表变量,并插入值:

DECLARE @SplitIds TABLE
(
  SplitId varchar(10)
)

Insert into @SplitIds (SplitIds)
SELECT field FROM Toolbox.dbo.Split_DelimitedString_fn(@string,',')

然后你可以加入这张桌子......

SELECT columns
FROM TableName a INNER JOIN @SplitIds b ON LEFT(id, 2) = b.SplitId
WHERE a.date BETWEEN '20140701' AND '20140731'

不确定这是否会加快查询速度,但您可以使用执行计划来帮助并检查索引

答案 3 :(得分:0)

如果我理解正确,性能问题的核心是如何在SQL中有效地拆分CSV字符串。如果是这样my answer here如何使用Jeff Moden's Tally-Ho CSV Splitter可能是票。为清楚起见,在此重复:

功能定义:

CREATE FUNCTION [dbo].[DelimitedSplit8K]
--===== Define I/O parameters
        (@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 for
                     -- both 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

上一个答案的用法示例;

with data as (
    select Code,Location,Quantity,Store from ( values
        ('L698-W-EA',          NULL,                                      2, 'A')
       ,('L82009-EA',          'A1K2, A1N2, C4Y3, CBP2',                  2, 'A')
       ,('L80401-A-EA',        'A1S2, SHIP, R2F1, CBP5, BRP, BRP1-20',    17,'A')
       ,('CWD2132W-BOX-25PK',  'A-AISLE',                                 1, 'M')
       ,('GM22660003-EA',      'B1K2',                                    1, 'M')
    )data(Code,Location,Quantity,Store)
)
,shredded as (
    select Code,Location,Quantity,Store,t.*
    from data
    cross apply [dbo].[DelimitedSplit8K](data.Location,',') as t
)
select 
    pvt.Code,pvt.Quantity,pvt.Store
   ,cast(isnull(pvt.[1],' ') as varchar(8)) as Loc1
   ,cast(isnull(pvt.[2],' ') as varchar(8)) as Loc2
   ,cast(isnull(pvt.[3],' ') as varchar(8)) as Loc3
   ,cast(isnull(pvt.[4],' ') as varchar(8)) as Loc4
   ,cast(isnull(pvt.[5],' ') as varchar(8)) as Loc5 
   ,cast(isnull(pvt.[6],' ') as varchar(8)) as Loc6
from shredded
pivot (max(Item) for ItemNumber in ([1],[2],[3],[4],[5],[6])) pvt;
;
go

得到以下特性:

Code              Quantity    Store Loc1     Loc2     Loc3     Loc4     Loc5     Loc6
----------------- ----------- ----- -------- -------- -------- -------- -------- --------
L698-W-EA         2           A                                                   
L82009-EA         2           A     A1K2      A1N2     C4Y3     CBP2              
L80401-A-EA       17          A     A1S2      SHIP     R2F1     CBP5     BRP      BRP1-20
CWD2132W-BOX-25PK 1           M     A-AISLE                                       
GM22660003-EA     1           M     B1K2