SQL中的拆分字符串值,用于灵活过滤

时间:2016-09-02 10:00:04

标签: sql sql-server stored-procedures

我有一个功能,用户可以在某个租户中分配多个类别(食物,非食物等)。请参见示例数据表

表:tblSales

    date        tenant    sales   category
    1/1/2015    tenant1   1000    Food,Non-Food,Kiosk 
    1/1/2015    tenant2   2000    Food
    1/1/2015    tenant3   1000    Non-Food,Kiosk

当用户选择“类别列”中列出的任何类别时,系统应该能够加载记录。

例如,用户选择的类别:非食品,信息亭。预期结果应为:

    date        tenant    sales   category
    1/1/2015    tenant1   1000    Food,Non-Food,Kiosk 
    1/1/2015    tenant3   1000    Non-Food,Kiosk

因为非食品和报亭出现在租户1和3中。

所以,我认为,该过程应首先对Category列的值进行字符串操作,将每个单词分隔为逗号。我的代码无法正常工作

@Category nvarchar(500)  = 'Non-Food,Kiosk' --User selected

SELECT  date,tenant,sales,category
FROM  tblSales
WHERE (category  in (SELECT val FROM dbo.split (@Category, @delimeter)))

这似乎不起作用,因为它分裂的是用户选择的类别而不是数据本身的值。我试过这个

 @Category nvarchar(500)  = 'Non-Food,Kiosk' --User selected

    SELECT  date,tenant,sales,category
    FROM  tblSales
    WHERE ((SELECT val FROM dbo.split (category, @delimeter)) in (SELECT val FROM dbo.split (@Category, @delimeter)))

但是这导致了这个错误

子查询返回的值超过1。当子查询遵循=,!=,<,< =,>,> =或子查询用作表达式时,不允许这样做。

4 个答案:

答案 0 :(得分:1)

通常,将CSV数据存储到数据库列中是不好的做法,因为正如您目前所看到的,它会呈现数据库无法使用的许多优点。

但是,我认为你可以通过使用LIKE来逃脱。假设用户选择了类别Non-FoodKiosk,您可以尝试以下查询:

SELECT date,
       tenant,
       sales,
       category
FROM tblSales
WHERE category LIKE 'Non-Food' OR
      category LIKE 'Kiosk'

答案 1 :(得分:1)

除了Tim的回答(他对数据库中的CSV字段绝对正确!)请注意,SQL Server 2016引入了STRING_SPLIT函数。对于单个类别,它就像:

一样简单
SELECT
  date
 ,tenant
 ,sales
 ,category
FROM tblSales
WHERE @Category IN (SELECT value FROM STRING_SPLIT(category, ','))

对于以逗号分隔的类别列表,您必须与EXISTS一起使用两次:

WHERE EXISTS
(
 SELECT *  
 FROM STRING_SPLIT(category, ',')  
 WHERE value IN (SELECT value FROM STRING_SPLIT(@Category, ','))
)

如果您使用的是较旧的SQL Server版本,则可以编写自己的STRING_SPLIT函数,请查看T-SQL split string。您可以使用与上面相同的语法来使用该函数(请注意我在这里编写代码并且未经测试,因此您可能需要一些修复)。

关于性能的注意事项:从QP你可以检查子查询的执行方式,从天真的角度来看我会说CTE,临时表和子查询具有大致相同的性能(在这个简单的例子中)但是如果此代码对性能至关重要,则最好执行一些基准测试(使用实际数据和实际访问方案)。

答案 2 :(得分:0)

尝试使用以下代码。

  1. 创建一个拆分删除字符串的函数。

    CREATE FUNCTION SplitWords
    (    
      @Input NVARCHAR(MAX),
      @Character CHAR(1)
    )
      RETURNS @Output TABLE (
      Item NVARCHAR(1000)
     )
     AS
     BEGIN
      DECLARE @StartIndex INT, @EndIndex INT
    
      SET @StartIndex = 1
      IF SUBSTRING(@Input, LEN(@Input) - 1, LEN(@Input)) <> @Character
      BEGIN
            SET @Input = @Input + @Character
      END
    
      WHILE CHARINDEX(@Character, @Input) > 0
      BEGIN
            SET @EndIndex = CHARINDEX(@Character, @Input)
    
            INSERT INTO @Output(Item)
            SELECT SUBSTRING(@Input, @StartIndex, @EndIndex - 1)
    
            SET @Input = SUBSTRING(@Input, @EndIndex + 1, LEN(@Input))
      END
    
      RETURN
     END
     GO
    
  2. 在prcedure / script中创建一个输入tabl,并将拆分数据保存在其中。这里输入的是@Category

    DECLARE @input TABLE (item VARCHAR(50))
    INSERT INTO @input
    SELECT Item
    FROM  [dbo].SplitWords (@Category, ',')  
    
  3. 使用like运算符与实际表格进行联接

    SELECT DISTINCT  a.date,
         a.tenant,
         a.sales,
         a.category
     FROM tblSales s
       JOIN @input a
         ON category LIKE '%'+item+'%'
    

答案 3 :(得分:0)

您可以尝试使用SQL Select语句,其中我使用了用户定义的SQL function for split string任务

declare @Category nvarchar(500)  = 'Non-Food,Kiosk'
declare @cnt int = (select COUNT(*) from dbo.SPLIT(@Category,','))
;with cte as (
    select 
        t.*, COUNT(*) over (partition by tenant) cnt
    from dbo.SPLIT(@Category,',') u
    inner join (
        select
            tblSales.*, c.val
        from tblSales
        cross apply dbo.SPLIT(tblSales.category,',') c
    ) t on u.val = t.val
)
select distinct tenant from cte where cnt = @cnt

enter image description here