拆分字符串和除值

时间:2015-02-03 13:20:24

标签: sql tsql sql-server-2012

我找到了一个包含Test列和A列的表BA列在一个条目中包含不同的值,例如abc;def;ghi,全部以;分隔。 B列包含数值,但只包含一个。

我想要的是将A列中的值分成多行。

所以:

abc;def;ghi;jkl

- >

abc
def
ghi
jkl

B中有一个值,例如20,我希望将该值拆分为行数

所以关闭的最终结果是:

abc 5
def 5
ghi 5
jkl 5 

问题是列A中的值必须是可变的。

4 个答案:

答案 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

enter image description here

答案 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进行划分。

enter image description here