1

我希望从表中的 1 行获取值作为查询中的列名,其中列的数据来自第二个表。我认为这可以通过 PIVOT 来实现,但在尝试解决它时无处可去

具体来说,我们希望记录患者随时间变化的不同抗原的抗体水平。将被监测的实际抗原因每位患者而异。因此,我们有一个记录将被监控的抗原的表,该表称为 reftblDSAColumnLabels。正如您在示例中看到的,有两行代表不同的人,由 PersonCategoryId 标识。此表对 PersonCategoryId 有唯一约束

(该表实际上最多有“Antigen12Label”,因此每个患者最多可以监测 12 种不同的抗原的抗体水平,但我在这里简化了它)

tblDSA 列标签

包含数据的表称为 tblDSAData,您可以在下面看到 PersonCategoryId = 1 的患者的一些代表性行 在此处输入图像描述

我想要实现的是具有以下列标题的输出,其中 PersonCategoryId = 1

PersonCategoryId SampleDate A1 Cw6 DR15 DR51

这就是 PersonCategoryId = 2

PersonCategoryId SampleDate A2 A3 B7 B9

我觉得这应该相当容易,但我似乎有一个关于 PIVOT 的心理障碍

4

1 回答 1

1

Well part of the problem that I see is you have two tables that are de-normalized, meaning you basically have two tables that are designed as spreadsheets and not tables. The best solution to this problem would be to restructure your tables.

If possible, my advice would be to change the table structure to the following:

CREATE TABLE reftblDSAColumnLabels
(
  [PersonCategoryId] int, 
  [AntigenNum] int, 
  [AntigenValue] varchar(4)
);

CREATE TABLE tblDSAData 
(
  [PersonCategoryId] int, 
  [SampleDate] datetime,
  [AntigenNum] int, 
  [AntigenValue] int
);

This way you could join the tables on both the personCategoryId and the AntigenNum (1, 2, 3, etc). You will see why I suggest this in a minute.

Since your tables are de-normalized it will be very difficult to generate the result set on the fly by passing in a personCategoryId. You will need to use dynamic SQL to generate the result based on the id that is submitted to a procedure.

In order to get this result, I would suggest apply both the UNPIVOT and PIVOT functions. The UNPIVOT is going to take your tables that are in multiple columns and convert them into the structures that I suggested above. This will make getting the result much easier.

UNPIVOT:

You need to unpivot both tables, the queries to unpivot will be similar to the following:

select personCategoryId, 
  replace(replace(col, 'Antigen', ''), 'Label', '') colNum,
  value l_value
from reftblDSAColumnLabels
unpivot
(
  value
  for col in ([Antigen1Label], [Antigen2Label], [Antigen3Label], [Antigen4Label])
) unpiv
where PersonCategoryId = 1;

See SQL Fiddle with Demo

select personCategoryId,SampleDate,
  replace(replace(col, 'Antigen', ''), 'Value', '') colNum,
  value d_value
from tblDSAData
unpivot
(
  value
  for col in ([Antigen1Value], [Antigen2Value], [Antigen3 Value], [Antigen4Value])
) unpiv;

See SQL Fiddle with Demo. If you run these queries you will notice that you get a result similar to this:

| PERSONCATEGORYID | COLNUM | L_VALUE |
---------------------------------------
|                1 |      1 |      A1 |
|                1 |      2 |     Cw6 |
|                1 |      3 |    DR15 |
|                1 |      4 |    DR51 |

and

| PERSONCATEGORYID |                      SAMPLEDATE | COLNUM | D_VALUE |
-------------------------------------------------------------------------
|                1 | February, 08 2013 00:00:00+0000 |      1 |    1278 |
|                1 | February, 08 2013 00:00:00+0000 |      2 |   11272 |
|                1 | February, 08 2013 00:00:00+0000 |      3 |    6880 |
|                1 | February, 08 2013 00:00:00+0000 |      4 |    7544 |
|                1 | February, 11 2013 00:00:00+0000 |      1 |    1711 |
|                1 | February, 11 2013 00:00:00+0000 |      2 |    9681 |
|                1 | February, 11 2013 00:00:00+0000 |      3 |    8437 |
|                1 | February, 11 2013 00:00:00+0000 |      4 |    8967 |

PIVOT

Once this data is in this multiple row format, you can easily join the results on the personCategoryId and the colNum and apply the PIVOT function to get the final result. The code with the join and the PIVOT will be:

