web-dev-qa-db-ja.com

SQL FOR XMLを使用してHTMLテーブルを作成する

SQL Server 2008 R2でFOR XMLステートメントを使用して、HL7継続ケアドキュメント(CCD)を作成しています。

私はこの方法で多くのことをしましたが、HTMLテーブルでデータの一部を表す必要があるのはこれが初めてであり、これが問題を引き起こしています。

したがって、表には次の情報があります。

  Problem  |   Onset    | Status
---------------------------------
  Ulcer    | 01/01/2008 | Active
  Edema    | 02/02/2005 | Active

そして、私は次をレンダリングしようとしています

<tr>
    <th>Problem</th>
    <th>Onset</th>
    <th>Status</th>
</tr>
<tr>
    <td>Ulcer</td>
    <td>01/01/2008</td>
    <td>Active</td>
</tr>
<tr>
    <td>Edema</td>
    <td>02/02/2005</td>
    <td>Active</td>
</tr>

私はこのクエリを使用しています:

SELECT    p.ProblemType AS "td"
    , p.Onset AS "td"
    , p.DiagnosisStatus AS "td"
FROM tblProblemList p
WHERE p.PatientUnitNumber = @PatientUnitNumber
FOR XML PATH('tr')

そして、私は次を取得し続けます:

<tr>
  <td>Ulcer2008-01-01Active</td>
</tr>
<tr>
  <td>Edema2005-02-02Active</td>
</tr>

誰かアドバイスはありますか?

24
David Walker
select 
  (select p.ProblemType     as 'td' for xml path(''), type),
  (select p.Onset           as 'td' for xml path(''), type),
  (select p.DiagnosisStatus as 'td' for xml path(''), type)
from tblProblemList p
where p.PatientUnitNumber = @PatientUnitNumber
for xml path('tr')

ヘッダーも追加するには、union allを使用できます。

select 
  (select 'Problem' as th for xml path(''), type),
  (select 'Onset'   as th for xml path(''), type),
  (select 'Status'  as th for xml path(''), type)
union all         
select 
  (select p.ProblemType     as 'td' for xml path(''), type),
  (select p.Onset           as 'td' for xml path(''), type),
  (select p.DiagnosisStatus as 'td' for xml path(''), type)
from tblProblemList p
where p.PatientUnitNumber = @PatientUnitNumber
for xml path('tr')
30
Mikael Eriksson

ミカエルの答えはうまくいきますが、これもそうです:

FOR XML PATH( 'tr')を使用するのではなく、FOR XML RAW( 'tr')、ELEMENTSを使用してください。これにより、値が連結されなくなり、非常にきれいな出力が得られます。クエリは次のようになります。

SELECT  p.ProblemType AS td,
        p.Onset AS td,
        p.DiagnosisStatus AS td
FROM    tblProblemList p
WHERE   p.PatientUnitNumber = @PatientUnitNumber
FOR XML RAW('tr'), ELEMENTS

純粋なマークアップを使用してヘッダー行を追加することで、何が起こっているかをより適切に制御できるようになります。完全なコードブロックは次のようになります。

DECLARE @body NVARCHAR(MAX)
SET     @body = N'<table>'
    + N'<tr><th>Problem</th><th>Onset</th><th>Status</th></tr>'
    + CAST((
        SELECT  p.ProblemType AS td,
                p.Onset AS td,
                p.DiagnosisStatus AS td
        FROM    tblProblemList p
        WHERE   p.PatientUnitNumber = @PatientUnitNumber
        FOR XML RAW('tr'), ELEMENTS
    ) AS NVARCHAR(MAX))
    + N'</table>'

編集

出力テーブルをフォーマットする必要性に基づいて思いついた追加の値を追加したかったのです。

「AS td」エイリアスは、マークアップに<td>value</td>要素を生成しますが、テーブルセルがtdであることを理解しているためではありません。この切断により、クエリの実行後に後で更新できる偽のHTML要素を作成できます。たとえば、ProblemType値を中央揃えにしたい場合、要素名を微調整してこれを可能にすることができます。スタイル名またはクラスを要素名に追加できないのは、SQLのエイリアスの命名規則に違反するためですが、tdcなどの新しい要素名を作成できます。これにより、<tdc>value</tdc>要素が生成されます。これは決して有効なマークアップではありませんが、replaceステートメントで簡単に処理できます。

