2013-04-01 42 views
244

Tôi đang tìm một cách hiệu quả để chuyển đổi các hàng thành các cột trong máy chủ SQL, tôi nghe nói rằng PIVOT không phải là rất nhanh và tôi cần phải xử lý nhiều bản ghi.Hiệu quả chuyển đổi các hàng thành các cột trong máy chủ sql

Đây là ví dụ của tôi:

---------------------------- 
    | Id | Value |ColumnName | 
    ---------------------------- 
    | 1 |John |FirstName | 
    | 2 |2.4 | Amount  | 
    | 3 |ZH1E4A |PostalCode | 
    | 4 |Fork |LastName  | 
    | 5 |857685 |AccountNumber| 
    ---------------------------- 

này được kết quả của tôi:

-------------------------------------------------------------------- 
|FirstName |Amount| PostalCode | LastName | AccountNumber| 
-------------------------------------------------------------------- 
|John  | 2.4 | ZH1E4A  | Fork  | 857685  | 
-------------------------------------------------------------------- 

Làm thế nào tôi có thể xây dựng kết quả?

Trả lời

370

Có một số cách để bạn có thể chuyển đổi dữ liệu từ nhiều hàng thành cột. Trong SQL Server bạn có thể sử dụng PIVOT chức năng để chuyển đổi dữ liệu từ hàng để cột:

select Firstname, Amount, PostalCode, LastName, AccountNumber 
from 
(
    select value, columnname 
    from yourtable 
) d 
pivot 
(
    max(value) 
    for columnname in (Firstname, Amount, PostalCode, LastName, AccountNumber) 
) piv; 

Xem Demo.

Nếu bạn có một số không rõ của columnnames mà bạn muốn transpose, sau đó bạn có thể sử dụng SQL động:

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

select @cols = STUFF((SELECT ',' + QUOTENAME(ColumnName) 
        from yourtable 
        group by ColumnName, id 
        order by id 
      FOR XML PATH(''), TYPE 
      ).value('.', 'NVARCHAR(MAX)') 
     ,1,1,'') 

set @query = N'SELECT ' + @cols + N' from 
      (
       select value, ColumnName 
       from yourtable 
      ) x 
      pivot 
      (
       max(value) 
       for ColumnName in (' + @cols + N') 
      ) p ' 

exec sp_executesql @query; 

Xem Demo.

Nếu bạn không muốn sử dụng PIVOT chức năng, sau đó bạn có thể sử dụng một chức năng tổng hợp với một biểu thức CASE:

select 
    max(case when columnname = 'FirstName' then value end) Firstname, 
    max(case when columnname = 'Amount' then value end) Amount, 
    max(case when columnname = 'PostalCode' then value end) PostalCode, 
    max(case when columnname = 'LastName' then value end) LastName, 
    max(case when columnname = 'AccountNumber' then value end) AccountNumber 
from yourtable 

Xem Demo.

Điều này cũng có thể được hoàn thành bằng cách sử dụng nhiều kết nối, nhưng bạn sẽ cần một số cột để liên kết từng hàng mà bạn không có trong dữ liệu mẫu của mình. Nhưng cú pháp cơ bản sẽ là:

select fn.value as FirstName, 
    a.value as Amount, 
    pc.value as PostalCode, 
    ln.value as LastName, 
    an.value as AccountNumber 
from yourtable fn 
left join yourtable a 
    on fn.somecol = a.somecol 
    and a.columnname = 'Amount' 
left join yourtable pc 
    on fn.somecol = pc.somecol 
    and pc.columnname = 'PostalCode' 
left join yourtable ln 
    on fn.somecol = ln.somecol 
    and ln.columnname = 'LastName' 
left join yourtable an 
    on fn.somecol = an.somecol 
    and an.columnname = 'AccountNumber' 
where fn.columnname = 'Firstname' 
+4

+1. . . Nhưng trong ví dụ cuối cùng, bạn có thể sử dụng 'cross join' thay vì' left join' vì mỗi truy vấn con trả về một hàng. –

+0

Tôi cần tạo truy vấn động vì tôi không biết số hàng. Cho phép nói về việc chuyển đổi một bảng với 10.000.000 triệu bản ghi – tbag

+7

@tbag Nếu bạn có một số hàng không xác định, thì bạn sẽ phải sử dụng sql động nhưng lưu ý rằng việc chuyển đổi hàng triệu hàng sẽ không hiệu quả. – Taryn

4

