sql - 通过将一列中的值引用到另一列来汇总记录

标签 sql sql-server t-sql pivot dynamic-sql

我有一个表,其中有两列 - col1 和 col2。需要汇总该表,以便如果某个值位于 col1 中,则与 col2 相邻的值将插入到原始 col1 值旁边。为了说明这一点,如果我有一个像这样的表:

col1    col2
24      670
25      980
26      24
28      1570
28      26
27      5745
27      4654

那么输出应该如下所示(没有特定的顺序):

col1    col_1   col_2   col_3   col_4
24      670     26      28    1570
25      980     NULL    NULL    NULL
27      4654    5745    NULL    NULL

这里 col2 中的 24 已经存在于 col1 中,因此 26(以及其他关联 28 和 1570)被插入到 24 旁边。不幸的是,以我目前在 sql 方面的知识,我能够得到这个:


--refrences
-- https://stackoverflow.com/questions/38233002/how-to-create-add-columns-using-a-variable-in-a-loop
-- https://stackoverflow.com/questions/10404348/sql-server-dynamic-pivot-query


declare @max_columns int
declare @counter int
declare @col_header nvarchar(max)= ''
declare @query nvarchar(max)= ''

drop table if exists  #tmp_ini
drop table if exists  #tmp
drop table if exists  #tmp_allcols

create table #tmp_ini (col1  int, col2 int )

create table #tmp_allcols (col1  int, col nvarchar(max), [val] int, [row_id] int)


insert into #tmp_ini values (24, 670),
                            (25, 980),
                            (26, 24),
                            (28, 1570),
                            (28, 26),
                            (27, 5745),
                            (27, 4654)


select * into #tmp from #tmp_ini