DECLARE @body NVARCHAR(MAX)
SET     @body = N'<table>'
    + N'<tr><th>Problem</th><th>Onset</th><th>Status</th></tr>'
    + CAST((
        SELECT  p.ProblemType AS tdc,
                p.Onset AS td,
                p.DiagnosisStatus AS td
        FROM    tblProblemList p
        WHERE   p.PatientUnitNumber = @PatientUnitNumber
        FOR XML RAW('tr'), ELEMENTS
    ) AS NVARCHAR(MAX))
    + N'</table>'

SET @body = REPLACE(@body, '<tdc>', '<td class="center">')
SET @body = REPLACE(@body, '</tdc>', '</td>')

これにより、<td class="center">value</td>という形式のセル要素が作成されます。文字列の上部にある簡単なブロックと、簡単な調整で値を中央揃えにします。

解決する必要がある別の状況は、マークアップにリンクを含めることでした。セルの値がhrefで必要な値である限り、これは簡単に解決できます。この例を拡張して、詳細URLにリンクするIDフィールドを含めます。

DECLARE @body NVARCHAR(MAX)
SET     @body = N'<table>'
    + N'<tr><th>Problem</th><th>Onset</th><th>Status</th></tr>'
    + CAST((
        SELECT  p.ID as tda
                p.ProblemType AS td,
                p.Onset AS td,
                p.DiagnosisStatus AS td
        FROM    tblProblemList p
        WHERE   p.PatientUnitNumber = @PatientUnitNumber
        FOR XML RAW('tr'), ELEMENTS
    ) AS NVARCHAR(MAX))
    + N'</table>'

SET @body = REPLACE(@body, '<tda>', '<td><a href="http://mylinkgoeshere.com/id/')
SET @body = REPLACE(@body, '</tda>', '">click-me</a></td>')

この例では、リンクテキスト内のセルの値を使用することは考慮していませんが、CHARINDEXの一部の作業では解決可能な問題です。

このシステムの最終的な実装は、SQLクエリに基づいてHTMLメールを送信することでした。セルの配置と一般的なリンクタイプが繰り返し必要だったため、置換関数をSQLの共有スカラー関数に移動したため、電子メールを送信するすべてのストアドプロシージャにそれらを含める必要はありませんでした。

これが何らかの価値をもたらすことを願っています。

25
Chris Porter

