【问题标题】:SQL - query for combining multiple rowsSQL - 用于组合多行的查询
【发布时间】:2022-10-15 01:22:16
【问题描述】:

我正在搜索,但找不到解决问题的方法。 我有一张包含这些数据的表格(比如 T1):

SampleCode Name Content
# 1 A#
# 2 B#
# 3 C#
@ 1 A@
@ 2 B@
@ 3 C@

所以我需要一个选择查询,结果是:

Column 1 Column 2 Column 3
SampleCode # @
Name.1 A# A@
Name.2 B# B@
Name.3 C# C@

有人有提示吗?

谢谢

法比奥

【问题讨论】:

  • 你用的是什么dbms?
  • 另外,虽然您的结果是可能的,但将第一行作为标题不是更有意义吗?

标签: sql select row


【解决方案1】:

查看本文中的 2 个选项:https://www.sqlshack.com/multiple-options-to-transposing-rows-into-columns/

如果使用 TSQL,您可以使用 TRANSPOSE 语句。或者,您可以使用游标/循环和动态 SQL 创建结果集。

全动态解决方案

我不敢相信我敲定了这个...也许我这样做只是为了看看我是否可以。它又长又复杂,并且使用了几个循环。我认为你应该重新评估你正在尝试做的事情。这不能在视图中运行。您将不得不放入一个函数或存储过程。您可能需要评估临时表名称以查看它们是否与您的环境兼容。此外,由于临时表名称不是唯一的,因此您不能运行多个实例;如果要运行多个实例,则必须将唯一标识符添加到临时表中的列或临时表名称。这是完全动态的……您不必提前知道需要多少列。

--**************************************************
--Header area... sample data and temp tables.
--**************************************************
--Clean up sample and result tables if they exist.
IF OBJECT_ID(N'tempdb..#T1') IS NOT NULL  
   DROP TABLE #T1;  

IF OBJECT_ID(N'tempdb..#ResultsTemp') IS NOT NULL  
   DROP TABLE #ResultsTemp;  

IF OBJECT_ID(N'tempdb..#codeTable') IS NOT NULL  
   DROP TABLE #codeTable;  

--For Debugging.
DECLARE @debug int = 1; --0=no debug, 1=show debug messages, do not exec all SQL statements.

--Table var to hold sample data.
CREATE TABLE #T1 (
    SampleCode nvarchar(50),
    [Name] nvarchar(50),
    Content nvarchar(50),
    row_num int
);

--Load Sample Data.
INSERT INTO #T1 (SampleCode, [Name], Content)
VALUES 
    ('#', '1', 'A#')
    , ('#', '2', 'B#')
    , ('#', '3', 'C#')
    , ('@', '1', 'A@')
    , ('@', '2', 'B@')
    , ('@', '3', 'C@')
;

--**********END HEADER**********



--Number the rows so we can loop over them.
UPDATE #T1 
SET row_num = newT1.row_num
FROM  
(
    SELECT t12.SampleCode
        , t12.[Name]
        , T12.Content
        , ROW_NUMBER()OVER(ORDER BY SampleCode, [Name], Content) as row_num
    FROM #T1 as t12
) AS newT1
WHERE #T1.SampleCode = newT1.SampleCode
    AND #T1.[Name] = newT1.[Name]
    AND #T1.Content = newT1.Content
;
SELECT * FROM #T1;  --Debugging... just show the contents of #T1 after adding row_num

--Create temp table to load results.
CREATE TABLE #ResultsTemp (
    Column1 nvarchar(50)
);

