Domanda

I need to pull visit data from a SQL Server 2005 XML column and dymanically display it in an ASP.NET datagrid. I would like to write a stored procedure to handle the transformation. The XML structure is very simple (only one level), but the field names are different depending on the visit type. I will only be displaying one visit type in a datagrid at a time, but I need to select multiple visits (rows) per datagrid.

Visits Table:

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

Sample Data:

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

Sample VisitXML Column:

Record 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>

Record 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>

Record 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>

Record 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>

I want to be able to dynamically query a certain VisitType and display that data in a grid, without referencing XML field names. For example, my datagrid for Type1 would use auto-generated columns and look like this:

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

Is this possible? I need help with the stored proc.

È stato utile?

Soluzione

I was able to find a solution using dynamic SQL by reading another post: Query XML creating field names whithout knowing node names

I ended up using a cursor as well. I've never had to do that, ever, but it seemed practical here.

Here's my stored proc, if anyone is interested.

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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top