vì dữ liệu xoay vòng vẫn là quyết định nóng tôi quyết định thêm thứ gì đó vào tôi. Đây là một phương pháp chứ không phải chỉ là một tập lệnh duy nhất nhưng mang lại cho bạn nhiều khả năng hơn. Trước hết Có 3 kịch bản bạn cần triển khai: 1) Kiểu TABLE do người dùng định nghĩa [ColumnActionList] -> giữ dữ liệu làm tham số 2) SP [proc_PivotPrepare] -> chuẩn bị dữ liệu của chúng tôi 3) SP [proc_PivotExecute] -> execute kịch bản

CREATE TYPE [dbo].[ColumnActionList] AS TABLE(
     [ID] [smallint] NOT NULL, 
     [ColumnName] [nvarchar](128) NOT NULL, 
     [Action] [nchar](1) NOT NULL 
    ); 
GO 

    CREATE PROCEDURE [dbo].[proc_PivotPrepare] 
    (
    @DB_Name  nvarchar(128), 
    @TableName  nvarchar(128) 
    ) 
    AS 
    ---------------------------------------------------------------------------------------------------- 
    -----| Author: Bartosz 
    ---------------------------------------------------------------------------------------------------- 
    SELECT @DB_Name = ISNULL(@DB_Name,db_name()) 
    DECLARE @SQL_Code nvarchar(max) 

    DECLARE @MyTab TABLE (ID smallint identity(1,1), [Column_Name] nvarchar(128), [Type] nchar(1), [Set Action SQL] nvarchar(max)); 

    SELECT @SQL_Code  = 'SELECT [<| SQL_Code |>] = '' '' ' 
             + 'UNION ALL ' 
             + 'SELECT ''----------------------------------------------------------------------------------------------------'' ' 
             + 'UNION ALL ' 
             + 'SELECT ''-----| Declare user defined type [ID]/[ColumnName]/[PivotAction] '' ' 
             + 'UNION ALL ' 
             + 'SELECT ''----------------------------------------------------------------------------------------------------'' ' 
             + 'UNION ALL ' 
             + 'SELECT ''DECLARE @ColumnListWithActions ColumnActionList;''' 
             + 'UNION ALL ' 
             + 'SELECT ''----------------------------------------------------------------------------------------------------'' ' 
             + 'UNION ALL ' 
             + 'SELECT ''-----| Set [PivotAction] (''''S'''' as default) to select dimentions and values '' ' 
             + 'UNION ALL ' 
             + 'SELECT ''-----|''' 
             + 'UNION ALL ' 
             + 'SELECT ''-----| ''''S'''' = Stable column || ''''D'''' = Dimention column || ''''V'''' = Value column '' ' 
             + 'UNION ALL ' 
             + 'SELECT ''----------------------------------------------------------------------------------------------------'' ' 
             + 'UNION ALL ' 
             + 'SELECT ''INSERT INTO @ColumnListWithActions VALUES ('' + CAST(ROW_NUMBER() OVER (ORDER BY [NAME]) as nvarchar(10)) + '', '' + '''''''' + [NAME] + ''''''''+ '', ''''S'''');''' 
             + 'FROM [' + @DB_Name + '].sys.columns ' 
             + 'WHERE object_id = object_id(''[' + @DB_Name + ']..[' + @TableName + ']'') ' 
             + 'UNION ALL ' 
             + 'SELECT ''----------------------------------------------------------------------------------------------------'' ' 
             + 'UNION ALL ' 
             + 'SELECT ''-----| Execute sp_PivotIT with parameters: columns and dimentions and main table name'' ' 
             + 'UNION ALL ' 
             + 'SELECT ''----------------------------------------------------------------------------------------------------'' ' 
             + 'UNION ALL ' 
             + 'SELECT ''EXEC [dbo].[sp_PivotIt] @ColumnListWithActions, ' + '''''' + @TableName + '''''' + ';''' 
             + 'UNION ALL ' 
             + 'SELECT ''----------------------------------------------------------------------------------------------------'' '        
    EXECUTE SP_EXECUTESQL @SQL_Code; 

GO 
CREATE PROCEDURE [dbo].[proc_PivotExecute] 
(
@ColumnListWithActions ColumnActionList ReadOnly 
,@TableName      nvarchar(128) 
) 
AS 
--####################################################################################################################### 
--###| Author: Bartosz 
--####################################################################################################################### 


--####################################################################################################################### 
--###| Step 1 - Select our user-defined-table-variable into temp table 
--####################################################################################################################### 

