SQL Server:将XML数据转换为表

时间:2016-02-03 18:26:11

标签: sql-server xml

我需要帮助来微调我编写的代码。我是SQL Server的新手,我相信有更好的方法可以做到这一点,或者可能会简化或微调下面的一些代码以提高性能或节省内存资源。

基本上,我有这个XML数据:

<table_result id="001" subj_cd="cdaaa" grade="b" name="Phua Chu Kang"/>

enter image description here

我想从这个XML数据

创建一个看起来像这样的表

enter image description here

请注意以下几点:

  • SplitThis不是内置函数(请查看下面的代码)。

数据可以有空格,但由"分隔。请注意,XML数据可以具有不同数量的字段 - 特定给定表的数据对 - 在以下代码中称为#dummy。即,上面的示例XML数据具有4个字段(id,subj_cd,grade,name),并且下一个XML数据可以具有5个字段(即id,name,occup,phone_no,address)。在以下代码中,创建#table_result以匹配示例XML数据以便于演示。换句话说,表结构是已知的。因此我可以忽略XML数据中的字段名称,并专注于提取数据本身。

代码在SQL Server 2012上运行良好(您可以直接复制并粘贴运行代码),我可以得到如上所述。如果可能的话,我只需要对此进行微调。我有这样的行: - - 测试blabla。您可以取消注释并尝试。我可以使用增强功能,例如避免使用临时表的数量或在代码中替换row_number()的使用的任何方法。

/* remove all temp tables */
declare @sql varchar(5000)
SELECT @sql = isnull(@sql+';', '') + 'drop table ' + SUBSTRING(t.name, 1, CHARINDEX('___', t.name)-1)
FROM tempdb..sysobjects AS t
WHERE t.name LIKE '#%[_][_][_]%'
AND t.id =OBJECT_ID('tempdb..' + SUBSTRING(t.name, 1, CHARINDEX('___', t.name)-1));
exec (@sql)
/* end */

/* function */
drop function splitthis
go
create function splitthis(@separator char(1), @list varchar(max))
     returns @returntable table(item nvarchar(max))
as
begin
    declare @index int
    declare @newtext varchar(max) 
    if @list = null
        return
    set @index = charindex(@separator, @list)
    while not(@index = 0)
    begin
        set @newtext = rtrim(ltrim(left(@list, @index - 1)))
        set @list = right(@list, len(@list) - @index)
        insert into @returntable(item) values(@newtext)
        set @index = charindex(@separator, @list)
    end
    insert into @returntable(item) values(rtrim(ltrim(@list)))
    update @returntable set item='' where item is null 
    return
end
go
/* end of function */

/* create dummy tables */
create table #table_result 
(id nvarchar(max), subj_cd nvarchar(max), grade nvarchar(max), name nvarchar(max))

create table #dummy (name nvarchar(max), data nvarchar(max))
insert into #dummy 
values ('a', '<table_result id="001" subj_cd="cdaaa" grade="b" name="phua chu kang"/>');
--test : select * from #dummy

/* remove the fist non-data opening tag */
declare @record nvarchar(max)
select @record = data from #dummy where name = 'a'
select *, null as temp into #tempb from splitthis(' ',@record)
select *, row_number() over (order by temp) count into #tempc from #tempb
select item into #tempd from #tempc where #tempc.count>1
-- test : select * from #tempd

/* get the actual field & data into a single column table */
declare @temp varchar(max)
set @temp=''select @temp=@temp+' ' + item from #tempd 
select *, null as temp into #tempe from splitthis('"',@temp)
select *, row_number() over (order by temp) count into #tempf from #tempe
select item, count into #tempg from #tempf
--test : select * from #tempg

/* prepare the data table */
select 
    case when #tempg.count % 2 = 0 
        then item
        else null
    end as data
into #temph
from #tempg 
select data, null as temp into #tempi from #temph
select data, row_number() over (order by temp) count into #data from #tempi
    where data is not null
--test : select * from #data

/* prepare the field table. */
select name, null as temp into #tempj 
from tempdb.sys.columns where object_id=object_id('tempdb..#table_result');
select *, row_number() over (order by temp) count into #field from #tempj
--test : select * from #field

/* get the final table */
select a.name as field, b.data from #field a
left join #data b on a.count=b.count

2 个答案:

答案 0 :(得分:1)

这是 - 使用XML方法 - 更容易!

试试这个:

DECLARE @xml XML='<table_result id="001" subj_cd="cdaaa" grade="b" name="Phua Chu Kang"/>';

SELECT One.Attr.value('fn:local-name(.)','varchar(max)') AS field
      ,One.Attr.value('.','varchar(max)') AS data
FROM @xml.nodes('table_result/@*') AS One(Attr)

结果

field     data
id        001
subj_cd   cdaaa
grade     b
name      Phua Chu Kang

现在我尝试模仿你的表结构(我建议从一开始就将数据存储为XML!在这种情况下,你可以省略第一个CROSS APPLYCAST ... AS XML):

DECLARE @tbl TABLE(name VARCHAR(10),data VARCHAR(MAX));
INSERT INTO @tbl VALUES
 ('a','<table_result id="001" subj_cd="cdaaa" grade="b" name="Phua Chu Kang"/>') 
,('b','<Another test="test data" test2="test2 data"/>') 
,('c','<OneMore x="x data" y="y data" z="z data"/>'); 

SELECT tbl.name
      ,One.Attr.value('fn:local-name(..)','varchar(max)') AS element
      ,One.Attr.value('fn:local-name(.)','varchar(max)') AS field
      ,One.Attr.value('.','varchar(max)') AS data
FROM @tbl AS tbl
CROSS APPLY(SELECT CAST(tbl.data AS XML)) AS MyData(AsXml)
CROSS APPLY MyData.AsXml.nodes('*/@*') AS One(Attr)

结果

name  element      field    data
a     table_result  id      001
a     table_result  subj_cd cdaaa
a     table_result  grade   b
a     table_result  name    Phua Chu Kang
b     Another       test    test data
b     Another       test2   test2 data
c     OneMore       x       x data
c     OneMore       y       y data
c     OneMore       z       z data

答案 1 :(得分:0)

现在,我对T-SQL XML并不是很了解,但你不能这样做:

app:layout_scrollFlags="scroll|enterAlways"

请注意,我将create table #dummy (name nvarchar(max), data xml); insert into #dummy values ('a', '<table_result id="001" subj_cd="cdaaa" grade="b" name="phua chu kang"/>'); select 'id' "field", elem.value('@id', 'nvarchar(50)') "data" from #dummy cross apply data.nodes('/table_result') tbl(elem) union all select 'subj_cd' "field", elem.value('@subj_cd', 'nvarchar(50)') "data" from #dummy cross apply data.nodes('/table_result') tbl(elem) union all select 'grade' "field", elem.value('@grade', 'nvarchar(50)') "data" from #dummy cross apply data.nodes('/table_result') tbl(elem) union all select 'name' "field", elem.value('@name', 'nvarchar(50)') "data" from #dummy cross apply data.nodes('/table_result') tbl(elem); 的数据类型更改为#dummy.data。这需要能够使用XML函数。