4

这对我来说太棘手了...

我在 Microsoft SQL Server 2008 上工作,我有一个包含人名的表。这些人的名字可能会随着时间而改变,因此也有历史信息。

例子:

PID Sequence Name
1   0        Michael Hansen
2   0        Ulla Hansen
2   94       Ulla Margrethe Hansen
2   95       Ulla Margrethe Jensen
3   0        Daniella Oldfield
3   95       Daniella Quist

(我还没有建立这个表——所以我不能进去改变数据的存储方式)。PID 为 1 的人称为 Michael Hansen。这是他的当前姓名(序列 0 始终指定当前姓名)并且由于没有其他记录,他一直被命名为 Michael Hansen。

个人 PID 2 当前称为 Ulla Hansen(序列 0)。在此之前,她被称为 Ulla Margrethe Hansen(因为这是下一个序列号),在此之前,她又被称为 Ulla Margrethe Jensen。

我对这张表的了解是当前名称始终是序列 0。我还知道,如果有两个名称,则下一个序列是 95。以及三个历史名称:当前名称:序列 0,在该序列之前 94 和最旧的名称95.

我的数据库包含最多 6 个历史名称的信息(序列 0、91、92、93、94、95)。

现在我被告知要在一个新表中列出所有名称,每人只有一行,例如:

PID Name1             Name2                   Name3
1   Michael Hansen
2   Ulla Hansen       Ulla Margrethe Hansen   Ulla Margrethe Jensen
3   Daniella Oldfield Daniella Quist

到目前为止,我有以下几乎可以工作的 SQL:

SELECT PID 
  ,MAX(CASE sequence WHEN 0 THEN Name ELSE '' END) AS Name1
  ,MAX(CASE sequence WHEN 91 THEN Name ELSE '' END) AS Name2
  ,MAX(CASE sequence WHEN 92 THEN Name ELSE '' END) AS Name3
  ,MAX(CASE sequence WHEN 93 THEN Name ELSE '' END) AS Name4
  ,MAX(CASE sequence WHEN 94 THEN Name ELSE '' END) AS Name5
  ,MAX(CASE sequence WHEN 95 THEN Name ELSE '' END) AS Name6
FROM tblShipTypeHistory
GROUP BY PID

它在每个 PID 的一行中为我提供了我想要的所有名称。当前名称也始终列在 Name1 下。问题是我需要第二个最新的名字出现在 Name2 列等中。我的情况(当然)只有在一个人有六个名字的情况下才有效。

所以我需要做的是根据 PID 实际拥有的名称动态命名列 Name2 到 Name6。所以我尝试动态地构建我的 SQL(DECLARE @SQL AS NVARCHAR(MAX) 然后将 @SQL = 设置为上面的 SQL 示例)。然后我尝试了类似的东西

SET @SQL = 'SELECT ....
,MAX(CASE sequence WHEN 91 THEN Name ELSE '' END) AS Name' + COUNT(PID) - 4 + '
,MAX(CASE sequence WHEN 92 THEN Name ELSE '' END) AS Name' + COUNT(PID) - 3 + '
,MAX(CASE sequence WHEN 93 THEN Name ELSE '' END) AS Name' + COUNT(PID) - 2 + '
,MAX(CASE sequence WHEN 94 THEN Name ELSE '' END) AS Name' + COUNT(PID) - 1 + '
,MAX(CASE sequence WHEN 95 THEN Name ELSE '' END) AS Name' + COUNT(PID) + '

逻辑上这可以工作(它会给出正确的列名),但不幸的是语法“+ count(PID)”不起作用。

(呸!)那么有谁对此有解决方案吗?

先感谢您。

4

4 回答 4

3

将 RANK() 和 PIVOT() 结合使用。Rank() 找出名称的“年龄”,枢轴获取所有列。

create table t(pid int not null, sequence int not null, name nvarchar(50))


