7

私は SQL Server (2008/2012) を使用しており、多くの検索から同様の回答があることはわかっていますが、私のケースに適した例/ポインターが見つからないようです。

このデータを保持する SQL Server テーブルに XML 列があります。

<Items>
 <Item>
  <FormItem>
    <Text>FirstName</Text>
    <Value>My First Name</Value>
  </FormItem>
  <FormItem>
    <Text>LastName</Text>
    <Value>My Last Name</Value>
  </FormItem>
  <FormItem>
    <Text>Age</Text>
    <Value>39</Value>
  </FormItem>
 </Item>
 <Item>
  <FormItem>
    <Text>FirstName</Text>
    <Value>My First Name 2</Value>
  </FormItem>
  <FormItem>
    <Text>LastName</Text>
    <Value>My Last Name 2</Value>
  </FormItem>
  <FormItem>
    <Text>Age</Text>
    <Value>40</Value>
  </FormItem>
 </Item>
</Items>

したがって、の構造は<FormItem>同じになりますが、複数の (通常は 20 ~ 30 を超えない) フォーム アイテムのセットを持つことができます。

私は基本的に、以下の形式で SQL からクエリを返そうとしています。つまり、/FormItem/Text に基づく動的列です。

FirstName         LastName         Age    ---> More columns as new `<FormItem>` are returned
My First Name     My Last Name     39          Whatever value etc..
My First Name 2   My Last Name 2   40          

そのため、現時点では次のことがありました。

select 
    Tab.Col.value('Text[1]','nvarchar(100)') as Question,
    Tab.Col.value('Value[1]','nvarchar(100)') as Answer
from
    @Questions.nodes('/Items/Item/FormItem') Tab(Col)

もちろん、それは私のXML行を列に転置しておらず、明らかにフィールドで固定されています..私は、SQLが(私の場合)<Text>ノードの個別の選択を実行するさまざまな「動的SQL」アプローチを試してきました。ある種のピボットを使用しますか? しかし、必要な結果を各行の列の動的セットとして返すための魔法の組み合わせを見つけることができなかったようです(<Item>のコレクション内<Items>)。

非常に多くの非常によく似た例を見たので、それができると確信していますが、解決策は私にはわかりません!

どんな助けでもありがたく受け取った!!

4

3 に答える 3

7

XML の解析はかなりコストがかかるため、1 回解析して動的クエリを作成し、1 回解析してデータを取得する代わりに、名前と値のリストを含む一時テーブルを作成し、それを動的ピボット クエリのソースとして使用できます。
dense_rankピボットする ID を作成するためにあります。
動的クエリで列リストを作成するには、for xml path('')トリックを使用します。

このソリューションでは、テーブルに主キー (ID) が必要です。変数に XML がある場合は、多少単純化できます。

select dense_rank() over(order by ID, I.N) as ID,
       F.N.value('(Text/text())[1]', 'varchar(max)') as Name,
       F.N.value('(Value/text())[1]', 'varchar(max)') as Value
into #T
from YourTable as T
  cross apply T.XMLCol.nodes('/Items/Item') as I(N)
  cross apply I.N.nodes('FormItem') as F(N)

declare @SQL nvarchar(max)
declare @Col nvarchar(max)

select @Col = 
  (
  select distinct ','+quotename(Name)
  from #T
  for xml path(''), type
  ).value('substring(text()[1], 2)', 'nvarchar(max)')

set @SQL = 'select '+@Col+'
            from #T
            pivot (max(Value) for Name in ('+@Col+')) as P'

exec (@SQL)

drop table #T

SQL フィドル

于 2013-03-01T17:52:09.573 に答える
3
select Tab.Col.value('(FormItem[Text = "FirstName"]/Value)[1]', 'varchar(32)') as FirstName, 
        Tab.Col.value('(FormItem[Text = "LastName"]/Value)[1]', 'varchar(32)') as LastName, 
        Tab.Col.value('(FormItem[Text = "Age"]/Value)[1]', 'int') as Age
