将XML列转换为表格数据

时间:2014-03-21 15:07:07

标签: sql xml vb.net sql-server-2005 datagrid

我需要从SQL Server 2005 XML列中提取访问数据,并在ASP.NET数据网格中以动态方式显示它。我想编写一个存储过程来处理转换。 XML结构非常简单(只有一个级别),但字段名称因访问类型而异。我一次只会在数据网格中显示一种访问类型,但我需要为每个数据网格选择多次访问(行)。

访问表:

CREATE TABLE Visits
(
  VisitID UNIQUIDENTIFIER,
  VisitType VARCHAR(10),
  VisitXML XML
)

示例数据:

VisitID VisitType VisitXML
------- --------- -----------
1       Type1     (see below)
2       Type1     (see below)
3       Type2     (see below)
4       Type3     (see below)

示例VisitXML列:

记录1:

<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<visit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <Patient Number>100</Patient Number>
   <Blood Pressure>120/84</Blood Pressure>
   <Cholesterol>100</cholesterol>
</visit>

记录2:

<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <visit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Patient Number>200</Patient Number>
    <Blood Pressure>140/70</Blood Pressure>
    <Cholesterol>204</cholesterol>
 </visit>

记录3:

<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<visit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <Patient Number>100</Patient Number>
   <Height>71 inches</Height>
</visit>

记录4:

<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <visit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Patient Number>100</Patient Number>
    <Sex>F</Sex>
    <Weight>133.5 lbs</Height>
    <BirthDate>6/19/1946</BirthDate>
    <SmokingStatus>Non-Smoker</SmokingStatus>
 </visit>

我希望能够动态查询某个VisitType并在网格中显示该数据,而无需引用XML字段名称。例如,我的Type1数据网格将使用自动生成的列,如下所示:

VisitID VisitType Patient Number Blood Pressure Cholesterol
------- --------- -------------- -------------- -----------
1       Type1     100            120/84         100
2       Type1     200            140/70         204

这可能吗?我需要有关存储过程的帮助。

1 个答案:

答案 0 :(得分:0)

我能够通过阅读另一篇文章Query XML creating field names whithout knowing node names

找到使用动态SQL的解决方案

我最终也使用了光标。我从来没有这样做过,但这似乎很实用。

如果有人有兴趣,请查看我的存储过程。

CREATE PROC [dbo].[myproc]
@VisitType UNIQUEIDENTIFIER

AS

/*************************************************************
THIS PROCEDURE RETURNS ALL OF THE XML FIELDS FOR EACH VISIT 
IN A PARTICULAR VISIT SET.  

EXAMPLE:
SourceID  FacilityID AccountNumber LanguageResp Ethnicity
--------- ---------- ------------- ------------ ---------
AGH       AGH        V0000001      ENG          NON
AGH       AGH        V0000099      ENG          NON


**************************************************************/


/***********************************************
A GLOBAL TEMP TABLE STORES THE XML DATA AS WE
PULL IT OUT OF THE XML COLUMN, RECORD BY RECORD
(VISIT BY VISIT)
***********************************************/
BEGIN
IF (SELECT object_id('tempdb..##TempAudit')) IS NOT NULL
BEGIN
    DROP TABLE ##TempAudit
END



/***********************************************
@FooTable WILL CONTAIN THE IDs and XML 
COLUMN FOR THE RECORDS THAT WE WANT TO WORK
WITH.

SINCE ONE VISIT CAN HAVE MULTIPLE RECORDS
IN THIS TABLE WE CAN'T USE VISIT ID FOR OUR ID; WE HAVE TO USE
RECORD ID
***********************************************/
declare @FooTable table
(
  ID_FIELD INT,
  XML_FIELD xml
)

/***********************************************
WE NEED TO FIGURE OUT THE XML STRUCTURE (FIELD
NAMES) FOR THE VISIT TYPE WE ARE WORKING WITH.
EVERY XML FIELD WILL HAVE THE SAME STRUCTURE FOR
A GIVEN VISIT TYPE, SO WE JUST NEED TO LOOK AT THE
FIRST VISIT FOR THAT VISIT TYPE.
************************************************/
declare @FirstRecordID INT --@FirstVisitID VARCHAR(30)
SET @FirstRecordID = (SELECT TOP 1 RecordID FROM VisitTable WHERE VisitType = @VisitType)


