2013-12-12 52 views
2

我一直在嘗試使用SQL Server樞軸一段時間,但我似乎並沒有得到正確的。我讀過一堆的答案,但不明白數據透視是如何工作的。我需要平鋪SQL Server行到列使用樞軸

我在寫存儲過程。我有表1(作爲TVP收到),並且需要使其看起來像表2(see this image for tables)。

重要說明:Table1.valueTypeID中的值不能硬編碼到邏輯中,因爲它們總是可以更改。因此,邏輯必須是超級動態的。

請參閱下面的代碼。數據透視表位於存儲過程的末尾。

  -- Create date: 12/10/2013 
      -- Description: select all the contacts associated with received accountPassport 
      -- ============================================= 
      ALTER PROCEDURE [dbo].[selectContactsPropsByAccountPassport] 
       -- Add the parameters for the stored procedure here 
       @accountPassport int, 
       @valueTypeFiltersTVP valueTypeFiltersTVP READONLY 
      AS 
      BEGIN 
       -- SET NOCOUNT ON added to prevent extra result sets from 
       -- interfering with SELECT statements. 
       SET NOCOUNT ON; 

       -- Insert statements for procedure here 
       DECLARE @accountID int; 
       DECLARE @contactsAppAccountPassport int; 
       DECLARE @searchResults TABLE 
        (
         resultContactID int 
        ); 
       DECLARE @resultContactID int; 

       DECLARE @contactsPropsForReturn TABLE 
        (
         contactID int, 
         valueTypeID int, 
         value varchar(max) 
        ); 

       create table #contactsPropsForReturnFiltered(contactID int,valueTypeID int, value varchar(max)) 

       /* 
       DECLARE #contactsPropsForReturnFiltered TABLE 
        (
         contactID int, 
         valueTypeID int, 
         value varchar(max) 
        ); 
       */ 

      --2. get @contactsAppAccountPassport associated with recieved @accountPassport 
       -- go into dbo.accounts and get the @accountID associated with this @accountPassport 
       SELECT 
        @accountID = ID 
       FROM 
        dbo.accounts 
       WHERE 
        passport = @accountPassport 

       -- go into dbo.accountsProps and get the value (@contactsAppAccountPassport) where valueType=42 and accountID = @accountID 
       SELECT 
        @contactsAppAccountPassport = value 
       FROM 
        dbo.accountsProps 
       WHERE 
        (valueTypeID=42) AND (accountID = @accountID) 

      --3. get all the contact ID's from dbo.contacts associated with @contactsAppAccountPassport 
       INSERT INTO 
        @searchResults 
       SELECT 
        ID 
       FROM 
        dbo.contacts 
       WHERE 
        contactsAppAccountPassport = @contactsAppAccountPassport 

      --4. Get the props of all contact ID's from 3. 

       --start for each loop....our looping object is @resultContactID row. if there are more rows, we keep looping. 
       DECLARE searchCursor CURSOR FOR 
        SELECT 
         resultContactID 
        FROM 
         @searchResults 

        OPEN searchCursor 

         FETCH NEXT FROM searchCursor INTO @resultContactID 

         WHILE (@@FETCH_STATUS=0) 
         BEGIN 
          INSERT INTO 
           @contactsPropsForReturn 
          SELECT 
           contactID, 
           valueTypeID, 
           value 
          FROM 
           dbo.contactsProps 
          WHERE 
           contactID = @resultContactID 
          FETCH NEXT FROM searchCursor INTO @resultContactID 
         END --end of WHILE loop 

        --end of cursor (both CLOSE and DEALLOCATE necessary) 
        CLOSE searchCursor 
        DEALLOCATE searchCursor 

        -- select and return only the props that match with the requested props 
        -- (we don't want to return all the props, only the ones requested) 
        INSERT INTO 
         #contactsPropsForReturnFiltered 
        SELECT 
         p.contactID, 
         p.valueTypeID, 
         p.value 
        FROM 
         @contactsPropsForReturn as p 
        INNER JOIN 
         @valueTypeFiltersTVP as f 
        ON 
         p.valueTypeID = f.valueTypeID 



        DECLARE @cols AS NVARCHAR(MAX), 
        @query AS NVARCHAR(MAX); 

        SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(ValueTypeId) 
         FROM #contactsPropsForReturnFiltered 
         FOR XML PATH(''), TYPE 
         ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,''); 

      set @query = 'SELECT contactid, ' + @cols + ' from 
         (
          select contactid 
           , Value 
           ,ValueTypeId 
          from #contactsPropsForReturnFiltered 
         ) x 
         pivot 
         (
          min(Value) 
          for ValueTypeId in (' + @cols + ') 
         ) p '; 

      execute(@query); 

      END 