これは [〜#〜] flwor [〜#〜] を使用してFUNCTION- baseでXMLを使用する一般的なソリューションです。

SELECTをXHTMLテーブルに変換します。

2008R2 +で動作(テスト済み)していますが、これは2008年でも動作し、2005年でも動作するはずです。誰かがこれを確認したい場合は、コメントを残してください。 THX

次の関数は、以前に提供したさまざまな関数をすべて置き換えます(必要に応じて、以前のバージョンを参照してください)

CREATE FUNCTION dbo.CreateHTMLTable
(
    @SelectForXmlPathRowElementsXsinil XML
   ,@tblClass VARCHAR(100) --NULL to omit this class
   ,@thClass VARCHAR(100)  --same
   ,@tbClass VARCHAR(100)  --same
)
RETURNS XML
AS
BEGIN

RETURN 
(
    SELECT @tblClass AS [@class]  
    ,@thClass AS [thead/@class]
    ,@SelectForXmlPathRowElementsXsinil.query(
              N'let $first:=/row[1]
                return 
                <tr> 
                {
                for $th in $first/*
                return <th>{if(not(empty($th/@caption))) then xs:string($th/@caption) else local-name($th)}</th>
                }
                </tr>') AS thead
    ,@tbClass AS [tbody/@class]
    ,@SelectForXmlPathRowElementsXsinil.query(
               N'for $tr in /row
                 return 
                 <tr>{$tr/@class}
                 {
                 for $td in $tr/*
                 return
                 if(empty($td/@link)) 
                 then <td>{$td/@class}{string($td)}</td>
                 else <td>{$td/@class}<a href="{$td/@link}">{string($td)}</a></td>
                 }
                 </tr>') AS tbody
    FOR XML PATH('table'),TYPE
) 
END
GO

一番簡単な電話

いくつかの値を持つモックアップテーブル

DECLARE @tbl TABLE(ID INT, [Message] VARCHAR(100));
INSERT INTO @tbl VALUES
 (1,'Value 1')
,(2,'Value 2');

-呼び出しは、SELECT ... FOR XMLを括弧で囲む必要があります!
-スニペットの実行をクリックして結果を確認します!

SELECT dbo.CreateHTMLTable
(
     (SELECT * FROM @tbl FOR XML PATH('row'),ELEMENTS XSINIL)
     ,NULL,NULL,NULL
);
    <table>
          <thead>
                <tr>
                  <th>ID</th>
                  <th>Message</th>
                </tr>
          </thead>
          <tbody>
                <tr>
                  <td>1</td>
                  <td>Value 1</td>
                </tr>
                <tr>
                  <td>2</td>
                  <td>Value 2</td>
                </tr>
          </tbody>
        </table>

空白のあるヘッダーが必要な場合

テーブルに名前に空白を含む列が含まれている場合、または列のキャプションを手動で設定する場合(multi langugage support!)、または置換する場合CamelCaseNameにキャプションが記述されている場合、これを属性として渡すことができます。

DECLARE @tbl2 TABLE(ID INT, [With Blank] VARCHAR(100));
INSERT INTO @tbl2 VALUES
 (1,'Value 1')
,(2,'Value 2');

SELECT dbo.CreateHTMLTable
(
     (
     SELECT ID
           ,'The new name' AS [SomeOtherName/@caption] --set a caption 
           ,[With Blank] AS [SomeOtherName] 
     FROM @tbl2 FOR XML PATH('row'),ELEMENTS XSINIL
     )
     ,NULL,NULL,NULL
);
    <table>
          <thead>
                <tr>
                  <th>ID</th>
                  <th>The new name</th>
                </tr>
          </thead>
          <tbody>
                <tr>
                  <td>1</td>
                  <td>Value 1</td>
                </tr>
                <tr>
                  <td>2</td>
                  <td>Value 2</td>
                </tr>
          </tbody>
        </table>

完全なCSSサポートとハイパーリンク

属性を使用してリンクまたは行ベース、さらには値ベースのクラスを渡して、CSSスタイリング用に列やセルをマークできます。

--a mock-up table with a row based condition and hyper-links

DECLARE @tbl3 TABLE(ID INT, [With blank] VARCHAR(100),Link VARCHAR(MAX),ShouldNotBeNull INT);
INSERT INTO @tbl3 VALUES
 (1,'NoWarning',NULL,1)
,(2,'No Warning too','http://www.Link2.com',2)
,(3,'Warning','http://www.Link3.com',3)
,(4,NULL,NULL,NULL)
,(5,'Warning',NULL,5)
,(6,'One more warning','http://www.Link6.com',6);
--The query adds an attribute Link to an element (NULL if not defined)
SELECT dbo.CreateHTMLTable
(
     (
     SELECT 
       CASE WHEN LEFT([With blank],2) != 'No' THEN 'warning' ELSE NULL END AS [@class]      --The first @class is the <tr>-class
      ,ID
      ,'center' AS [Dummy/@class]                                                    --a class within TestText (appeary always)
      ,Link AS [Dummy/@link]                                                         --a mark to pop up as link
      ,'New caption' AS [Dummy/@caption]                                             --a different caption
      ,[With blank] AS [Dummy]                                                       --blanks in the column's name must be tricked away...
      ,CASE WHEN ShouldNotBeNull IS NULL THEN 'MarkRed' END AS [ShouldNotBeNull/@class] --a class within ShouldNotBeNull (appears only if needed)
      ,'Should not be null' AS [ShouldNotBeNull/@caption]                             --a caption for a CamelCase-ColumnName
      ,ShouldNotBeNull
     FROM @tbl3 FOR XML PATH('row'),ELEMENTS XSINIL),'testTbl','testTh','testTb'
);
<style type="text/css" media="screen,print">
.center
{
    text-align: center;
}
.warning
{
    color: red;
}
.MarkRed
{
    background-color: red;
}
table,th
{
        border: 1px solid black;
}
</style>
<table class="testTbl">
  <thead class="testTh">
    <tr>
      <th>ID</th>
      <th>New caption</th>
      <th>Should not be null</th>
    </tr>
  </thead>
  <tbody class="testTb">
    <tr>
      <td>1</td>
      <td class="center">NoWarning</td>
      <td>1</td>
    </tr>
    <tr>
      <td>2</td>
      <td class="center">
        <a href="http://www.Link2.com">No Warning too</a>
      </td>
      <td>2</td>
    </tr>
    <tr class="warning">
      <td>3</td>
      <td class="center">
        <a href="http://www.Link3.com">Warning</a>
      </td>
      <td>3</td>
    </tr>
    <tr>
      <td>4</td>
      <td class="center" />
      <td class="MarkRed" />
    </tr>
    <tr class="warning">
      <td>5</td>
      <td class="center">Warning</td>
      <td>5</td>
    </tr>
    <tr class="warning">
      <td>6</td>
      <td class="center">
        <a href="http://www.Link6.com">One more warning</a>
      </td>
      <td>6</td>
    </tr>
  </tbody>
</table>

可能な拡張機能として、追加のパラメーターとして集計値を含むone-row-footerを渡し、<tfoot>として追加することができます

18
Shnugo

これらの答えはすべて正常に機能しますが、最近HTMLの条件付き書式を設定したいという問題に遭遇しました。 tdのスタイルプロパティがデータに基づいて変化することを望みました。基本的な形式は、設定td =を追加したものと似ています。

declare @body nvarchar(max)
set @body = 
cast
(select 
'color:red' as 'td/@style', td = p.ProblemType, '',
td = p.Onset, '',
td = p.DiagnosisStatus, ''
from tblProblemList p
where p.PatientUnitNumber = @PatientUnitNumber
for xml path('tr'), type)
as nvarchar(max)

これに条件付き書式を追加するには、caseステートメントを追加するだけです。

declare @body nvarchar(max)
set @body = 
cast
select 
cast (case 
when p.ProblemType = 1 then 'color:#ff0000;'
else 'color:#000;'
end as nvarchar(30)) as 'td/@style',
td = p.ProblemType, '',
td = p.Onset, '',
td = p.DiagnosisStatus, ''
from tblProblemList p
where p.PatientUnitNumber = @PatientUnitNumber
for xml path('tr'), type)
as nvarchar(max)
2
CCarter

私はしばらく前にこの問題に遭遇しました。ここに私がそれを解決した方法があります:

SELECT
p.ProblemType AS "td"
, '' AS "text()"
, p.Onset AS "td"
, '' AS "text()"
, p.DiagnosisStatus AS "td"

FROM tblProblemList p
WHERE p.PatientUnitNumber = @PatientUnitNumber
FOR XML PATH('tr')
1
pd1138

私はこれを好む:

select 
convert(xml,
(
    select 'column1' as th,
           'column2' as th
    for xml raw('tr'),elements
)),     
convert(xml,
(
    select t1.column1 as td,
           t1.column2 as td
    from #t t1
    for xml raw('tr'),elements
))
for xml raw('table'),elements
0
elle0087

すでに途方もない答えがあります。クエリ内でデザインを使用するのに適したスタイルを使用することもできます。

BEGIN
  SET NOCOUNT ON;
  DECLARE @htmlOpenTable VARCHAR(200) = 
     '<table style="border-collapse: collapse; border: 1px solid #2c3e50; background-color: #f9fbfc;">'
  DECLARE @htmlCloseTable VARCHAR(200) = 
     '</table>'
  DECLARE @htmlTdTr VARCHAR(max) = (        
    SELECT 
       'border-top: 1px solid #2c3e50' as [td/@style], someColumn as td, '',
       'border-top: 1px solid #2c3e50' as [td/@style], someColumn as td, ''
    FROM someTable
    WHERE someCondition
    FOR XML PATH('tr')
  )
  SELECT @htmlOpenTable + @htmlTdTr + @htmlCloseTable
END

someColumnはテーブルの属性です

someTableはテーブル名です

someCondition節を使用している場合、WHEREはオプションです。

クエリは2つの属性のみを選択することに注意してください。必要な数だけ追加でき、スタイルを変更することもできます。

もちろん、他の方法でスタイルを使用できます。実際には、外部CSSを使用する方が常に優れていますが、インラインスタイルが必要になる可能性があるため、インラインスタイルの配置方法を知ることは良い習慣です。

0
Ahmad Shli

これを試して:

FOR XML raw, elements, root('tr')
0
Chains