select *
from
(
  select l.personCategoryId, l_value, d_value, SampleDate
  from
  (
    select personCategoryId, 
      replace(replace(col, 'Antigen', ''), 'Label', '') colNum,
      value l_value
    from reftblDSAColumnLabels
    unpivot
    (
      value
      for col in ([Antigen1Label], [Antigen2Label], [Antigen3Label], [Antigen4Label])
    ) unpiv
    where PersonCategoryId = 1
  ) l
  inner join
  (
    select personCategoryId,SampleDate,
      replace(replace(col, 'Antigen', ''), 'Value', '') colNum,
      value d_value
    from tblDSAData
    unpivot
    (
      value
      for col in ([Antigen1Value], [Antigen2Value], [Antigen3Value], [Antigen4Value])
    ) unpiv
  ) d
    on l.PersonCategoryId = d.PersonCategoryId
    and l.colNum = d.colNum
) src
pivot
(
  max(d_value)
  for l_value in (A1, Cw6, DR15, DR51)
) piv;

See SQL Fiddle with Demo.

Now for your current problem, you need to pass in the personCategoryId so the column headers will be changing for each id. Since the column headers will change you will need to use dynamic SQL to get the result. You can easily convert the above code into dynamic SQL and the script will be:

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX),
    @personCategoryId int = 1

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(value) 
                    from
                    (
                      select value
                      from reftblDSAColumnLabels
                      unpivot
                      (
                        value
                        for col in ([Antigen1Label], [Antigen2Label], [Antigen3Label], [Antigen4Label])
                      ) unpiv
                      where PersonCategoryId = @personCategoryId
                    ) d
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


set @query = 'SELECT personCategoryId, SampleDate,' + @cols + '  
             from
              (
                select l.personCategoryId, l_value, d_value, SampleDate
                from
                (
                  select personCategoryId, 
                    replace(replace(col, ''Antigen'', ''''), ''Label'', '''') colNum,
                    value l_value
                  from reftblDSAColumnLabels
                  unpivot
                  (
                    value
                    for col in ([Antigen1Label], [Antigen2Label], [Antigen3Label], [Antigen4Label])
                  ) unpiv
                  where PersonCategoryId = '+cast(@personCategoryId as varchar(10))+'
                ) l
                inner join
                (
                  select personCategoryId,SampleDate,
                    replace(replace(col, ''Antigen'', ''''), ''Value'', '''') colNum,
                    value d_value
                  from tblDSAData
                  unpivot
                  (
                    value
                    for col in ([Antigen1Value], [Antigen2Value], [Antigen3Value], [Antigen4Value])
                  ) unpiv
                ) d
                  on l.PersonCategoryId = d.PersonCategoryId
                  and l.colNum = d.colNum
            ) src
            pivot 
            (
                max(d_value)
                for l_value in (' + @cols + ')
            ) p '


execute(@query)

See SQL Fiddle with Demo. All versions will give a result:

| PERSONCATEGORYID | SAMPLEDATE |   A1 |   CW6 |  DR15 | DR51 |
---------------------------------------------------------------
|                1 | 2013-02-08 | 1278 | 11272 |  6880 | 7544 |
|                1 | 2013-02-11 | 1711 |  9681 |  8437 | 8967 |
|                1 | 2013-02-13 | 2107 | 11516 |  8958 | 7884 |
|                1 | 2013-02-15 | 1947 | 13857 | 10352 | 8719 |
|                1 | 2013-02-18 | 1917 | 10026 |  9848 | 8493 |

Edit #1, If you were to normalize the two tables you would still have to use dynamic SQL to get the column headers for each personCategoryId, however you would be able to remove the unpivot of both tables. The code will be:

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX),
    @personCategoryId int = 1

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(AntigenValue) 
                    from reftblDSAColumnLabels
                    where PersonCategoryId = @personCategoryId
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


set @query = 'SELECT personCategoryId, SampleDate,' + @cols + '  
             from
              (
                select l.personCategoryId, d.SampleDate,
                    l.AntigenValue l_value, d.AntigenValue d_value
                from reftblDSAColumnLabels l
                inner join tblDSAData d
                  on l.PersonCategoryId = d.PersonCategoryId
                  and l.AntigenNum = d.AntigenNum
            ) src
            pivot 
            (
                max(d_value)
                for l_value in (' + @cols + ')
            ) p '


execute(@query)

See SQL Fiddle with Demo

于 2013-05-29T15:36:19.183 回答