Создать HTML-таблицу с помощью SQL FOR XML - PullRequest
23 голосов
/ 17 августа 2011

Я создаю документ HL7 Continuity of Care (CCD), используя операторы FOR XML в SQL Server 2008 R2.

Я сделал много с этим методом, но впервыепредставить часть данных в таблице 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>

Кто-нибудь получил какой-либо совет?

Ответы [ 8 ]

29 голосов
/ 17 августа 2011
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')
25 голосов
/ 28 марта 2012

Ответ Микаэля работает, но так будет:

Вместо использования 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> элементов.Хотя эта разметка недопустима в любом случае, оператор замены легко обрабатывать.

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.

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.

Моя последняя реализация этой системы заключалась в отправке электронных писем в формате HTML на основе запросов SQL.У меня была постоянная потребность в выравнивании ячеек и общих типах ссылок, поэтому я переместил функции замены в общую скалярную функцию в SQL, чтобы мне не пришлось использовать их во всех моих хранимых процедурах, отправляющих электронную почту.

Iнадеюсь, что это добавляет ценность.

17 голосов
/ 14 сентября 2016

Это общее решение с FUNCTION на XML -базе с использованием FLWOR

. Оно преобразует любой 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 в парантезе!
--click запустить фрагмент , чтобы увидеть результат!

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>

Если вам нужны заголовки с пробелами

Если ваша таблица содержит столбец с пробелом в имени или если вы хотитечтобы установить заголовок столбца вручную ( поддержка мультиязычности! * ), или, если вы хотите заменить 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>

В качестве возможного улучшения можно передать однорядный нижний колонтитул с агрегированными значениями в качестве дополнительного параметра и добавить его как <tfoot>

2 голосов
/ 04 июня 2014

Все эти ответы работают нормально, но недавно я столкнулся с проблемой, когда я хотел иметь условное форматирование на HTML, т.е.Я хотел, чтобы свойство стиля тд варьировалось в зависимости от данных.Базовый формат аналогичен добавлению параметра 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)
1 голос
/ 19 марта 2012

Я столкнулся с этой проблемой некоторое время назад. Вот как я это решил:

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')
0 голосов
/ 21 июня 2018

Я предпочитаю сделать это:

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 голосов
/ 22 ноября 2016

Уже есть потрясающие ответы.Я просто хотел добавить, что вы также можете использовать стили в своем запросе, которые могут быть хороши с точки зрения дизайна.

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 claus

Обратите внимание, что запрос выбирает только два атрибута, вы можете добавить столько, скольковы хотите, а также вы можете изменить стили.

Конечно, вы можете использовать стили другими способами.На самом деле, всегда лучше использовать внешний CSS, но полезно знать, как размещать встроенные стили, потому что они могут вам понадобиться

0 голосов
/ 17 августа 2011

Попробуйте это:

FOR XML raw, elements, root('tr')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...