2015-03-31 38 views
1

我新的SQL Server和有問題牛逼SQL:選擇每個匹配的行爲

這裏是我的底線的情況:

表foo:

id | pid | val1 | val2 | val3 
------------------------------ 
1 | 4721 | 1 | 2 | 3 
2 | 25 | 4 | 5 | 6 
3 | 4721 | 7 | 8 | 9 

結果表:

pid | id_1 | val1_1 | val2_1 | val3_1 | id_2 | val1_2 | val2_2 | val3_2 | id_3 | val1_3 | val2_3 | val3_3 
---------------------------------------------------- 
4721 | 1 | 1 | 2 | 3 | 2 | 4 | 5 | 6 | 3 | 7 | 8 | 9 

我要的是選擇所有匹配的行PID = 4721,並顯示他們在一個單獨的列。

+0

爲什麼是第二排的結果呢? – 2015-03-31 15:50:45

+0

@GiorgiNakeuri sry,結果第二排是錯誤的。 – hunt3r87 2015-04-01 08:54:27

回答

2

嚴格來說,對這個問題的SQL答案是「這是一個顯示問題。顯示是應用程序問題,而不是數據庫問題。」從關係角度講,這也是要求服務器打破第一範式並創建重複組,這意味着它幾乎肯定會需要跳過幾個環節,並且會有很大的侷限性。

的根據DBA「正確的方式」會做這樣的事情:

SELECT pid, id, val1, val2, val3 
FROM Table 
ORDER BY pid, id; 

然後,在你的應用程序,走過你的結果集,當你需要它格式化輸出。

你甚至可以包括對pid內的每個id命令,也許使它更容易一點:

SELECT pid, 
    id, 
    val1, 
    val2, 
    val3, 
    ROW_NUMBER() OVER (PARTITION BY pid, ORDER BY id) AS "id_order" 
FROM Table 
ORDER BY pid, id; 

但是,讓我們說你不能這樣做。

如果你絕對用SQL做到這一點(例如,您的報告軟件不能處理這種事情,這是你要做的),你知道你永遠不會有超過3 id每個pid,你可以嘗試這樣的事:

;WITH Table_id_ordered AS (
    SELECT pid, 
     id, 
     val1, 
     val2, 
     val3, 
     ROW_NUMBER() OVER (PARTITION BY pid, ORDER BY id) AS "id_order" 
    FROM Table 
) 
SELECT t1.pid, 
    t1.id as id_1, 
    t1.val1 as val1_1, 
    t1.val2 as val2_1, 
    t1.val3 as val3_1, 
    t2.id as id_2, 
    t2.val1 as val1_2, 
    t2.val2 as val2_2, 
    t2.val3 as val3_2, 
    t3.id as id_3, 
    t3.val1 as val1_3, 
    t3.val2 as val2_3, 
    t3.val3 as val3_3 
FROM Table_id_ordered t1 
LEFT JOIN Table_id_ordered t2 
    ON t2.pid = t1.pid 
    AND t2.id_order = t1.id_order + 1 
LEFT JOIN Table_id_ordered t3 
    ON t3.pid = t2.pid 
    AND t3.id_order = t2.id_order + 1 
WHERE t1.id_order = 1; 

顯然,這只是對多達三個id任何pid好。正如所寫的,它也不會告訴你表中的id是否在第五位或第五位。他們完全沒有結果。我提到的第一種方法將始終返回所有數據,並且可以編寫應用程序來輕鬆處理。

對於pid,可以創建動態解決方案以完成任意數量的id,但這些要複雜得多。

+0

非常感謝,你真的幫了我。我將在應用程序端組織輸出,但這是另一個話題。 – hunt3r87 2015-04-01 09:14:25

0

我首先取消您的數據轉移以獲取您的列和值列表,分配新的列名稱,然後動態地轉移您的數據。這應該適用於任何值的組合。

說明:我稍微更改了列名,以便它們能正確排序。

IF OBJECT_ID('tempdb..#yourTable') IS NOT NULL 
    DROP TABLE #yourTable; 
IF OBJECT_ID('tempdb..#UnpivotTable') IS NOT NULL 
    DROP TABLE #UnpivotTable; 

SELECT * INTO #yourTable 
FROM 
(
    SELECT 1,4721,1,2,3 
    UNION ALL 
    SELECT 2,25,4,5,6 
    UNION ALL 
    SELECT 3,4721,7,8,9 
) AS bar(id,pID,val1,val2,val3); 

DECLARE @cols VARCHAR(MAX); 


SELECT pID, 
     --This is where I create the column names 
     CONCAT(ROW_NUMBER() OVER (PARTITION BY pID,col ORDER BY pID,id2),'_',col) NewColName, 
     val INTO #UnpivotTable 
FROM 
(
    --I need ID2 for ROW_NUMBER() so the NewColNames are applied to the correct values 
    SELECT *,id as ID2 
    FROM #yourTable 
) A 
UNPIVOT 
(
    val FOR col IN(ID,val1,val2,val3) 
) unpvt 


--Puts columns in alphabetic order into @cols 
SELECT @cols = COALESCE(@cols + ',','') + QUOTENAME(NewColName) 
FROM #UnpivotTable 
--Group by gets rid of any duplicate column names 
GROUP BY NewColName 
ORDER BY NewColName 

EXEC 
(
    'SELECT * 
    FROM #UnpivotTable 
    PIVOT 
    (
     MAX(val) FOR newColName IN (' + @cols + ') 
    ) pvt 
    WHERE pID = 4721' 
) 

--Cleanup 
IF OBJECT_ID('tempdb..#yourTable') IS NOT NULL 
    DROP TABLE #yourTable; 
IF OBJECT_ID('tempdb..#UnpivotTable') IS NOT NULL 
    DROP TABLE #UnpivotTable; 

結果:

pID   1_id  1_val1  1_val2  1_val3  2_id  2_val1  2_val2  2_val3 
----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- 
4721  1   1   2   3   3   7   8   9