2012-08-31 49 views
7

我確定我在這裏錯過了一些東西。如何查詢行數據作爲列?

我有一個這樣的數據集:

 
FK RowNumber Value Type Status 
1  1   aaaaa A  New 
1  2   bbbbb B  Good 
1  3   ccccc A  Bad 
1  4   ddddd C  Good 
1  5   eeeee B  Good 
2  1   fffff C  Bad 
2  2   ggggg A  New 
2  3   hhhhh C  Bad 
3  1   iiiii A  Good 
3  2   jjjjj A  Good 

我想查詢前3結果和樞軸他們爲列,所以最終的結果集看起來是這樣的:

 
FK Value1 Type1 Status1 Value2 Type2 Status2 Value3 Type3 Status3 
1  aaaaa  A  New  bbbbb  B  Good  ccccc  A  Bad 
2  fffff  C  Bad  ggggg  A  New  hhhhh  C  Bad 
3  iiiii  A  Good  jjjjj  A  Good 

我如何在SQL Server 2005中完成這項工作?

我一直在試圖利用這個PIVOT,但我還是很陌生與該關鍵字並不能得到它的工作就是我想要的。

SELECT * --Id, [1], [2], [3] 
FROM 
(
    SELECT Id, Value, Type, Status 
    , ROW_NUMBER() OVER (PARTITION BY Id ORDER Status, Type) as [RowNumber] 
    FROM MyTable 
) as T 
PIVOT 
(
    -- I know this section doesn't work. I'm still trying to figure out PIVOT 
    MAX(T.Value) FOR RowNumber IN ([1], [2], [3]), 
    MAX(T.Type) FOR RowNumber IN ([1], [2], [3]), 
    MAX(T.Status) FOR RowNumber IN ([1], [2], [3]) 
) AS PivotTable; 

我的實際數據集是比這更復雜一點,我需要的前10條記錄,而不是前3名,所以我不希望爲每一位簡單地做CASE WHEN RowNumber = X THEN...

更新

我測試了所有的答案下面,發現大部分人似乎對與較小的數據集沒有明顯的性能差異(3K左右的記錄)相同,但有運行的時候是一個細微的差別對較大數據集的查詢。

這裏是我的測試結果,使用80,000條記錄和查詢前10行中的5列,因此我的最終結果集爲50列+ Id列。我建議你自己測試一下,以決定哪一種最適合你和你的環境。

  • unpivoting並重新樞轉數據的bluefoot's answer平均最快在約12秒。我也喜歡這個答案,因爲我發現它最容易閱讀和維護。

  • Aaron's answerkoderoid's answer都建議使用MAX(CASE WHEN RowNumber = X THEN ...),並且在13秒左右時間後接近平均。

  • 使用多個PIVOT報表Rodney's answer平均約16秒,儘管它可能是用更少的PIVOT語句(我的測試中有5)速度更快。

  • 並且建議使用CTE和OUTER APPLYAaron's answer的前半部分是最慢的。我不知道需要多長時間才能運行,因爲我在2分鐘後取消了它,這是3k條記錄,3行和3列,而不是80k條記錄,10行和5列。

+1

發生了什麼事與'ddddd'和'eeeee'行? – Kermit

+0

@njk他們沒有包含在最終的結果集中,因爲我只想獲得最高的X記錄(在我的例子中,我使用3,但在我的實際查詢中我需要前10個記錄) – Rachel

+0

什麼是選擇「前10名」的標準? – Kermit

回答

7

你可以做一個UNPIVOT,然後將數據的PIVOT。這可以是靜態或動態進行:

靜態版本:

select * 
from 
(
    select fk, col + cast(rownumber as varchar(1)) new_col, 
    val 
    from 
    (
    select fk, rownumber, value, cast(type as varchar(10)) type, 
     status 
    from yourtable 
) x 
    unpivot 
    (
    val 
    for col in (value, type, status) 
) u 
) x1 
pivot 
(
    max(val) 
    for new_col in 
    ([value1], [type1], [status1], 
    [value2], [type2], [status2], 
    [value3], [type3]) 
) p 

看到SQL Fiddle with demo

動力版本,這將讓列的列表,以unpivot,然後pivot在運行時:

DECLARE @colsUnpivot AS NVARCHAR(MAX), 
    @query AS NVARCHAR(MAX), 
    @colsPivot as NVARCHAR(MAX) 

select @colsUnpivot = stuff((select ','+quotename(C.name) 
     from sys.columns as C 
     where C.object_id = object_id('yourtable') and 
       C.name not in ('fk', 'rownumber') 
     for xml path('')), 1, 1, '') 

