将SQL字段解析为多行

时间:2011-09-22 04:00:38

标签: sql sql-server tsql

如何获取如下所示的SQL表:

MemberNumber    JoinDate    Associate
1234            1/1/2011    A1 free A2 upgrade A31
5678            3/15/2011   A4
9012            5/10/2011   free

输出(使用视图或写入另一个表或其他最简单的表):

MemberNumber    Date
1234-P         1/1/2011
1234-A1        1/1/2011
1234-A2        1/1/2011
1234-A31       1/1/2011
5678-P         3/15/2011
5678-A4        3/15/2011
9012-P         5/10/2011

每行产生“-P”(主)输出线以及任何A#(关联)线。 Associate字段可以包含许多不同的非“A#”值,但“A#”是我感兴趣的(#从1到99)。在那个领域也可以有很多“A#”。

3 个答案:

答案 0 :(得分:3)

当然,表格重新设计会大大简化此查询,但有时我们只需要完成它。我使用多个CTE编写了以下查询;我发现它更容易理解,看看到底发生了什么,但是一旦你掌握了这项技术,你就可以进一步简化这一过程。

要注入“P”主行,您会看到我只是将其卡入了Associate列,但最好放在CTE外部的简单UNION中。

此外,如果您确实选择重构架构,则可以使用以下技术将您的Associate列“拆分”为行。

;with 
Split (MemberNumber, JoinDate, AssociateItem)
as  (       select MemberNumber, JoinDate, p.n.value('(./text())[1]','varchar(25)')
            from    (   select MemberNumber, JoinDate, n=cast('<n>'+replace(Associate + ' P',' ','</n><n>')+'</n>' as xml).query('.')
                        from @t
                    ) a
            cross apply n.nodes('n') p(n)
    )
select  MemberNumber + '-' + AssociateItem,
        JoinDate
from    Split
where   left(AssociateItem, 1) in ('A','P')
order
by      MemberNumber;

XML方法在性能方面不是一个很好的选择,因为随着“数组”中项目数量的增加,它的速度会降低。如果您有长阵列,则以下方法可能对您有用:

--* should be physical table, but use this cte if needed
--;with 
--number (n) 
--as    (   select top(50) row_number() over(order by number) as n
--      from    master..spt_values
--  )
    select  MemberNumber + '-' + substring(Associate, n, isnull(nullif(charindex(' ', Associate + ' P', n)-1, -1), len(Associate)) - n+1),
            JoinDate
    from    (   select MemberNumber, JoinDate, Associate + ' P' from @t
            ) t (MemberNumber, JoinDate, Associate)
    cross
    apply   number n
    where   n <= convert(int, len(Associate)) and
            substring(' ' + Associate, n, 1) = ' ' and
            left(substring(Associate, n, isnull(nullif(charindex(' ', Associate, n)-1, -1), len(Associate)) - n+1), 1) in ('A', 'P');

答案 1 :(得分:1)

试试这个新版本

declare @t table (MemberNumber varchar(8), JoinDate date, Associate varchar(50))  

insert into @t values  ('1234', '1/1/2011', 'A1 free A2 upgrade A31'),('5678', '3/15/2011', 'A4'),('9012', '5/10/2011', 'free')  

;with b(f, t, membernumber, joindate, associate)
as 
( 
select 1, 0, membernumber, joindate, Associate
from @t 
union all 
select t+1, charindex(' ',Associate + ' ', t+1), membernumber, joindate, Associate
from b 
where t <  len(Associate)
)
select MemberNumber + case when t = 0 then '-P' else '-'+substring(Associate, f,t-f) end NewMemberNumber, JoinDate
from b  
where t = 0 or substring(Associate, f,1) = 'A' 
--where t = 0 or substring(Associate, f,2) like 'A[1-9]' 
-- order by MemberNumber, t

结果与请求的输出相同。

答案 2 :(得分:0)

我建议您通过添加链接表而不是“关联”列来更改数据库结构。链接表将包含两个或更多列,如下所示:

MemberNumber    Associate   Details
-----------------------------------
1234            A1          free
1234            A2          upgrade
1234            A31         
5678            A4          

然后可以通过简单的JOIN获得所需的结果:

SELECT CONCAT(m.`MemberNumber`, '-', 'P'), m.`JoinDate`
FROM `members` m
UNION
SELECT CONCAT(m.`MemberNumber`, '-', IFNULL(a.`Associate`, 'P')), m.`JoinDate`
FROM `members` m
RIGHT JOIN `members_associates` a ON m.`MemberNumber` = a.`MemberNumber`