从平台中选择

时间:2010-12-10 22:09:22

标签: sql sql-server

我有一个包含此SubjectID UserID列的表格。单个用户有许多主题ID。我需要以某种方式选择以获得与此类似的结果集:

UserID1 SubjectID1 SubjectID2 Subject3
UserID2 SubjectID1 SubjectID2 Subject3
UserID3 SubjectID1 SubjectID2 Subject3

2 个答案:

答案 0 :(得分:2)

您使用的是哪个数据库? Microsoft SQL Server(2005年起)具有PIVOT / UNPIVOT以将行转置为列,反之亦然。 http://msdn.microsoft.com/en-us/library/ms177410.aspx

答案 1 :(得分:1)

为了便于讨论,我们在名为 SubjectUser 的表中定义样本数据集,如下所示:

SELECT NULL AS SubjectId, NULL AS UserID INTO SubjectUser WHERE 1=0
UNION SELECT 'cpsc500', 'maxt3r'
UNION SELECT 'phil507', 'zontar33'
UNION SELECT 'phil507', 'maxt3r'
UNION SELECT 'eng501',  'zontar33'
UNION SELECT 'eng501',  'maxt3r'
UNION SELECT 'bkwv101', 'spaced99'

然后,您可以使用此查询获得所需的结果:

;WITH
  orderedSubjects AS (
    SELECT
      UserId
    , SubjectId
    , 'SubjectId'
      + CAST(
          ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY SubjectId)
          AS NVARCHAR )
      AS col
    FROM SubjectUser
  )
SELECT *
FROM orderedSubjects
PIVOT (
  MAX(SubjectId)
  FOR col IN (SubjectId1, SubjectId2, SubjectId3)
) AS p

让我们来看看这个查询的部分内容。第一个问题是为每个用户的主题分配列名。 orderedSubjects 表用于此目的,由此查询定义:

SELECT
  UserId
, SubjectId
, 'SubjectId'
  + CAST(
      ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY SubjectId)
      AS NVARCHAR )
  AS col
FROM SubjectUser

ROW_NUMBER()... PARTITION BY ... ORDER BY用于为每个用户的主题分配序列号。任意地,主题按字母顺序排列。然后,通过在每个序列号前加上字符串 SubjectId 来生成列名。结果如下:

UserId     SubjectId   col
maxt3r     cpsc500     SubjectId1
maxt3r     eng501      SubjectId2
maxt3r     phil507     SubjectId3
spaced99   bkwv101     SubjectId1
zontar33   eng501      SubjectId1
zontar33   phil507     SubjectId2

我们现在拥有创建最终结果所需的数据。它是通过旋转 orderedSubjects

获得的
SELECT *
FROM orderedSubjects
PIVOT (
  MAX(SubjectId)
  FOR col IN (SubjectId1, SubjectId2, SubjectId3)
) AS p

MAX 的应用只是因为 PIVOT 语法需要使用聚合函数。在目前的情况下,每个组中只有一个 SubjectID ,因此 MAX 可以很容易地 MIN

另请注意,SQL Server PIVOT 语法要求生成的数据透视表列的数量固定。在这种情况下,查询是针对三个主题的硬编码。如果数据包含的值多于分配的列数,则会丢弃多余的值。

最终结果如下:

UserId      SubjectId1      SubjectId2      SubjectId3
maxt3r      cpsc500         eng501          phil507
spaced99    bkwv101         NULL            NULL
zontar33    eng501          phil507         NULL