【问题标题】:Split string and divide value拆分字符串并划分值
【发布时间】:2015-02-03 13:20:24
【问题描述】:

我有一张表Test,列有ABA 列在一个条目中包含不同的值,例如abc;def;ghi,均以; 分隔。 B 列包含数值,但只有一个。

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

所以:

abc;def;ghi;jkl

-->

abc
def
ghi
jkl

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

所以最后的结果是:

abc 5
def 5
ghi 5
jkl 5 

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

【问题讨论】:

  • 首先,也是最重要的一点:各个值应该分开存储。改变你的桌子设计 - 这是一个 1:M 的关系。
  • 基本上你的最终结果应该首先是数据的存储方式。
  • @ChristopherStainczyk - 如果您有信息要添加,请编辑您的问题以添加它(正如其他人已经完成的那样)。您无需在此处对自己的问题或答案做出直接评论。

标签: sql tsql sql-server-2012


【解决方案1】:

首先你需要创建这个函数

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 => x.origID = Test.ID

【讨论】:

    【解决方案2】:

    您可以在 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
    

    【讨论】:

      【解决方案3】:

      这是一个如何实现所需结果的示例

      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
      

      【讨论】:

        【解决方案4】:

        这是完整的工作示例:

         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 该部门。

        【讨论】:

          猜你喜欢
          • 2020-09-13
          • 2020-12-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-21
          • 2021-05-06
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多