如何在 SQL 服务器中按键查询多值列
HOW TO query multi-valued column by key in SQL SERVER
如何查询 multivalued
列的特定键?
示例数据
ID DAY PRICE
1 01;02;03;04;... 100;230;110;34.5;...
2 01;02;03;04;... 120;240;510;34.5;...
例如:
select ... 其中 DAY key = '02'
预计:
ID DAY PRICE
1 02 230
2 02 240
备注
实际table有30多个字段。
加入多个 CROSS APPLY SPLIT_STRING
看起来是一个乏味的解决方案
这里有一个选项可以动态反转数据(无需实际使用动态 SQL),然后旋转结果。
您只需列出 for Item in (...)
部分中的 30 列
交叉应用 B 会将 ROW 转换为 XML
交叉应用 C 将 UNPIVOT XML
交叉应用 D 将 parse/split 来自 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'
Returns
ID RetSeq Day Price
1 2 02 230
2 2 02 240
您可以在 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
如果您可以在数据库中创建额外的 FUNCTION,则可以使用以下脚本来获得所需的输出。
创建函数:
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+'%'
此解决方案使用 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"
如何查询 multivalued
列的特定键?
示例数据
ID DAY PRICE
1 01;02;03;04;... 100;230;110;34.5;...
2 01;02;03;04;... 120;240;510;34.5;...
例如:
select ... 其中 DAY key = '02'
预计:
ID DAY PRICE
1 02 230
2 02 240
备注
实际table有30多个字段。
加入多个 CROSS APPLY SPLIT_STRING
看起来是一个乏味的解决方案
这里有一个选项可以动态反转数据(无需实际使用动态 SQL),然后旋转结果。
您只需列出 for Item in (...)
部分中的 30 列
交叉应用 B 会将 ROW 转换为 XML
交叉应用 C 将 UNPIVOT XML
交叉应用 D 将 parse/split 来自 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'
Returns
ID RetSeq Day Price
1 2 02 230
2 2 02 240
您可以在 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
如果您可以在数据库中创建额外的 FUNCTION,则可以使用以下脚本来获得所需的输出。
创建函数:
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+'%'
此解决方案使用 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"