IF OBJECT_ID('tempdb.dbo.#ColumnListWithActions', 'U') IS NOT NULL DROP TABLE #ColumnListWithActions; 
SELECT * INTO #ColumnListWithActions FROM @ColumnListWithActions; 

--####################################################################################################################### 
--###| Step 2 - Preparing lists of column groups as strings: 
--####################################################################################################################### 

DECLARE @ColumnName      nvarchar(128) 
DECLARE @Destiny      nchar(1) 

DECLARE @ListOfColumns_Stable   nvarchar(max) 
DECLARE @ListOfColumns_Dimension nvarchar(max) 
DECLARE @ListOfColumns_Variable  nvarchar(max) 
--############################ 
--###| Cursor for List of Stable Columns 
--############################ 

DECLARE ColumnListStringCreator_S CURSOR FOR 
SELECT  [ColumnName] 
FROM  #ColumnListWithActions 
WHERE  [Action] = 'S' 
OPEN ColumnListStringCreator_S; 
FETCH NEXT FROM ColumnListStringCreator_S 
INTO @ColumnName 
    WHILE @@FETCH_STATUS = 0 

    BEGIN 
     SELECT @ListOfColumns_Stable = ISNULL(@ListOfColumns_Stable, '') + ' [' + @ColumnName + '] ,'; 
     FETCH NEXT FROM ColumnListStringCreator_S INTO @ColumnName 
    END 

CLOSE ColumnListStringCreator_S; 
DEALLOCATE ColumnListStringCreator_S; 

--############################ 
--###| Cursor for List of Dimension Columns 
--############################ 

DECLARE ColumnListStringCreator_D CURSOR FOR 
SELECT  [ColumnName] 
FROM  #ColumnListWithActions 
WHERE  [Action] = 'D' 
OPEN ColumnListStringCreator_D; 
FETCH NEXT FROM ColumnListStringCreator_D 
INTO @ColumnName 
    WHILE @@FETCH_STATUS = 0 

    BEGIN 
     SELECT @ListOfColumns_Dimension = ISNULL(@ListOfColumns_Dimension, '') + ' [' + @ColumnName + '] ,'; 
     FETCH NEXT FROM ColumnListStringCreator_D INTO @ColumnName 
    END 

CLOSE ColumnListStringCreator_D; 
DEALLOCATE ColumnListStringCreator_D; 

--############################ 
--###| Cursor for List of Variable Columns 
--############################ 

DECLARE ColumnListStringCreator_V CURSOR FOR 
SELECT  [ColumnName] 
FROM  #ColumnListWithActions 
WHERE  [Action] = 'V' 
OPEN ColumnListStringCreator_V; 
FETCH NEXT FROM ColumnListStringCreator_V 
INTO @ColumnName 
    WHILE @@FETCH_STATUS = 0 

    BEGIN 
     SELECT @ListOfColumns_Variable = ISNULL(@ListOfColumns_Variable, '') + ' [' + @ColumnName + '] ,'; 
     FETCH NEXT FROM ColumnListStringCreator_V INTO @ColumnName 
    END 

CLOSE ColumnListStringCreator_V; 
DEALLOCATE ColumnListStringCreator_V; 

SELECT @ListOfColumns_Variable  = LEFT(@ListOfColumns_Variable, LEN(@ListOfColumns_Variable) - 1); 
SELECT @ListOfColumns_Dimension = LEFT(@ListOfColumns_Dimension, LEN(@ListOfColumns_Dimension) - 1); 
SELECT @ListOfColumns_Stable   = LEFT(@ListOfColumns_Stable, LEN(@ListOfColumns_Stable) - 1); 

--####################################################################################################################### 
--###| Step 3 - Preparing table with all possible connections between Dimension columns excluding NULLs 
--####################################################################################################################### 
DECLARE @DIM_TAB TABLE ([DIM_ID] smallint, [ColumnName] nvarchar(128)) 
INSERT INTO @DIM_TAB 
SELECT [DIM_ID] = ROW_NUMBER() OVER(ORDER BY [ColumnName]), [ColumnName] FROM #ColumnListWithActions WHERE [Action] = 'D'; 

DECLARE @DIM_ID smallint; 
SELECT  @DIM_ID = 1; 


DECLARE @SQL_Dimentions nvarchar(max); 

IF OBJECT_ID('tempdb.dbo.##ALL_Dimentions', 'U') IS NOT NULL DROP TABLE ##ALL_Dimentions; 

