2012-08-10 18 views
4

這對我來說很棘手......Transact SQL - 動態構建列名

我在Microsoft SQL Server 2008上工作,我有一個包含personnames的表。人名可能隨時間而改變,因此也有歷史信息。

例子:

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總是指定當前的名字),並且沒有其他記錄他總是被命名爲邁克爾漢森。

個人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) + ' 

邏輯上這可能是工作(這將給予正確的列名),但不幸的是語法「+數(PID)」不起作用。

(Phew!)那麼任何有此解決方案的人?

預先感謝您。

回答

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

+0

好吧,它適合我!我知道最多隻有6個歷史名稱,實際上它是按照我的意願命名的,所以從這裏大拇指!謝謝! – olf 2012-08-10 10:41:58

+0

非常感謝,你的sql代碼也解決了我的問題。 @Jonas Lincoln – 2013-07-24 03:51:02

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 

Example on SQL Fiddle

+0

嗯,我明白你的觀點,但由於某種原因,它不起作用。第一個例子說它不知道PID,所以它不只是一個鑄造nvarchar的問題。 第二個例子說「列名未知」。 – olf 2012-08-10 10:45:33

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; 
+0

op示例結果僅顯示3個名稱列。然而,他的所有嘗試都清楚地表明,他試圖根據序列順序將名稱列轉換爲6個不同名稱列。他說'每個人只在一張新表中列出所有的名字',這意味着他將把它存儲在另一個表格結構中,這顯然會包含6個名稱列。 最好的是要求確認。 – Paciv 2012-08-10 10:08:53

+0

它對我有用!我知道最多隻有6個歷史名稱,實際上它是按照我的意願命名的,所以從這裏大拇指!謝謝! – olf 2012-08-10 10:42:39

+0

我給Jonas Lincoln一個接受的答案,因爲它與你的相似 - 但他快七分鐘:-)。 – olf 2012-08-10 10:48:25