我找到了一个包含Test
列和A
列的表B
。
A
列在一个条目中包含不同的值,例如abc;def;ghi
,全部以;
分隔。 B
列包含数值,但只包含一个。
我想要的是将A列中的值分成多行。
所以:
abc;def;ghi;jkl
- >
abc
def
ghi
jkl
列B
中有一个值,例如20,我希望将该值拆分为行数
所以关闭的最终结果是:
abc 5
def 5
ghi 5
jkl 5
问题是列A
中的值必须是可变的。
答案 0 :(得分:0)
首先你需要创建这个功能
REATE FUNCTION Split
(
@delimited nvarchar(max),
@delimiter nvarchar(100)
) RETURNS @t TABLE
(
-- Id column can be commented out, not required for sql splitting string
id int identity(1,1), -- I use this column for numbering splitted parts
val nvarchar(max),
origVal nvarchar(max)
)
AS
BEGIN
declare @xml xml
set @xml = N'<root><r>' + replace(@delimited,@delimiter,'</r><r>') + '</r></root>'
insert into @t(val,origval)
select
r.value('.','varchar(max)') as item, @delimited
from @xml.nodes('//root/r') as records(r)
RETURN
END
GO
然后这个查询可能有帮助
Select x.Val, test.B / (len(test.A) - len(replace(Test.A, ';', '')) + 1) from Test
inner join dbo.Split(Test.A,';') x on x.origVal = Test.A
此部分len(test.A) - len(replace(Test.A, ';', ''))
将计算字符串
;
的数量
如果A
列中存在重复的字符串,请注意此查询可能会出现问题,在这种情况下,您需要将唯一值(例如ID
)传递给split
函数并将其返回到结果表中,然后通过此值加入它(即x.origVal = Test.A
=&gt; x.origID = Test.ID
)
答案 1 :(得分:0)
您可以使用CTE,STUFF和Windows函数的一些技巧
DECLARE @t TABLE
(
ID INT ,
A NVARCHAR(MAX) ,
B INT
)
INSERT INTO @t
VALUES ( 1, 'a;b;c;d;', 20 ),
( 2, 'x;y;z;', 40 );
WITH cte ( ID, B, D, A )
AS ( SELECT ID ,
B ,
LEFT(A, CHARINDEX(';', A + ';') - 1) ,
STUFF(A, 1, CHARINDEX(';', A + ';'), '')
FROM @t
UNION ALL
SELECT ID ,
B ,
LEFT(A, CHARINDEX(';', A + ';') - 1) ,
STUFF(A, 1, CHARINDEX(';', A + ';'), '')
FROM cte
WHERE A > ''
)
SELECT ID ,
B ,
D,
CAST(B AS DECIMAL) / COUNT(*) OVER (PARTITION BY ID) AS Portion
FROM cte
输出:
ID B D Portion
1 20 a 5.00000000000
1 20 b 5.00000000000
1 20 c 5.00000000000
1 20 d 5.00000000000
2 40 x 13.33333333333
2 40 y 13.33333333333
2 40 z 13.33333333333
答案 2 :(得分:0)
这是一个如何实现所需结果的例子
DECLARE @table AS TABLE
(
ColumnA VARCHAR(100) ,
ColumnB FLOAT
)
INSERT INTO @table
( ColumnA, ColumnB )
VALUES ( 'abc;def;ghi;jkl', 20 ),
( 'asf;ret;gsd;jas', 30 ),
( 'dfa;aef;gffhi;fjfkl', 40 );
WITH C AS ( SELECT n = 1
UNION ALL
SELECT n + 1
FROM C
WHERE n <= 100
),
SetForSplit
AS ( SELECT T.ColumnA ,
T.ColumnB ,
C.n ,
( CASE WHEN LEFT(SUBSTRING(T.ColumnA, n, 100), 1) = ';'
THEN SUBSTRING(T.ColumnA, n + 1, 100) + ';'
ELSE SUBSTRING(T.ColumnA, n, 100) + ';'
END ) AS SomeText
FROM @table AS T
JOIN C ON C.n <= LEN(T.ColumnA)
WHERE SUBSTRING(T.ColumnA, n, 1) = ';'
OR n = 1
)
SELECT ROW_NUMBER() OVER ( PARTITION BY columnA ORDER BY LEFT(SomeText,
CHARINDEX(';',
SomeText) - 1) ) AS RowN,
LEFT(SomeText, CHARINDEX(';', SomeText) - 1) AS ColA ,
ColumnB / COUNT(*) OVER ( PARTITION BY ColumnA ) AS ColB
FROM SetForSplit
ORDER BY ColumnA
答案 3 :(得分:0)
这是完整的工作示例:
DECLARE @DataSource TABLE
(
[A] VARCHAR(MAX)
,[B] INT
);
INSERT INTO @DataSource ([A], [B])
VALUES ('a;b;c;d', 20 ),
('x;y;z', 40 );
SELECT T.c.value('.', 'VARCHAR(100)')
,[B] / COUNT([B]) OVER (PARTITION BY [B])
FROM @DataSource
CROSS APPLY
(
SELECT CONVERT(XML, '<t>' + REPLACE([A], ';', '</t><t>') + '</t>')
) DS([Bxml])
CROSS APPLY [Bxml].nodes('/t') AS T(c)
并且您可以根据自己的喜好ROUND
进行划分。