SELECT @SQL_Dimentions  = 'SELECT ID = ROW_NUMBER() OVER (ORDER BY ' + @ListOfColumns_Dimension + '), ' + @ListOfColumns_Dimension 
              + ' INTO ##ALL_Dimentions ' 
              + ' FROM (SELECT DISTINCT' + @ListOfColumns_Dimension + ' FROM ' + @TableName 
              + ' WHERE ' + (SELECT [ColumnName] FROM @DIM_TAB WHERE [DIM_ID] = @DIM_ID) + ' IS NOT NULL '; 
              SELECT @DIM_ID = @DIM_ID + 1; 
      WHILE @DIM_ID <= (SELECT MAX([DIM_ID]) FROM @DIM_TAB) 
      BEGIN 
      SELECT @SQL_Dimentions = @SQL_Dimentions + 'AND ' + (SELECT [ColumnName] FROM @DIM_TAB WHERE [DIM_ID] = @DIM_ID) + ' IS NOT NULL '; 
      SELECT @DIM_ID = @DIM_ID + 1; 
      END 

SELECT @SQL_Dimentions = @SQL_Dimentions + ')x'; 

EXECUTE SP_EXECUTESQL @SQL_Dimentions; 

--####################################################################################################################### 
--###| Step 4 - Preparing table with all possible connections between Stable columns excluding NULLs 
--####################################################################################################################### 
DECLARE @StabPos_TAB TABLE ([StabPos_ID] smallint, [ColumnName] nvarchar(128)) 
INSERT INTO @StabPos_TAB 
SELECT [StabPos_ID] = ROW_NUMBER() OVER(ORDER BY [ColumnName]), [ColumnName] FROM #ColumnListWithActions WHERE [Action] = 'S'; 

DECLARE @StabPos_ID smallint; 
SELECT  @StabPos_ID = 1; 


DECLARE @SQL_MainStableColumnTable nvarchar(max); 

IF OBJECT_ID('tempdb.dbo.##ALL_StableColumns', 'U') IS NOT NULL DROP TABLE ##ALL_StableColumns; 

SELECT @SQL_MainStableColumnTable  = 'SELECT ID = ROW_NUMBER() OVER (ORDER BY ' + @ListOfColumns_Stable + '), ' + @ListOfColumns_Stable 
              + ' INTO ##ALL_StableColumns ' 
              + ' FROM (SELECT DISTINCT' + @ListOfColumns_Stable + ' FROM ' + @TableName 
              + ' WHERE ' + (SELECT [ColumnName] FROM @StabPos_TAB WHERE [StabPos_ID] = @StabPos_ID) + ' IS NOT NULL '; 
              SELECT @StabPos_ID = @StabPos_ID + 1; 
      WHILE @StabPos_ID <= (SELECT MAX([StabPos_ID]) FROM @StabPos_TAB) 
      BEGIN 
      SELECT @SQL_MainStableColumnTable = @SQL_MainStableColumnTable + 'AND ' + (SELECT [ColumnName] FROM @StabPos_TAB WHERE [StabPos_ID] = @StabPos_ID) + ' IS NOT NULL '; 
      SELECT @StabPos_ID = @StabPos_ID + 1; 
      END 

SELECT @SQL_MainStableColumnTable = @SQL_MainStableColumnTable + ')x'; 

EXECUTE SP_EXECUTESQL @SQL_MainStableColumnTable; 

--####################################################################################################################### 
--###| Step 5 - Preparing table with all options ID 
--####################################################################################################################### 

DECLARE @FULL_SQL_1 NVARCHAR(MAX) 
SELECT @FULL_SQL_1 = '' 

DECLARE @i smallint 

IF OBJECT_ID('tempdb.dbo.##FinalTab', 'U') IS NOT NULL DROP TABLE ##FinalTab; 

SELECT @FULL_SQL_1 = 'SELECT t.*, dim.[ID] ' 
            + ' INTO ##FinalTab ' 
            + 'FROM ' + @TableName + ' t ' 
            + 'JOIN ##ALL_Dimentions dim ' 
            + 'ON t.' + (SELECT [ColumnName] FROM @DIM_TAB WHERE [DIM_ID] = 1) + ' = dim.' + (SELECT [ColumnName] FROM @DIM_TAB WHERE [DIM_ID] = 1); 
           SELECT @i = 2        
           WHILE @i <= (SELECT MAX([DIM_ID]) FROM @DIM_TAB) 
            BEGIN 
            SELECT @FULL_SQL_1 = @FULL_SQL_1 + ' AND t.' + (SELECT [ColumnName] FROM @DIM_TAB WHERE [DIM_ID] = @i) + ' = dim.' + (SELECT [ColumnName] FROM @DIM_TAB WHERE [DIM_ID] = @i) 
            SELECT @i = @i +1 
           END 
