如何在SQL SERVER中按键查询多值列

时间:2019-08-23 17:02:19

标签: sql sql-server split multivalue

如何在multivalued列中查询特定键?

样本数据

ID   DAY              PRICE
1    01;02;03;04;...  100;230;110;34.5;...
2    01;02;03;04;...  120;240;510;34.5;...

例如:

选择...,其中DAY键='02'

预期:

ID DAY PRICE
1  02  230
2  02  240

注释

实际表有30多个字段。

加入多个CROSS APPLY SPLIT_STRING看起来很乏味

4 个答案:

答案 0 :(得分:2)

这里是一个选项,它将动态地取消透视数据(实际上并没有使用动态SQL),然后透视结果。

您只需要在for Item in (...)部分列出30列

交叉应用B 会将 ROW 转换为XML

交叉应用C UNPIVOT XML

交叉应用D 将从C中解析/分割定界字符串(带有序列)

那对PIVOT来说就不大了

示例

Declare @YourTable Table ([ID] varchar(50),[DAY] varchar(50),[PRICE] varchar(50))
Insert Into @YourTable Values 
 (1,'01;02;03;04','100;230;110;34.5')
,(2,'01;02;03;04','120;240;510;34.5')

Select *
 From  (
        Select A.ID
              ,C.Item
              ,D.*
         From  @YOurTable A
         Cross Apply ( values (cast((Select A.* for XML RAW) as xml))) B(XMLData)
         Cross Apply (
                        Select Item  = xAttr.value('local-name(.)', 'varchar(100)')
                              ,Value = xAttr.value('.','varchar(max)')
                         From  XMLData.nodes('//@*') xNode(xAttr)
                         Where xAttr.value('local-name(.)','varchar(100)') not in ('Id','Other-Columns','To-Exclude')
                     ) C
         Cross Apply (
                        Select RetSeq = row_number() over (order by (Select null))
                              ,RetVal = ltrim(rtrim(B.i.value('(./text())[1]', 'varchar(100)')))
                        From  ( values (cast('<x>' + replace(C.Value,';','</x><x>')+'</x>' as xml))) as A(x)
                        Cross Apply x.nodes('x') AS B(i)
                     ) D
        ) src
 Pivot (max(RetVal) for Item in ([Day],[Price]) ) pvt
 Where Day='02'

返回

ID  RetSeq  Day Price
1   2       02  230
2   2       02  240

答案 1 :(得分:2)

此解决方案使用cte,希望它对您有用:

with cte1 as
( 
select id, value daykey,
 row_number() over(order by (select null)) as rowid
from mvct
cross apply string_split(day, ";")
),
cte2 as
(
select id, value pricekey,
 row_number() over(order by (select null)) as rowid
from mvct
cross apply string_split(price, ";")
)
select cte1.id, cte1.daykey, cte2.pricekey
from cte1
inner join cte2 on cte1.id = cte2.id
and cte1.rowid = cte2.rowid
and cte1.daykey = "02"

答案 2 :(得分:1)

您可以在https://www.sqlservercentral.com/articles/reaping-the-benefits-of-the-window-functions-in-t-sql-2上找到原始的DelimitedSplit8k_LEAD功能代码

演示2列

select id, details.[day], details.price
from (
     values
     (1,'01;02;03;04','100;230;110;34.5')
    ,(2,'01;02;03;04','120;240;510;34.5')
) t (ID,[Day],Price)
cross apply (
    select d.item [day], p.item price
    from DelimitedSplit8k_LEAD([Day],';') d
    join DelimitedSplit8k_LEAD(Price,';') p on d.ItemNumber = p.ItemNumber
) details

答案 3 :(得分:1)

如果可以在数据库中创建其他功能,则可以使用以下脚本获取所需的输出。

创建功能:

CREATE FUNCTION FIND_CHARINDEX
(@TargetStr   VARCHAR(8000), 
 @SearchedStr VARCHAR(8000), 
 @Occurrence  INT
)
RETURNS INT
AS
     BEGIN
         DECLARE @pos INT, @counter INT, @ret INT;
         SET @pos = CHARINDEX(@TargetStr, @SearchedStr);
         SET @counter = 1;
         IF @Occurrence = 1
             SET @ret = @pos;
             ELSE
             BEGIN
                 WHILE(@counter < @Occurrence)
                     BEGIN
                         SELECT @ret = CHARINDEX(@TargetStr, @SearchedStr, @pos + 1);
                         SET @counter = @counter + 1;
                         SET @pos = @ret;
         END;
         END;
         RETURN(@ret);
     END;

SELECT语句:

DECLARE @S_String VARCHAR(20) = '05'
DECLARE @S_String_New VARCHAR(20) = ';'+@S_String+';'

SELECT 
REVERSE(
    SUBSTRING(
        REVERSE(
            SUBSTRING(
                ';'+Day+';',
                0, 
                (CHARINDEX(@S_String_New,';'+Day+';',0)+LEN(@S_String)) +1
            )
        ),
        0,
        CHARINDEX(
            ';',
            REVERSE(SUBSTRING(';'+Day+';',0, (CHARINDEX(@S_String_New,';'+Day+';',0)+LEN(@S_String)) +1)),
            0
        )
    )
),

REVERSE(
    SUBSTRING(
        REVERSE(
            SUBSTRING(
                ';'+PRICE+';',
                0,    
                (
                    dbo.FIND_CHARINDEX(
                        ';',
                        ';'+PRICE+';',
                        (
                            LEN(SUBSTRING(';'+Day+';',0, (CHARINDEX(@S_String_New,';'+Day+';',0)+LEN(@S_String)) +1)) 
                                - LEN(REPLACE(SUBSTRING(';'+Day+';',0,  (CHARINDEX(@S_String_New,';'+Day+';',0)+LEN(@S_String))  +1),';',''))+1
                        )
                    )
                )
            )
        ),
        0,
        CHARINDEX(
            ';',
            REVERSE(
                SUBSTRING(
                    ';'+PRICE+';',
                    0,    
                    (
                        dbo.FIND_CHARINDEX(
                            ';',
                            ';'+PRICE+';',   
                            (
                                LEN(SUBSTRING(';'+Day+';',0, (CHARINDEX(@S_String_New,';'+Day+';',0)+LEN(@S_String)) +1)) 
                                    - LEN(REPLACE(SUBSTRING(';'+Day+';',0,  (CHARINDEX(@S_String_New,';'+Day+';',0)+LEN(@S_String))  +1),';',''))+1
                            )
                        )
                    )
                )
            ),
            1
        )
    )
)
FROM your_table
WHERE ';'+Day+';' LIKE '%'+@S_String_New+'%'