insert into #tmp_allcols
    select col1,
            'col_' + cast(t.row_id as nvarchar(max)) as col ,
            col2 as val,
            t.row_id 
    from (SELECT * , ROW_NUMBER() OVER ( PARTITION  by col1  Order by col2  ) AS row_id FROM #tmp ) t


select @max_columns= max(m.tot) from(
        select COUNT(*) as tot from #tmp_allcols group by col1
     ) m


set @counter=1
while @counter <= @max_columns
    begin
        set @col_header += 'col_' + cast(@counter as nvarchar(50)) +  ', '
        set @counter = @counter + 1
    end


set @col_header = SUBSTRING(@col_header,1,LEN(@col_header)-1)


set @query += ' select * from ('
set @query += '     select col1, col, val from #tmp_allcols '
set @query += '  ) tmp'
set @query += ' PIVOT ( max(val) for Col in ('
set @query += @col_header
set @query += '     )) AS pvt'

print @query

exec sp_executesql @query 

当前输出:

col1    col_1   col_2
24      670     NULL
25      980     NULL
26      24      NULL
27      4654    5745
28      26      1570

如有任何提示或帮助,我们将不胜感激。我看过这些帖子,但没有走得太远SQL Pivot IF EXISTS ; `PIVOT` with `EXISTS` ; `PIVOT` with `EXISTS`

最佳答案

我认为没有一种简单的方法可以满足您的需要,这就是为什么我跳过了简单的方法,并做了一些疯狂的 SQL 逻辑:

--#####################
--#### SCHEMA #########
--#####################
CREATE TABLE Entities(
    entity1 int NULL,
    entity2 int NULL
)

INSERT INTO Entities VALUES
(24, 670),
(25, 980),
(26, 24),
(28, 1570),
(28, 26),
(27, 5745),
(27, 4654)

--###############################
--########### QUERY #############
--###############################

IF OBJECT_ID('tempdb..#TempRelations') IS NOT NULL
DROP TABLE #TempRelations 

IF OBJECT_ID('tempdb..#TempEntities') IS NOT NULL
DROP TABLE #TempEntities

IF OBJECT_ID('tempdb..#FinalRelations') IS NOT NULL
DROP TABLE #FinalRelations

IF OBJECT_ID('tempdb..#FinalTable') IS NOT NULL
DROP TABLE #FinalTable

CREATE TABLE #TempRelations (entity INT NULL)
CREATE TABLE #FinalRelations (id INT, entity INT NULL)

SELECT ROW_NUMBER() OVER (ORDER BY entity1) Id, *
INTO #TempEntities
FROM Entities

DECLARE @entity1 INT, @entity2 INT
DECLARE @count INT = (SELECT COUNT(*) FROM #TempEntities)
DECLARE @relationsCount INT = 1;
DECLARE @i INT = 1
                    
--WHILE THERE ARE STILL ENTITIES
WHILE EXISTS(SELECT 1 
             FROM #TempEntities 
             WHERE entity1 IS NOT NULL 
                OR entity2 IS NOT NULL)
BEGIN
    SELECT @entity1 = entity1, @entity2 = entity2
    FROM #TempEntities
    WHERE Id = @i

    DELETE #TempRelations

    IF @entity1 IS NOT NULL
    BEGIN
        INSERT INTO #TempRelations VALUES (@entity1)
        INSERT INTO #TempRelations VALUES (@entity2)
    END

    UPDATE #TempEntities 
        SET entity1 = NULL, 
            entity2 = NULL
        WHERE Id = @i

    --WHILE THERE ARE STILL RELATIONS TO BE TAKEN FROM ENTITIES
    WHILE 1=1
    BEGIN       
        SET @i = 1

        WHILE @i <= @count
        BEGIN       
            SELECT @entity1 = entity1, @entity2 = entity2
            FROM #TempEntities
            WHERE Id = @i

            --IF entity1 OR entity2 HAS RELATION TO THE CURRENT TEMP RELATIONS, INSERT THE RELATION THEN REMOVE THE ENTITIES
            IF @entity1 IS NOT NULL AND ( EXISTS (SELECT 1
                                                  FROM #TempRelations
                                                  WHERE entity = @entity1
                                                  OR entity = @entity2) OR (SELECT COUNT(*) FROM #TempRelations) = 0 )
            BEGIN               
                INSERT INTO #TempRelations VALUES (@entity1)
                INSERT INTO #TempRelations VALUES (@entity2)
                UPDATE #TempEntities 
                SET entity1 = NULL, 
                    entity2 = NULL
                WHERE Id = @i
            END

            SET @i = @i + 1         
        END

        IF NOT EXISTS (SELECT 1 
            FROM #TempEntities
            WHERE EXISTS (SELECT 1
                          FROM #TempRelations
                          WHERE entity = entity1
                          OR entity = entity2))
        BREAK

    END

    INSERT INTO #FinalRelations
    SELECT DISTINCT @relationsCount,        
                    entity
    FROM #TempRelations 

    SET @relationsCount = @relationsCount + 1
    DELETE #TempRelations
END

SELECT 'col' + (CAST(ROW_NUMBER() OVER (PARTITION BY id ORDER BY id) AS VARCHAR)) Col,
       id,
       entity
INTO #FinalTable
FROM #FinalRelations

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

select @cols = STUFF((SELECT DISTINCT ',' + col 
                      FROM #FinalTable
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = N'SELECT ' + @cols + N' FROM 
             (
                SELECT Col, id, entity
                FROM #FinalTable
            ) x
            PIVOT 
            (
                MAX(entity)
                FOR Col IN (' + @cols + N')
            ) p '

PRINT (@query)

exec sp_executesql @query;

关于sql - 通过将一列中的值引用到另一列来汇总记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73465413/

相关文章:

java - SQL结果集突然关闭

mysql - 查询作者 > 1 的每个出版商

sql-server - SQL Server 2012 中的日期格式

sql - 复杂的 sp_executesql - 容易受到注入(inject)?

php - 是否可以有一个离线数据库

java - H2 SQL 语法错误 `expected "., COMMENT, ("` using Flyway with/Spring Boot Test

sql - 使用 CTE 进行递归选择时出现错误 - "UNION operator must have an equal number of expressions"

sql - 将所有联系信息放在一张表中与使用键值表

t-sql - 是否可以限制表仅在一组列中的一个中具有值

SQL Server 2008 列名 'Type' 未显示在更新查询中