+0

你嘗試過什麼? –

+0

@GordonLinoff我用遊標動態地構建了新表,但是在工作中的每個人都說遊標很貴,應該避免。對此有何看法? – quelquecosa

+0

遊標通常很貴,應該避免,特別是當查詢可以完成這項工作時。在MS SQL文檔中查找'pivot'來看看它是如何工作的。 –

回答

2

您需要在您的情況下使用動態數據透視表。請嘗試以下操作

create table table1 
(
    contactid int, 
    ValueTypeId int, 
    Value varchar(100) 
); 

insert into table1 values (56064, 40, 'Issac'); 
insert into table1 values (56064, 34, '(123)456-7890'); 
insert into table1 values (56065, 40, 'Lola'); 
insert into table1 values (56065, 34, '(123)456-7832'); 
insert into table1 values (56068, 40, 'Mike'); 
insert into table1 values (56068, 41, 'Gonzalez'); 
insert into table1 values (56068, 34, '(123)456-7891'); 


DECLARE @cols AS NVARCHAR(MAX), 
    @query AS NVARCHAR(MAX); 

SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(ValueTypeId) 
      FROM table1 
      FOR XML PATH(''), TYPE 
      ).value('.', 'NVARCHAR(MAX)') 
     ,1,1,''); 

set @query = 'SELECT contactid, ' + @cols + ' from 
      (
       select contactid 
        , Value 
        ,ValueTypeId 
       from table1 
      ) x 
      pivot 
      (
       min(Value) 
       for ValueTypeId in (' + @cols + ') 
      ) p '; 


execute(@query); 

drop table table1 
+0

我試過使用你的解決方案,但我的C#服務器拋出一個奇怪的錯誤,說「必須聲明表變量@contactsPropsForReturnFilter」。我已經用存儲過程的代碼更新了我的問題。數據透視表(您的代碼,已修改)位於存儲過程的末尾。你可以看一下嗎? – quelquecosa

+0

得到它的工作!必須將我的@ contactsPropsForReturnFiltered表變量更改爲#contactsPropsForReturnFiltered臨時表。從我讀的,它不可能執行引用@ tableVariable的@query字符串,因爲@ tableVariable不在其範圍內。真棒。愛知識。 – quelquecosa

+0

這是不正確的。在SQL 2008及更高版本中,您可以在動態SQL中使用表值參數。查看我更新的答案以獲取更多信息。 – ErikE

0

爲什麼您需要以這種方式顯示數據?

在許多情況下,客戶端比數據庫引擎在旋轉方面更好。例如,SQL Server Reporting Services可以通過矩陣控制輕鬆實現。同樣,如果你正在編寫一個網頁,例如Asp.Net,你可以快速運行記錄集,將你的數據傳遞給一個新的數據表示(同時收集唯一的值),然後通過新的數據對象吐出HTML來呈現結果。

如果可能的話,請讓客戶端代替服務器執行pivoting操作。

UPDATE:

如果你真的想使用動態SQL表變量,你可以在SQL Server 2008和了就好了。這裏有一個腳本示例:

USE tempdb 
GO 
CREATE TYPE IDList AS TABLE (
    ID int 
); 
GO 
DECLARE @SQL nvarchar(max); 
SET @SQL = 'SELECT * FROM @TransactionIDs WHERE ID >= 4;' 
DECLARE @TransactionIDs IDLIst; 
INSERT @TransactionIDs VALUES (1), (2), (4), (8), (16); 
EXEC sp_executesql @SQL, N'@TransactionIDs IDList READONLY', @TransactionIDs; 
GO 
DROP TYPE IDList; 
+0

我同意http://michaeljswart.com/2011/06/forget-about-pivot/ –

+0

數據永遠不會讓它回到Web客戶端。在這種情況下,它會返回到在IIS服務器上運行的c#應用程序。根據我的理解,你認爲最好是在c#應用程序而不是SQL服務器上重新組織數據。 你能給出這個意見的理由嗎? – quelquecosa

+0

@quelquecosa我建議這樣做的原因有兩個:1)它是一個演示文稿問題; 2)SQL Server不會自動旋轉(它無法自動從您的數據中發現列名,例如MS Access可以在其數據透視查詢中)。由於在C#中的旋轉並不那麼困難,我的建議是使用一種工具來避免動態SQL的陷阱和缺陷。 – ErikE