select @colsPivot = STUFF((SELECT ',' 
         + quotename(c.name 
         + cast(t.rownumber as varchar(10))) 
        from yourtable t 
        cross apply 
         sys.columns as C 
        where C.object_id = object_id('yourtable') and 
         C.name not in ('fk', 'rownumber') 
        group by c.name, t.rownumber 
        order by t.rownumber 
      FOR XML PATH(''), TYPE 
      ).value('.', 'NVARCHAR(MAX)') 
     ,1,1,'') 


set @query 
    = 'select * 
     from 
     (
     select fk, col + cast(rownumber as varchar(10)) new_col, 
      val 
     from 
     (
      select fk, rownumber, value, cast(type as varchar(10)) type, 
      status 
      from yourtable 
     ) x 
     unpivot 
     (
      val 
      for col in ('+ @colsunpivot +') 
     ) u 
    ) x1 
     pivot 
     (
     max(val) 
     for new_col in 
      ('+ @colspivot +') 
    ) p' 

exec(@query) 

SQL Fiddle with Demo

兩者都會產生相同的結果,但是如果您不知道列數,則動態效果會很好。

動態版本正在假設rownumber已經是數據集的一部分。

+0

+1但'VARCHAR(1)'將打破值'10'。此外,我收到關於類型衝突的錯誤(您所使用的演示將rownumber作爲表中的列;它需要在運行時生成,我認爲)。所以我很難在演示之外進行測試。 –

+0

@AaronBertrand我以'varchar(1)'爲例,因爲現在只有1-5行號,所以需要擴展更長的值。我根據我提供的數據進行了演示,如果「rownumber」不存在,那麼在使用之前需要對其進行評估。我根據它提供的數據集顯示它可以通過'unpivot'然後'pivot'進行。 – Taryn

+0

在這裏和堆上,她解釋說她需要10套(她在樣本中只使用了3套)。 –

7

您可以嘗試在三個單獨的透視語句中執行透視。請這給一試:

SELECT Id 
    ,MAX(S1) [Status 1] 
    ,MAX(T1) [Type1] 
    ,MAX(V1) [Value1] 
    --, Add other columns 
FROM 
(
    SELECT Id, Value , Type, Status 
    , 'S' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Status, Type) AS VARCHAR(10)) [Status_RowNumber] 
    , 'T' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Status, Type) AS VARCHAR(10)) [Type_RowNumber] 
    , 'V' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Status, Type) AS VARCHAR(10)) [Value_RowNumber] 
    FROM MyTable 
) as T 
PIVOT 
( 
    MAX(Status) FOR Status_RowNumber IN ([S1], [S2], [S3],[S4],[S5],[S6],[S7],[S8],[S9],[S10]) 
)AS StatusPivot 
PIVOT(
    MAX(Type) FOR Type_RowNumber IN ([T1], [T2], [T3],[T4],[T5],[T6],[T7],[T8],[T9],[T10]) 
)AS Type_Pivot 
PIVOT(
    MAX(Value) FOR Value_RowNumber IN ([V1], [V2], [V3],[V4],[V5],[V6],[V7],[V8],[V9],[V10]) 
)AS Value_Pivot 
GROUP BY Id 

我不知道選擇前十的記錄標準的全部範圍,但這種生產和可能讓你更接近你的答案輸出。

SQL Fiddle Example

+3

+1,非常聰明。但請不要使用'VARCHAR'而不要使用長度。 http://sqlblog.com/blogs/aaron_bertrand/archive/2009/10/09/bad-habits-to-kick-declaring-varchar-without-length.aspx –

+0

我已經更新了我的語句以包含varchar長度。這是一篇非常有用的文章。 –

+0

通過使用第二個子查詢來派生row_number,可以使它更加整齊。在任何情況下都應該評估一次,但看起來會更漂亮。 :-) –

2

羅德尼的木裏支點是聰明的,這是肯定的。當你進入10X與3X區域時,這裏有兩個其他選擇當然不太吸引人。

;WITH a AS 
(
    SELECT Id, Value, Type, Status, 
     n = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY [Status], [Type]) 
    FROM dbo.MyTable 
) 
SELECT a.Id, 
Value1 = a.Value, Type1 = a.[Type], Status1 = a.[Status], 
Value2 = b.Value, Type2 = b.[Type], Status2 = b.[Status], 
Value3 = c.Value, Type3 = c.[Type], Status3 = c.[Status] 
FROM a 
OUTER APPLY (SELECT * FROM a AS T2 WHERE n = a.n + 1 AND id = a.id) AS b 
OUTER APPLY (SELECT * FROM a AS T2 WHERE n = b.n + 1 AND id = b.id) AS c 
WHERE a.n = 1 
ORDER BY a.Id; 

- 或 -

