2013-01-29 4 views
3

Мои таблицы идут следующим образом:Построить SQL-запрос с динамическими столбцами

Пациенты таблице

PatientId Name 
1   James 
... 

просмотров таблица

Date PatientID_FK Weight 
1/1  1    220 
2/1  1    210 
... 

Как я могу создать запрос, который возвращает

PatientId Name Visit1Date Visit1Weight Visit2Date Visit2Weight ... 
1   James 1/1   220    2/1   210 
2   ... 

Как мы можем добавить mor e столбцов таким образом? Как написать это SELECT? Пожалуйста помоги.

Некоторые сообщения на StackExchange говорят, что оператор SQL не может его обрабатывать. Неужели так?

+1

[Что вы пробовали] (http://www.whathaveyoutried.com) ? – Kermit

+0

Какие СУБД и версия вы используете? – Lamak

+0

просто не знаю, как это сделать. Мы используем SQL Server 2008R2. – Blaise

ответ

3

Этот тип преобразования данных должен выполняться как с функциями pivot, так и с функциями unpivot. Поскольку ваши посещения будут неизвестны, вам нужно будет использовать динамический sql. Но сначала я покажу вам, как построить запрос со значениями жесткого кодирования, чтобы было легче понять, как работает этот процесс.

Во-первых, вам нужны UNPIVOT столбцы date и weight, поэтому значения находятся в одном столбце. Это может быть сделано с помощью UNION ALL запроса или UNPIVOT функции:

UNPIVOT:

select patientid, name, rn, col, value 
from 
(
    select p.patientid, p.name, convert(char(5), v.date, 110) date, 
    cast(v.weight as char(5)) weight, 
    row_number() over(partition by PatientID_FK order by date) rn 
    from patients p 
    left join visits v 
    on p.patientid = v.PatientID_FK 
) src 
unpivot 
(
    value 
    for col in (date, weight) 
) unpiv 

См SQL Fiddle with Demo. Результат этого запроса помещает значения столбца даты и веса в один столбец с несколькими строками. Обратите внимание на то, что я применил row_number() к записям, так что вы сможете сказать, что ценности идут с каждым посещением:

| PATIENTID | NAME | RN | COL | VALUE | 
------------------------------------------- 
|   1 | James | 1 | date | 01-01 | 
|   1 | James | 1 | weight | 220 | 
|   1 | James | 2 | date | 02-01 | 
|   1 | James | 2 | weight | 210 | 

PIVOT:

Следующим шагом является применение функции PIVOT к элементам в столбце col, но сначала нам нужно изменить имя, чтобы оно давало вам имена, которые вы хотите.

Для этого я немного изменить SELECT заявление, чтобы добавить номер строки с именем Col:

select patientid, name, 'Visit'+col + cast(rn as varchar(10)) new_col, 
    value 
from ... 

Это даст вам новые имена, которые являются имена, которые вы хотите в качестве столбцов:

Visitdate1 
Visitweight1 
Visitdate2 
Visitweight2 

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

select * 
from 
(
    select patientid, name, 'Visit'+col + cast(rn as varchar(10)) new_col, 
    value 
    from 
    (
    select p.patientid, p.name, convert(char(5), v.date, 110) date, 
     cast(v.weight as char(5)) weight, 
     row_number() over(partition by PatientID_FK order by date) rn 
    from patients p 
    left join visits v 
     on p.patientid = v.PatientID_FK 
) src 
    unpivot 
    (
    value 
    for col in (date, weight) 
) unpiv 
) s1 
pivot 
(
    max(value) 
    for new_col in (Visitdate1,Visitweight1, 
        Visitdate2,Visitweight2) 
) piv 

См. SQL Fiddle with Demo.

Dynamic PIVOT:

Теперь, когда я объяснил логику, как это настроить, вы хотите реализовать этот же процесс с помощью динамического SQL. Вы динамическая версия SQL будет:

DECLARE @colsUnpivot AS NVARCHAR(MAX), 
    @query AS NVARCHAR(MAX), 
    @colsPivot as NVARCHAR(MAX) 

select @colsUnpivot = stuff((select ', '+quotename(C.name) 
     from sys.columns as C 
     where C.object_id = object_id('visits') and 
       C.name not in ('PatientID_FK') 
     for xml path('')), 1, 1, '') 

select @colsPivot = STUFF((SELECT ',' + quotename('Visit'+c.name 
              + cast(v.rn as varchar(10))) 
        from 
        (
         select row_number() over(partition by PatientID_FK order by date) rn 
         from visits 
        ) v 
        cross apply sys.columns as C 
        where C.object_id = object_id('visits') and 
        C.name not in ('PatientID_FK') 
        group by c.name, v.rn 
        order by v.rn 
      FOR XML PATH(''), TYPE 
      ).value('.', 'NVARCHAR(MAX)') 
     ,1,1,'') 

set @query 
    = 'select * 
     from 
     (
     select patientid, name, ''Visit''+col + cast(rn as varchar(10)) new_col, 
      value 
     from 
     (
      select p.patientid, p.name, convert(char(5), v.date, 110) date, 
      cast(v.weight as char(5)) weight, 
      row_number() over(partition by PatientID_FK order by date) rn 
      from patients p 
      left join visits v 
      on p.patientid = v.PatientID_FK 
     ) x 
     unpivot 
     (
      value 
      for col in ('+ @colsunpivot +') 
     ) u 
    ) x1 
     pivot 
     (
     max(value) 
     for new_col in ('+ @colspivot +') 
    ) p' 

exec(@query) 

См SQL Fiddle with Demo

Результат от обеих версий:

| PATIENTID | NAME | VISITDATE1 | VISITWEIGHT1 | VISITDATE2 | VISITWEIGHT2 | 
----------------------------------------------------------------------------- 
|   1 | James |  01-01 |  220 |  02-01 |  210 | 
Смежные вопросы