【问题标题】:How to create a View showing rows from splitted data如何创建显示拆分数据行的视图
【发布时间】:2016-11-28 19:44:04
【问题描述】:

以表为:

Name    Num     Value
----------------------
Peter   10      10
Mary    10,15   5,10
John    5,20    10,20

如何通过 View 获得如下表的结果?

Name   Num   Value
------------------
Peter  10     10
Mary   10      5
Mary   15     10
John    5     10
John   20     20

注意 Mary 和 John 有多个数据(逗号分隔)。

我有一个函数可以拆分单元格并返回一个表格,但仅针对特定的行和单元格,可以从所有表格中迭代?

附加信息:

SELECT NAM.NAME, Data AS NUM, VAL.VALUE
  FROM dbo.Split((SELECT NUM FROM t1 WHERE LineNum = 2), ','))
CROSS APPLY (
    SELECT Id As Id1, Data AS VALUE
          FROM dbo.Split((SELECT VALUE FROM t1 WHERE LineNum = 2), ','))
    ) AS VAL
CROSS APPLY (
    SELECT NAME FROM t1 WHERE LineNum = 2
    ) AS NAM
WHERE Id = Id1

注意:LineNum 是从 t1 开始的行号

从 t1 的第二行(玛丽 | 10,15 | 5,10)

上一个函数返回表为:

Name   Num   Value
------------------
Mary    10    5
Mary    15   10

从单元格 (10,15) 中拆分函数返回:

Id   Data
-----------
1    10
2    15

================================================

我的拆分函数:

CREATE FUNCTION [dbo].[Split]
(
    @String NVARCHAR(4000),
    @Delimiter NCHAR(1)
)
RETURNS TABLE
AS
RETURN
(
    WITH Split(stpos,endpos)
    AS(
        SELECT 0 AS stpos, CHARINDEX(@Delimiter,@String) AS endpos
        UNION ALL
        SELECT endpos+1, CHARINDEX(@Delimiter,@String,endpos+1)
            FROM Split
            WHERE endpos > 0
    )
    SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)),
        'Data' = SUBSTRING(@String,stpos,COALESCE(NULLIF(endpos,0),LEN(@String)+1)-stpos)
    FROM Split
)

【问题讨论】:

  • 你给我们展示一下你的分割函数怎么样?根据您使用的分离器,这可能是不可能的。当然这也引出了一个问题……你能改变你的系统并停止存储这样的分隔值吗?这确实是一种处理数据的糟糕方式。
  • 您好,我无法更改原始数据(分隔值单元格)
  • 但是你的分割函数在哪里呢?那是可能使这不起作用的部分。您将需要每个元素的序号位置,以便确保每个 num 和值都正确匹配。
  • 我的拆分功能:

标签: sql-server


【解决方案1】:
Declare @YourTable table (Name varchar(50), Num varchar(50), Value  varchar(50))
Insert into @YourTable values
('Peter','10',  '10'),
('Mary','10,15','5,10'),
('John','5,20', '10,20')

Select A.Name
      ,B.*
 From  @YourTable A
 Cross Apply ( 
                Select Num   = A.RetVal
                      ,Value = B.RetVal
                From (
                        Select  RetSeq = Row_Number() over (Order By (Select null))
                               ,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
                        From (Select x = Cast('<x>'+ Replace(A.Num,',','</x><x>')+'</x>' as xml).query('.')) as A 
                        Cross Apply x.nodes('x') AS B(i)
                ) A
                Join (
                        Select  RetSeq = Row_Number() over (Order By (Select null))
                               ,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
                        From (Select x = Cast('<x>'+ Replace(A.Value,',','</x><x>')+'</x>' as xml).query('.')) as A 
                        Cross Apply x.nodes('x') AS B(i)
                ) B on A.RetSeq=B.RetSeq
             ) B

返回

Name    Num Value
Peter   10  10
Mary    10  5
Mary    15  10
John    5   10
John    20  20

编辑 - 另一个使用 UDF 的选项

Select A.Name
      ,B.*
 From  @YourTable A
 Cross Apply ( 
                Select Num   = A.RetVal
                      ,Value = B.RetVal
                From [dbo].[udf-Str-Parse-8K](A.Num,',') A
                Join [dbo].[udf-Str-Parse-8K](A.Value,',') B 
                  on A.RetSeq=B.RetSeq
             ) B

最快的UDF

CREATE FUNCTION [dbo].[udf-Str-Parse-8K] (@String varchar(max),@Delimiter varchar(10))
Returns Table 
As
Return (  
    with   cte1(N)   As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
           cte2(N)   As (Select Top (IsNull(DataLength(@String),0)) Row_Number() over (Order By (Select NULL)) From (Select N=1 From cte1 a,cte1 b,cte1 c,cte1 d) A ),
           cte3(N)   As (Select 1 Union All Select t.N+DataLength(@Delimiter) From cte2 t Where Substring(@String,t.N,DataLength(@Delimiter)) = @Delimiter),
           cte4(N,L) As (Select S.N,IsNull(NullIf(CharIndex(@Delimiter,@String,s.N),0)-S.N,8000) From cte3 S)

    Select RetSeq = Row_Number() over (Order By A.N)
          ,RetVal = Substring(@String, A.N, A.L) 
    From   cte4 A
);
--Much faster than str-Parse, but limited to 8K
--Select * from [dbo].[udf-Str-Parse-8K]('Dog,Cat,House,Car',',')

【讨论】:

  • 谢谢,似乎可以完成这项工作。现在我必须研究它和我不知道的所有 xml 使用。
  • @P.A 如果有兴趣,有一个更快的解析器功能。只是不清楚你是否想要一个 UDF
  • 肖恩,这是我的分割函数:
  • 感谢 John,带有 UDF 的那个对我来说既完美又简单。
  • @P.A 乐于助人
猜你喜欢
  • 1970-01-01
  • 2019-10-18
  • 2013-08-08
  • 2020-08-11
  • 1970-01-01
  • 2020-08-18
  • 1970-01-01
  • 2012-03-23
  • 1970-01-01
相关资源
最近更新 更多