EXECUTE SP_EXECUTESQL @FULL_SQL_1 

--####################################################################################################################### 
--###| Step 6 - Selecting final data 
--####################################################################################################################### 
DECLARE @STAB_TAB TABLE ([STAB_ID] smallint, [ColumnName] nvarchar(128)) 
INSERT INTO @STAB_TAB 
SELECT [STAB_ID] = ROW_NUMBER() OVER(ORDER BY [ColumnName]), [ColumnName] 
FROM #ColumnListWithActions WHERE [Action] = 'S'; 

DECLARE @VAR_TAB TABLE ([VAR_ID] smallint, [ColumnName] nvarchar(128)) 
INSERT INTO @VAR_TAB 
SELECT [VAR_ID] = ROW_NUMBER() OVER(ORDER BY [ColumnName]), [ColumnName] 
FROM #ColumnListWithActions WHERE [Action] = 'V'; 

DECLARE @y smallint; 
DECLARE @x smallint; 
DECLARE @z smallint; 


DECLARE @FinalCode nvarchar(max) 

SELECT @FinalCode = ' SELECT ID1.*' 
             SELECT @y = 1 
             WHILE @y <= (SELECT MAX([ID]) FROM ##FinalTab) 
              BEGIN 
               SELECT @z = 1 
               WHILE @z <= (SELECT MAX([VAR_ID]) FROM @VAR_TAB) 
                BEGIN 
                 SELECT @FinalCode = @FinalCode + ', [ID' + CAST((@y) as varchar(10)) + '.' + (SELECT [ColumnName] FROM @VAR_TAB WHERE [VAR_ID] = @z) + '] = ID' + CAST((@y + 1) as varchar(10)) + '.' + (SELECT [ColumnName] FROM @VAR_TAB WHERE [VAR_ID] = @z) 
                 SELECT @z = @z + 1 
                END 
                SELECT @y = @y + 1 
               END 
     SELECT @FinalCode = @FinalCode + 
             ' FROM (SELECT * FROM ##ALL_StableColumns)ID1'; 
             SELECT @y = 1 
             WHILE @y <= (SELECT MAX([ID]) FROM ##FinalTab) 
             BEGIN 
              SELECT @x = 1 
              SELECT @FinalCode = @FinalCode 
                       + ' LEFT JOIN (SELECT ' + @ListOfColumns_Stable + ' , ' + @ListOfColumns_Variable 
                       + ' FROM ##FinalTab WHERE [ID] = ' 
                       + CAST(@y as varchar(10)) + ')ID' + CAST((@y + 1) as varchar(10)) 
                       + ' ON 1 = 1' 
                       WHILE @x <= (SELECT MAX([STAB_ID]) FROM @STAB_TAB) 
                       BEGIN 
                        SELECT @FinalCode = @FinalCode + ' AND ID1.' + (SELECT [ColumnName] FROM @STAB_TAB WHERE [STAB_ID] = @x) + ' = ID' + CAST((@y+1) as varchar(10)) + '.' + (SELECT [ColumnName] FROM @STAB_TAB WHERE [STAB_ID] = @x) 
                        SELECT @x = @x +1 
                       END 
              SELECT @y = @y + 1 
             END 

SELECT * FROM ##ALL_Dimentions; 
EXECUTE SP_EXECUTESQL @FinalCode; 
--####################################################################################################################### 

từ thực hiện truy vấn đầu tiên (bằng cách thông qua nguồn DB và tên bảng), bạn sẽ nhận được một truy vấn thực hiện trước khi tạo ra cho SP thứ hai, tất cả các bạn phải làm là xác định là cột từ nguồn của bạn : + Ổn định + Giá trị (sẽ được sử dụng để tập trung các giá trị dựa trên đó) + Dim (cột bạn muốn sử dụng để xoay)

Tên và kiểu dữ liệu sẽ được xác định tự động!

Tôi không thể giới thiệu nó cho bất kỳ môi trường sản xuất nào nhưng thực hiện công việc cho các yêu cầu BI adhoc BI.

Các vấn đề liên quan