from @Questions.nodes('/Items/Item') Tab(Col)
于 2013-03-01T17:10:11.703 に答える
3

他の人を助ける可能性があるため、完全性のために「自分の答え」を追加したかったのですが、上記の@Mikaelからの大きな助けに基づいていることは間違いありません!! 繰り返しますが、これは完全性のみを目的としています-@Mikaelへのすべての称賛。

基本的に、私は次の手順で終了しました。いくつかのデータ/フィルターを選択し、結合されたデータも取得して、いくつかの入力パラメーターでブール値フィルターを許可する必要がありました。次に、相互適用を介してリレーショナル データと必要な xml ノードの一時テーブルを作成する次のセクションに進みます。最後のステップは、結果をピボットするか、選択した XML ノードから列を動的に作成することでした。

CREATE PROCEDURE [dbo].[usp_RPT_ExtractFlattenentries]
    @CompanyID          int,
    @MainSelector       nvarchar(50) = null,
    @SecondarySelector      nvarchar(255) = null,
    @DateFrom           datetime = '01-jan-2012',
    @DateTo             datetime = '31-dec-2100',
    @SysReference       nvarchar(20) = null
AS
BEGIN
    SET NOCOUNT ON;

    --  Create the table var to hold the XML form data from the entries
    declare @FeedbackXml table (
        ID int identity primary key,
        XMLCol xml,
        CompanyName nvarchar(20),
        SysReference nvarchar(20),
        RecordDate datetime,
        EntryName  nvarchar(255),
        MainSelector nvarchar(50)
    )

    --  STEP 1: Get the raw submission data based on the params passed in
    --  *Note: The double casting is necessary as the "form" field is nvarchar (not varchar) and we need xml in UTF-8 format
    begin
        insert into @FeedbackXml
            (XMLCol, CompanyName, SysReference, RecordDate, EntryName, MainSelector)
        select cast(cast(e.form as nvarchar(max)) as xml), c.name, e.SysReference, e.RecordDate, e.name, e.wizard
        from 
            entries s
        left join
            companies o on e.companies = c.ID
        where 
            (@CompanyID = -1 or @CompanyID = e.companies)
        and
            (@MainSelector is null or @MainSelector = e.wizard)
        and
            (@SecondarySelector is null or @SecondarySelector = e.name)
        and
            (@SysReference is null or @SysReference = e.SysReference)
        and
            (e.RecordDate >= @DateFrom and e.RecordDate <= @DateTo)
    end

    --  STEP 2: Flatten the required XML structure to provide a base for the pivot, and include other fields we wish to output
    select dense_rank() over(order by ID) as ID,
            T.RecordDate, T.CompanyName, T.SysReference, T.EntryName, T.MainSelector,
            F.N.value('(FieldNameNode/text())[1]', 'nvarchar(max)') as FieldName,
            F.N.value('(FieldNameValue/text())[1]', 'nvarchar(max)') as FieldValue
    into #TempData
    from @FeedbackXml as T
        cross apply T.XMLCol.nodes('/root/companies/') as I(N) -- Xpath to the desired node start point
        cross apply I.N.nodes('company') as F(N) -- The actual node collection that forms the "field name" and "field value" data

    --  STEP 3: Pivot the #TempData table creating a dynamic column structure based on the selected XML nodes in step 2
    declare @SQL nvarchar(max)
    declare @Col nvarchar(max)

    select @Col = 
      (
      select distinct ','+quotename(FieldName)
      from #TempData
      for xml path(''), type
      ).value('substring(text()[1], 2)', 'nvarchar(max)')

    set @SQL = 'select CompanyName, SysReference, EntryName, MainSelector, RecordDate, '+@Col+'
                from #TempData
                pivot (max(FieldValue) for FieldName in ('+@Col+')) as P'

    exec (@SQL)
    drop table #TempData

END

繰り返しますが、実際にはこの回答を追加して、私の視点からの全体像を提供するだけであり、他の人を助けるかもしれません.

于 2013-03-07T11:23:04.027 に答える