INSERT INTO t VALUES
(1,   0,        N'Michael Hansen'),
(2,   0,        N'Ulla Hansen'),
(2,   94,       N'Ulla Margrethe Hansen'),
(2,   95,       N'Ulla Margrethe Jensen'),
(3,   0,        N'Daniella Oldfield'),
(3,   95,       N'Daniella Quist')

SELECT pid, [1] as Name1, [2] as Name2, [3] as Name3, [4] as Name4, [5] as Name5, [6] as Name6
FROM
(
SELECT pid, name, rank() over(partition BY pid ORDER BY sequence) AS name_age
FROM t) SOURCE
PIVOT
(
  max(name)
 FOR [name_age] in ([1], [2], [3], [4], [5], [6])) as pvt

Sql 小提琴:http ://sqlfiddle.com/#!3/d8301/16

于 2012-08-10T09:40:45.993 回答
2

您的语法错误在于您试图将COUNT(*)int 与 NVARCHAR 连接起来。我认为这样的事情对你有用:

SET @SQL = 'SELECT ....
,MAX(CASE sequence WHEN 91 THEN Name ELSE '' END) AS Name' + CAST(COUNT(PID) - 4 AS NVARCHAR) + '
,MAX(CASE sequence WHEN 92 THEN Name ELSE '' END) AS Name' + CAST(COUNT(PID) - 3 AS NVARCHAR) + '
,MAX(CASE sequence WHEN 93 THEN Name ELSE '' END) AS Name' + CAST(COUNT(PID) - 2 AS NVARCHAR) + '
,MAX(CASE sequence WHEN 94 THEN Name ELSE '' END) AS Name' + CAST(COUNT(PID) - 1 AS NVARCHAR) + '
,MAX(CASE sequence WHEN 95 THEN Name ELSE '' END) AS Name' + CAST(COUNT(PID) AS NVARCHAR) + '

但是,这似乎不是很有未来的证据,因为您仍在手动创建 SQL,我不确定为什么您的序列从 0 变为 91,但我假设它们将始终按升序排列,即使存在间隙所以已经使用该ROW_NUMBER()函数来获取每个名称的实例:

DECLARE @SQL NVARCHAR(MAX) = '',
        @PVT NVARCHAR(MAX) = ''

SELECT  @SQL = @SQL + ', COALESCE(' + QUOTENAME('Name' + RowNum) + ', '''') AS ' +  QUOTENAME('Name' + RowNum),
        @PVT = @PVT + ', ' + QUOTENAME('Name' + RowNum)
FROM    (   SELECT  DISTINCT CONVERT(VARCHAR, ROW_NUMBER() OVER(PARTITION BY PID ORDER BY Sequence)) [RowNum]
            FROM    tblShipTypeHistory
        ) rn

SET @SQL = 'SELECT PID' + @SQL + '
            FROM    (   SELECT  PID, 
                                Name, 
                                ''Name'' + CONVERT(VARCHAR, ROW_NUMBER() OVER(PARTITION BY PID ORDER BY Sequence)) [NameID]
                        FROM    tblShipTypeHistory
                    ) data
                    PIVOT
                    (   MAX(Name)
                        FOR NameID IN (' + STUFF(@PVT, 1, 2, '') + ')
                    ) pvt'

EXECUTE SP_EXECUTESQL @SQL

SQL Fiddle 示例

于 2012-08-10T09:24:16.843 回答
1
with names(pid, name, rn) as 
(
  select pid, 
         name, 
         ROW_NUMBER() over (partition by pid order by sequence) 
  from tblShipTypeHistory
)
select pid, 
       [1] as Name1, 
       [2] as Name2, 
       [3] as Name3, 
       [4] as Name4, 
       [5] as Name5, 
       [6] as Name6
from names pivot(max(name) for rn in ([1], [2], [3], [4], [5], [6])) as a;
于 2012-08-10T09:48:02.363 回答
0

尝试动态 PIVOT http://beyondrelational.com/modules/2/blogs/70/posts/10840/dynamic-pivot-in-sql-server-2005.aspx

于 2012-08-10T09:12:10.347 回答