--Create some variable to track looping (without a cursor).
DECLARE @loopCount int = 1;
DECLARE @maxLoops int = (SELECT COUNT(DISTINCT SampleCode) FROM #T1);
DECLARE @sql nvarchar(512);

--Add columns to #ResultsTable dynamically
WHILE (@loopCount <= (@maxLoops))
BEGIN
    SET @sql = 'ALTER TABLE #ResultsTemp ADD ' + QUOTENAME('Column' + CAST((@loopCount + 1) as nvarchar)) + ' nvarchar(50) NULL';
    IF (@debug = 1) PRINT @sql;
    EXEC (@sql);

    SET @loopCount = @loopCount + 1;
END

--************************************************************
--SECTION FOR INSERTING FIRST ROW for "SampleCode"
--************************************************************
--Vars for tracking the SampleCode variations processed.
DECLARE @sampleLoop int = 1;
DECLARE @sampleCount int = (SELECT COUNT(DISTINCT SampleCode) FROM #T1);
CREATE TABLE #codeTable (
    SampleCode nvarchar(50)
    , row_num int
);

--Create a list of unique SampleCodes to loop over.
INSERT INTO #codeTable (SampleCode)
SELECT DISTINCT 
    SampleCode 
FROM #T1;

UPDATE #codeTable 
SET row_num = newCT.row_num
FROM  
(
    SELECT ct2.SampleCode
        , ROW_NUMBER()OVER(ORDER BY SampleCode) as row_num
    FROM #codeTable as ct2
) AS newCT
WHERE #codeTable.SampleCode = newCT.SampleCode
;

SET @sql = 'INSERT INTO #ResultsTemp (Column1) SELECT ''SampleCode''';
IF (@debug = 1) PRINT @sql;
EXEC (@sql);

WHILE (@sampleLoop <= @sampleCount)
BEGIN
    SET @sql = 'UPDATE #ResultsTemp SET Column' + CAST(@SampleLoop + 1 as nvarchar) + '=(SELECT TOP 1 SampleCode FROM #codeTable WHERE row_num = ' + CAST(@sampleLoop as nvarchar) + ');';
    IF (@debug = 1) PRINT @sql;
    EXEC (@sql);
    SET @sampleLoop = @sampleLoop + 1;
END

IF (@debug = 1) SELECT * FROM #ResultsTemp;

--**********END SECTION**********


--**************************************************
--SECTION FOR INSERTING THE REST OF THE CONTENT DATA
--**************************************************

--Vars for tracking number of rows processed from T1.
SET @loopCount = 1;
SET @maxLoops = (SELECT MAX(row_num) FROM #T1);

--Loop over each row in T1.
WHILE (@loopCount <= @maxLoops)
BEGIN
    --Create a name for this row.
    DECLARE @nameRaw nvarchar(50) = (SELECT TOP 1 [Name] FROM #T1 WHERE row_num = @loopCount);
    DECLARE @codeNum nvarchar(50) = (
        SELECT TOP 1 ct.row_num 
        FROM #T1 as t
            INNER JOIN #codeTable as ct
                ON ct.SampleCode = t.SampleCode
            WHERE t.row_num = @loopCount);
    DECLARE @name nvarchar(50) = 'Name.' + @nameRaw;

    --First insert a row for this Name... if not already in the list.
    SET @sql = 'INSERT INTO #ResultsTemp (Column1)
        SELECT ''Name.'' + t.[Name]
        FROM #T1 as t
            LEFT OUTER JOIN #ResultsTemp as rt
                ON rt.Column1 = ''' + @name + '''
        WHERE t.row_num = ' + CAST(@loopCount as nvarchar) + '
            AND rt.Column1 IS NULL;';
    IF (@debug = 1) PRINT @sql;
    EXEC (@sql);

    --Update this Name row with the "content".
    SET @sql = 'UPDATE rt 
        SET Column' + CAST(@codeNum + 1 as nvarchar) + '=t.Content
        FROM #ResultsTemp as rt
            INNER JOIN #T1 as t
                ON t.row_num = ' + CAST(@loopCount as nvarchar) + '
                AND t.[Name] = ''' + @nameRaw + '''
        WHERE rt.Column1 = ''' + @name + ''';';
    IF (@debug = 1) PRINT @sql;
    EXEC (@sql);

    SET @loopCount = @loopCount + 1;
END

--Show everything in the temp Results table.
SELECT *
FROM #ResultsTemp;

结果集:

静态选择解决方案

下一个选项不是动态的。您必须知道需要多少列,然后您可以将“Column3”代码复制到任何新列。您必须更改列名并更新“Select TOP 1”语句,如您要复制的每个新列的注释。

WITH CodeTable AS (
    SELECT DISTINCT t.SampleCode
    FROM #T1 as t
)
SELECT DISTINCT
    'Name.' + [Name]
    , (
        SELECT TOP 1 Content 
        FROM #T1 as t2
        WHERE t2.SampleCode = (
            SELECT TOP 1 SampleCode 
            FROM CodeTable as ct
            ORDER BY SampleCode
        )
            AND t2.[Name] = t.[Name]
      ) as Column2
    , (
        SELECT TOP 1 Content 
        FROM #T1 as t2
        WHERE t2.SampleCode = (
            SELECT TOP 1 SampleCode 
            FROM CodeTable as ct
            WHERE ct.SampleCode NOT IN (
                SELECT TOP 1 SampleCode --Update the TOP 1 to be TOP [number of previous columns]
                FROM CodeTable as ct2
                ORDER BY ct2.SampleCode
            )
            ORDER BY ct.SampleCode
        )
            AND t2.[Name] = t.[Name]
      ) as Column3
FROM #T1 as t

静态数据透视表解决方案

该解决方案来自我发布的链接。同样,您必须知道所需的列数,并且它没有您指定的通用列名。但是,如果您重新考虑您的输出,这是另一种解决方案。

--Use PivotTable.
SELECT *
FROM
(
    SELECT [Name], Content
    FROM #T1
) AS SourceTable PIVOT(MAX(Content) FOR [Content] IN([A#],
                                                     [A@]
                                                     )) AS PivotTable;

【讨论】:

    【解决方案2】:

    数据透视表可以解决您的问题:

    DECLARE @T TABLE (SampleCode sysname,   Name sysname,   Content sysname)
    
    INSERT @T (SampleCode, Name, Content)
    VALUES
    ('#', '1', 'A#'),
    ('#', '2', 'B#'),
    ('#', '3', 'C#'),
    ('@', '1', 'A@'),
    ('@', '2', 'B@'),
    ('@', '3', 'C@')
    
    SELECT CONCAT('Name.', PIVOTTABLE.Name), [PIVOTTABLE].[#], [PIVOTTABLE].[@]
    FROM    
    (
    SELECT * FROM @T AS t
    ) AS SOURCE
    PIVOT (
    MAX(Content)
    FOR SampleCode IN ([#], [@])
    ) AS PIVOTTABLE
    

    当然,这是您在问题中提供的解决方案。 当列太多而无法手动写下时,请使用动态查询,唯一需要生成的部分是 FOR 表达式中的值和 SELECT 中的相同值

    【讨论】:

      猜你喜欢
      • 2020-01-27
      • 1970-01-01
      • 2021-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多