;WITH a AS 
(
    SELECT Id, Value, [Type], [Status], 
     n = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY [Status], [Type]) 
    FROM dbo.MyTable 
) 
SELECT Id, 
    Value1 = MAX(CASE WHEN n = 1 THEN Value END), 
    Type1 = MAX(CASE WHEN n = 1 THEN [Type] END), 
    Status1 = MAX(CASE WHEN n = 1 THEN [Status] END), 
    Value2 = MAX(CASE WHEN n = 2 THEN Value END), 
    Type2 = MAX(CASE WHEN n = 2 THEN [Type] END), 
    Status2 = MAX(CASE WHEN n = 2 THEN [Status] END), 
    Value3 = MAX(CASE WHEN n = 3 THEN Value END), 
    Type3 = MAX(CASE WHEN n = 3 THEN [Type] END), 
    Status3 = MAX(CASE WHEN n = 3 THEN [Status] END) 
FROM a 
GROUP BY Id 
ORDER BY a.Id; 
+0

在你的第一個場景中,你知道CTE是否被每個連接評估過嗎?這意味着如果我創建10個連接來獲得前10條記錄,CTE會被評估10次嗎? – Rachel

+0

@Rachel沒有測試就不可能知道,對不起。太多的變量可能決定CTE是否會被評估多次。 –

+0

CTE的第一個查詢絕對不會因爲性能而爲我工作,但第二個查詢具有體面的運行時間。如果你感興趣的話,我用我的測試結果更新了我的問題:) – Rachel

1

這可能適合你,雖然它不是優雅的。

select aa.FK_Id 
    , isnull(max(aa.Value1), '') as Value1 
    , isnull(max(aa.Type1), '') as Type1 
    , isnull(max(aa.Status1), '') as Status1 
    , isnull(max(aa.Value2), '') as Value2 
    , isnull(max(aa.Type2), '') as Type2 
    , isnull(max(aa.Status2), '') as Status2 
    , isnull(max(aa.Value3), '') as Value3 
    , isnull(max(aa.Type3), '') as Type3 
    , isnull(max(aa.Status3), '') as Status3 
from 
(  
    select FK_Id 
      , case when RowNumber = 1 then Value else null end as Value1 
      , case when RowNumber = 1 then [Type] else null end as Type1 
      , case when RowNumber = 1 then [Status] else null end as Status1 
      , case when RowNumber = 2 then Value else null end as Value2 
      , case when RowNumber = 2 then [Type] else null end as Type2 
      , case when RowNumber = 2 then [Status] else null end as Status2 
      , case when RowNumber = 3 then Value else null end as Value3 
      , case when RowNumber = 3 then [Type] else null end as Type3 
      , case when RowNumber = 3 then [Status] else null end as Status3 
    from Table1 
) aa 
group by aa.FK_Id 
+0

是的,這是我的備份計劃。我需要從前10個記錄中選擇5個列,這會導致50個案例陳述 – Rachel

+0

當您有兩個解決方案時,請保留我們的表現。謝謝:) –

+0

我用我的測試結果更新了我的問題:) – Rachel

1

嘗試這樣:

declare @rowCount int 
set @rowCount = 10 

declare @isNullClause varchar(4024) 
set @isnullClause = '' 
declare @caseClause varchar(4024) 
set @caseClause = '' 

declare @i int 
set @i = 1 

while(@i <= @rowCount) begin 
    set @isnullClause = @isNullClause + 
         ' , max(aa.Value' + CAST(@i as varchar(3)) + ') as Value' + CAST(@i as varchar(3)) + 
         ' , max(aa.Type' + CAST(@i as varchar(3)) + ') as Type' + CAST(@i as varchar(3)) + 
         ' , max(aa.Status' + CAST(@i as varchar(3)) + ') as Status' + CAST(@i as varchar(3)) + ' '; 
    set @caseClause = @caseClause + 
     ' , case when RowNumber = ' + CAST(@i as varchar(3)) + ' then Value else null end as Value' + CAST(@i as varchar(3)) + 
     ' , case when RowNumber = ' + CAST(@i as varchar(3)) + ' then Type else null end as Type' + CAST(@i as varchar(3)) + 
     ' , case when RowNumber = ' + CAST(@i as varchar(3)) + ' then Status else null end as Status' + CAST(@i as varchar(3)) + ' ' 


    set @i = @i + 1; 
end 

declare @sql nvarchar(4000) 
set @sql = 'select aa.FK_Id ' + @isnullClause + ' from (select FK_Id ' 
      + @caseClause + ' from Table1) aa group by aa.FK_Id ' 

exec SP_EXECUTESQL @sql 
+0

+1爲提供構建CASE WHEN版本查詢的動態方式:) – Rachel

相關問題