it-swarm.dev

Vytvořit HTML tabulku s SQL FOR XML

Vytvářím dokument HL7 Continuity of Care Document (CCD) pomocí příkazů FOR XML v SQL Server 2008 R2.

Udělal jsem LOT s touto metodou, ale je to poprvé, co musím reprezentovat část dat v HTML tabulce, což mi dává potíže.

Mám tedy v tabulce následující informace:

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

a snažím se učinit následující

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

Používám tento dotaz:

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

A stále získávám následující:

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

Má někdo nějakou radu?

23
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')

Pro přidání záhlaví můžete použít také 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')
29
Mikael Eriksson

Mikaelova odpověď funguje, ale bude to takto:

Namísto použití FOR XML PATH ('tr') použijte PRO XML RAW ('tr'), ELEMENTS. To zabrání zřetězení hodnot a poskytne vám velmi čistý výstup. Váš dotaz by vypadal takto:

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

Dávám přednost přidání záhlaví řádek pomocí čistého značení, takže můžu mít trochu lepší kontrolu nad tím, co se děje. Celý blok kódu by vypadal takto:

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

UPRAVIT

Chtěl jsem přidat nějakou extra hodnotu, kterou jsem vytvořil na základě potřeby formátovat výstupní tabulku.

Alias ​​"AS td" vytvoří v značce <td>value</td> prvky, ale ne proto, že chápe, že buňka tabulky je td. Toto odpojení nám umožňuje vytvářet falešné prvky HTML, které lze později po provedení dotazu aktualizovat. Například, pokud jsem chtěl, aby hodnota ProblemType byla zarovnána do středu, můžu Tweak jména prvku povolit. Nemohu přidat styl nebo třídu do názvu prvku, protože v SQL se rozděluje konvence pojmenování aliasu, ale můžu vytvořit nový název prvku, například tdc. Tím se vytvoří <tdc>value</tdc> elementy. I když to není v žádném případě platné označení, je snadné, aby příkaz pro nahrazení zpracovával.

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>')

Tím se vytvoří elementy buněk s formátem <td class="center">value</td>. Rychlý blok v horní části řetězce a vy budete mít hodnoty zarovnané na střed s jednoduchým Tweakem.

Další situace, kterou jsem potřeboval vyřešit, bylo zahrnutí odkazů do značek. Pokud hodnota v buňce je hodnota, kterou potřebujete v href to je docela snadné vyřešit. Rozbalím příklad tak, aby obsahoval pole ID, které chcete propojit s podrobnou adresou URL.

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>')

Tento příklad nezohledňuje použití hodnoty v buňce uvnitř textu odkazu, ale je to řešitelný problém s některou prací CHARINDEX.

Moje poslední implementace tohoto systému byla pro zasílání HTML emailů na základě SQL dotazů. Měl jsem opakovanou potřebu zarovnání buněk a běžné typy odkazů, takže jsem přemístil funkce nahrazení do sdílené skalární funkce v SQL, takže jsem nemusel mít je ve všech mých uložených procedur, které poslal e-mail.

Doufám, že to přidá nějakou hodnotu.

24
Chris Porter

Toto je obecné řešení s FUNCTION na XML- base pomocí FLWOR

Transformuje SELECT do tabulky XHTML.

Funguje (testováno) s 2008R2 +, ale jsem si jistý, že to bude fungovat i v roce 2008, možná i v roce 2005. Pokud to chce někdo ověřit, zanechte prosím komentář. Díky

Následující funkce nahrazuje všechny dříve uvedené funkce (viz předchozí verze v případě potřeby)

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

Nejjednodušší volání

Maketa stolu s některými hodnotami

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

--Volaní musí uzavřít SELECT ... FOR XML v paranthesis!
- klikněte na tlačítko spustit snippet, abyste viděli výsledek!

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>

Pokud potřebujete záhlaví s polotovary

Pokud tabulka obsahuje sloupec s prázdným názvem, nebo chcete-li nastavit titulek sloupce ručně (multi langugage support!) Nebo chcete-li nahradit název CamelCaseName s popisem, který je napsán mimo tuto stránku, můžete tento atribut předat jako atribut: 

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>

Plná podpora CSS a hyperlinky

Atributy můžete použít k přechodu přes odkaz nebo řádek založený na hodnotách a dokonce i na hodnotách založených na hodnotách pro označení sloupců a dokonce buněk pro styl 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>

Jako možné vylepšení je možné předat one-row-footer s agregovanými hodnotami jako další parametr a přidat jej jako <tfoot>

16
Shnugo

Všechny tyto odpovědi fungují dobře, ale nedávno jsem narazil na problém, kdy jsem chtěl mít podmíněné formátování v html, tj. Chtěl jsem, aby se vlastnost stylu dt změnila na základě dat. Základní formát je podobný s přidáním nastavení 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)

Chcete-li k tomu přidat podmíněné formátování, stačí přidat prohlášení o případu:

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

Do té doby jsem narazil na tento problém. Takto jsem to vyřešil:

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

dávám přednost tomuto:

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

Existují ohromné ​​odpovědi. Chtěl jsem jen dodat, že můžete také použít styly v rámci vašeho dotazu, které by mohly být dobré z hlediska designu.

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

Kde someColumn je váš atribut z tabulky

A someTable je název vaší tabulky

someCondition je volitelné, pokud používáte WHERE claus

Vezměte prosím na vědomí, že dotaz je pouze výběr dvou atributů, můžete přidat tolik, kolik chcete, a také můžete změnit styl.

Samozřejmě můžete použít styly i jinými způsoby. Ve skutečnosti je vždy lepší používat externí CSS, ale je dobré vědět, jak vkládat styly inline, protože je budete potřebovat.

0
Ahmad Shli

Zkuste to:

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