/***********************************************
POPULATE @FooTable
***********************************************/
insert into @FooTable
(
ID_FIELD
,XML_FIELD
)

SELECT
    RecordID
    ,VisitDataXML
FROM
    dbo.VisitTable
WHERE
    VisitType = @VisitType
    and RecordID = @FirstRecordID --and VisitID = @FirstVisitID





declare @KnownName varchar(100) 
SET @KnownName = 'visit' 


/***********************************************
FIND THAT FIRST XLM VALUE/DOCUMENT
***********************************************/
declare @ID INT --varchar(30)
SET @ID = @FirstRecordID -- @FirstVisitID
-- Variable to hold the XML to process
declare @XML xml
select @XML = XML_FIELD
from @FooTable
where ID_FIELD = @ID


/***********************************************
WE NEED TO USE DYNAMIC SQL IN ORDER TO CONSTRUCT
OUR SELECT STATEMENT BECAUSE AT DESIGN TIME
WE DO NOT KNOW THE NAMES OF THE XLM ATTRIBUTES;
THEY ARE DIFFERENT FOR EACH VISIT TYPE
************************************************/
-- Variable for dynamic SQL
declare @SQL nvarchar(max)

-- Build the query
select @SQL = 'select '+stuff(
  (
  select ',T.N.value('''+T.N.value('local-name(.)', 'sysname')+'[1]'', ''varchar(max)'') as '+T.N.value('local-name(.)', 'sysname')
  from @XML.nodes('/root/*[local-name(.)=sql:variable("@KnownName")]/*') as T(N)
  for xml path(''), type
  ).value('.', 'nvarchar(max)'), 1, 1, '')+
  ' from @XML.nodes(''/root/*[local-name(.)=sql:variable("@KnownName")]'') as T(N)'



/***********************************************
WE ALSO NEED TO USE DYNAMIC SQL TO BUILD THE
THE TEMP TABLE FOR STORING THE RESULTS OF
WHEN WE RUN THE DYNAMIC QUERY
************************************************/
--Build the temp table:
declare @SQL2 nvarchar(max)
select @SQL2 = 
'CREATE TABLE ##TempAudit (' + stuff(
(select ' ' + T.N.value('local-name(.)', 'sysname') + ' VARCHAR(MAX),'
  from @XML.nodes('/root/*[local-name(.)=sql:variable("@KnownName")]/*') as T(N)
  for xml path(''), type
  ).value('.', 'nvarchar(max)'), 1, 1, '')

--TRIM THE LAST COMMA OFF THE END OF THE TABLE STATEMENT:
SELECT @SQL2 = LEFT(@SQL2,LEN(@SQL2)-1)
--ADD A CLOSING ')' TO THE TABLE STATEMENT
SELECT @SQL2 = @SQL2 + ')'


/**************************************************************
CREATE THE TEMP TABLE BY EXECUTING THE DYMANIC SQL STATEMENT
**************************************************************/
exec sp_executesql @SQL2

--DECLARE @VisitID VARCHAR(30) 
DECLARE @RecordID INT

/**************************************************************
USE A CURSOR TO EXECUTE THE XML RETRIEVAL FOR EACH RECORD
IN THE AUDIT
**************************************************************/
DECLARE the_cursor CURSOR FAST_FORWARD
FOR SELECT RecordID --VisitID  
    FROM dbo.VisitTable 
    WHERE VisitType = @VisitType

OPEN the_cursor
FETCH NEXT FROM the_cursor INTO @RecordID --@VisitID

WHILE @@FETCH_STATUS = 0
BEGIN

SELECT @XML = VisitDataXML FROM dbo.VisitTable WHERE VistType = @VisitType and RecordID = @RecordID --VisitID = @VisitID

/**************************************************************
RUN OUR DYNAMIC SQL TO PARSE THE XML FIELD 
**************************************************************/
INSERT ##TempAudit
exec sp_executesql @SQL, 
     N'@XML xml, @KnownName varchar(100)', 
     @XML = @XML, 
     @KnownName = @KnownName



    FETCH NEXT FROM the_cursor INTO @RecordID --@VisitID
END

CLOSE the_cursor
DEALLOCATE the_cursor


/**************************************************************
PERFORM THE FINAL SELECT
*************************************************************/
SELECT * FROM ##TempAudit



END