AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 282886
Accepted
Poseidon
Poseidon
Asked: 2021-01-10 01:30:40 +0800 CST2021-01-10 01:30:40 +0800 CST 2021-01-10 01:30:40 +0800 CST

透视 4 x 44 结果集

  • 772

我有一个SQL Server查询,它获取44家商店市场链的销售数据,它包括每个商店的 4 个值(销售额、销货成本、GP 和 GP 保证金),我声明了一个CTE并为每个商店加入了44 次以获得每个商店的4 个值作为列,如下所示:

查询:查询1

结果 1

但是当我尝试将PIVOT函数与动态 SQL 一起使用时,它会返回多个空值,如下所示:

查询:查询2 结果 2

表##tbl1包括我想要透视的数据集: ##tbl1

我使用了以下查询:(三个点代表要旋转的其余列)

select * from ##tbl1 

pivot (sum(total_sales)     for s in ([50001 Sales],[50002 Sales],...)) as pv_tb 
pivot (sum(Margin)          for m in ([50001 margin],[50002 margin],...)) as pv_tb1
pivot (sum(total_profit)    for p in ([50001 profit],[50002 profit],...)) as pv_tb2
pivot (sum(total_cost)      for c in ([50001 cost],[50002 cost],...)) as pv_tb3

我还使用动态 SQL 来传递里面的列名,PIVOT()而不必单独编写每个列名,但为了排除故障,我只是向您展示了查询的样子。

我无法绕过使用这个132CROSS APPLY列的结果,很难维护。UNPIVOT

谁能帮我找到比这更简单的方法?

sql-server sql-server-2012
  • 2 2 个回答
  • 57 Views

2 个回答

  • Voted
  1. nbk
    2021-01-10T06:13:05+08:002021-01-10T06:13:05+08:00
      WITH Sales_CTE ([50001 Sales], [50002 Sales], [50001 margin],[50002 margin],[50001 profit],[50002 profit],[50001 cost],[50002 cost])  
    AS  
    (    
    select * from 
        (
        select rtrim(cast(csstor as char))+' Sales' as s
                ,rtrim(cast(csstor as char))+' margin' as m
                ,rtrim(cast(csstor as char))+' profit' as p
                ,rtrim(cast(csstor as char))+' cost' as c
                ,[TOTAL_PRICE]
                ,[Margin] 
                ,[TOTAL_Profit]     
                ,[Total_COST]
        from sales
        
        ) as result_1
    pivot (sum(total_price)     for s in ([50001 Sales],[50002 Sales])) as pv_tb 
    pivot (sum(Margin)          for m in ([50001 margin],[50002 margin])) as pv_tb1
    pivot (sum(total_profit)    for p in ([50001 profit],[50002 profit])) as pv_tb2
    pivot (sum(total_cost)      for c in ([50001 cost],[50002 cost])) as pv_tb3)
    SELECT
    MAX([50001 Sales]) as [50001 Sales]
    , MAX([50002 Sales]) AS [50002 Sales]
    , MAX([50001 margin]) AS [50001 margin]
    ,MAX([50002 margin]) AS [50002 margin]
    ,MAX([50001 profit]) AS [50002 margin]
    ,MAX([50002 profit]) AS [50002 profit]
    ,MAX([50001 cost]) AS [50001 cost]
    ,MAX([50002 cost]) AS [50002 cost]
    FROM Sales_CTE
    

    结果是

    50001 销售额 50002 销售额 50001保证金 50002 保证金 50002 保证金 50002 利润 50001 费用 50002 费用
    39498,300 31647,150 12,562300 11,159300 4961,932 3531,624 34536,3675 28115,5264
    • 1
  2. Best Answer
    Jonathan Fite
    2022-06-25T05:09:22+08:002022-06-25T05:09:22+08:00

    这是我对这个问题的动态解决方案。我认为您在使用 pivot 语句时遇到的问题是,当您进行数据透视时,所有列都存在,这会给您带来那些错误的 NULL。即使它们不在选择列表中,透视运算符也会按它们分组(请参阅我如何在透视部分中进行子选择)。

    至于性能,在底层,性能差异可以忽略不计,PIVOT 和 UNPIVOT 是看似丑陋/复杂的 SQL 的语法糖。我确实更喜欢使用它们,因为我认为它使代码更易于阅读且噪音更小。

    在任何情况下,此解决方案都会提供所需的结果并动态执行。我唯一需要改变的是数据类型。UNPIVOT 运算符要求所有数据类型都是相同的,所以我只是强制它们为 NUMERIC(38,6),您可能需要添加转换步骤/CTE。

    需要注意的一件事是它不对源表进行任何过滤。如果您需要添加过滤,那么您将需要在多个位置更新它,否则查询将开始为您提供带有 NULL 值的结果。在这种情况下,最好将您想要的结果转储到临时表中。

    最后:这确实是最适合显示层做的工作。我不确定结果的去向,但是 SSRS 和 Excel 都内置了出色的数据透视操作,并且它们在动态处理这方面会比在 SQL 中做得更好。不过,我的解决方案:

    /** BUILD UP BASE TABLE
        -I made it a temp table, but you can do whatever.
        **/
    
    DROP TABLE IF EXISTS #Sales 
    
    CREATE TABLE #Sales (
        [CSSTOR] [numeric](5, 0) NOT NULL,
        [TOTAL_PRICE] [numeric](38, 6) NULL,
        [Margin] [numeric](38, 6) NULL,
        [TOTAL_Profit] [numeric](38, 6) NULL,
        [Total_COST] [numeric](38, 6) NULL
    ) 
    
    
    INSERT INTO #Sales VALUES(50036,    25131.250,  12.876400   ,3236.008   ,21895.2421)
    INSERT INTO #Sales VALUES(50026,    87732.470,  13.901600   ,12196.269  ,75536.2006)
    INSERT INTO #Sales VALUES(50006,    77530.620,  13.841900   ,10731.743  ,66798.8770)
    INSERT INTO #Sales VALUES(50017,    40097.190,  11.662900   ,4676.506   ,35420.6844)
    INSERT INTO #Sales VALUES(50047,    45848.710,  14.268400   ,6541.888   ,39306.8219)
    INSERT INTO #Sales VALUES(50037,    17029.810,  13.829900   ,2355.212   ,14674.5980)
    INSERT INTO #Sales VALUES(50027,    75493.600,  14.490900   ,10939.739  ,64553.8611)
    INSERT INTO #Sales VALUES(50007,    27153.030,  11.803900   ,3205.127   ,23947.9030)
    INSERT INTO #Sales VALUES(50048,    46728.060,  12.531400   ,5855.688   ,40872.3721)
    INSERT INTO #Sales VALUES(50028,    30872.940,  13.028200   ,4022.209   ,26850.7310)
    INSERT INTO #Sales VALUES(50008,    61974.180,  13.886100   ,8605.854   ,53368.3260)
    INSERT INTO #Sales VALUES(50038,    17190.610,  12.486200   ,2146.463   ,15044.1469)
    INSERT INTO #Sales VALUES(50039,    48018.250,  5.346900    ,2567.526   ,45450.7237)
    INSERT INTO #Sales VALUES(50049,    35941.500,  11.092800   ,3986.927   ,31954.5728)
    INSERT INTO #Sales VALUES(50029,    44106.940,  13.131800   ,5792.052   ,38314.8880)
    INSERT INTO #Sales VALUES(50019,    39339.940,  14.160600   ,5570.799   ,33769.1408)
    INSERT INTO #Sales VALUES(50040,    40693.330,  12.369900   ,5033.752   ,35659.5784)
    INSERT INTO #Sales VALUES(50030,    53085.970,  12.046700   ,6395.124   ,46690.8460)
    INSERT INTO #Sales VALUES(50020,    61528.750,  13.056700   ,8033.643   ,53495.1072)
    INSERT INTO #Sales VALUES(50010,    40194.330,  12.991800   ,5222.006   ,34972.3242)
    INSERT INTO #Sales VALUES(50041,    40097.640,  12.226900   ,4902.718   ,35194.9217)
    INSERT INTO #Sales VALUES(50031,    77884.530,  9.238900    ,7195.694   ,70688.8360)
    INSERT INTO #Sales VALUES(50021,    90946.760,  12.355500   ,11236.982  ,79709.7779)
    INSERT INTO #Sales VALUES(50001,    39498.300,  12.562300   ,4961.932   ,34536.3675)
    INSERT INTO #Sales VALUES(50042,    64223.200,  13.290500   ,8535.633   ,55687.5668)
    INSERT INTO #Sales VALUES(50032,    39490.130,  14.726000   ,5815.355   ,33674.7753)
    INSERT INTO #Sales VALUES(50022,    66602.810,  12.567800   ,8370.569   ,58232.2408)
    INSERT INTO #Sales VALUES(50012,    31329.510,  14.151300   ,4433.557   ,26895.9526)
    INSERT INTO #Sales VALUES(50002,    31647.150,  11.159300   ,3531.624   ,28115.5264)
    INSERT INTO #Sales VALUES(50043,    55461.870,  13.161100   ,7299.393   ,48162.4773)
    INSERT INTO #Sales VALUES(50033,    35486.720,  8.224400    ,2918.604   ,32568.1158)
    INSERT INTO #Sales VALUES(50023,    70549.250,  12.387900   ,8739.596   ,61809.6541)
    INSERT INTO #Sales VALUES(50013,    35080.480,  13.330000   ,4676.263   ,30404.2168)
    INSERT INTO #Sales VALUES(50003,    39691.180,  11.286800   ,4479.868   ,35211.3115)
    INSERT INTO #Sales VALUES(50044,    33908.310,  11.479500   ,3892.531   ,30015.7794)
    INSERT INTO #Sales VALUES(50034,    17208.780,  8.398300    ,1445.262   ,15763.5180)
    INSERT INTO #Sales VALUES(50024,    37125.040,  10.122100   ,3757.867   ,33367.1726)
    INSERT INTO #Sales VALUES(50014,    51475.620,  12.559800   ,6465.278   ,45010.3422)
    INSERT INTO #Sales VALUES(50004,    67062.810,  13.691500   ,9181.943   ,57880.8672)
    INSERT INTO #Sales VALUES(50045,    38778.360,  11.264400   ,4368.184   ,34410.1756)
    INSERT INTO #Sales VALUES(50035,    19578.640,  10.538400   ,2063.281   ,17515.3589)
    INSERT INTO #Sales VALUES(50025,    58158.960,  13.853000   ,8056.790   ,50102.1700)
    INSERT INTO #Sales VALUES(50015,    34844.170,  8.108700    ,2825.443   ,32018.7270)
    INSERT INTO #Sales VALUES(50005,    32089.110,  13.341200   ,4281.103   ,27808.0067)
    
    DECLARE @SQLCMD NVARCHAR(MAX)
    DECLARE @CHeaderList NVARCHAR(MAX)
    
    /** Build a list of headers in the correct order that we want (store order, then sales, margin, profit and cost).
        **/
    SET @CHeaderList = (SELECT STRING_AGG(QUOTENAME(CONVERT(NVARCHAR(MAX), CONCAT(CONVERT(NVARCHAR(20), CSSTOR), ' - ', [Type]))), ',') WITHIN GROUP (ORDER BY CONVERT(NVARCHAR(20), CSSTOR), [Sort])
                        FROM (  SELECT DISTINCT CSSTOR, [Type], [Sort]
                                FROM #Sales 
                                    CROSS APPLY (VALUES ('Sales', 1)
                                                        , ('Margin', 2)
                                                        , ('Profit', 3)
                                                        , ('Cost', 4)
                                                        ) AS P ([Type], [Sort])
                                                        ) AS D
                                                        )
    /** Build up our dynamic sql command.
        CTE_Unpvt - Unpivots the data to a store/type/value array. 
            It also builds up a "CHeader" column that matches the pattern in the CHeaderList.  This gives us '50047 - Cost'
            for example.
        
        CTE_PVT - Pivots the data back to what we want using the CHeaderList above.  
        
        **/
    
    SET @SQLCMD = N'
    ;WITH CTE_Unpvt AS
        (
        SELECT CSSTOR
            , [Type]
            , [Value]
            , CHeader = CONCAT(CONVERT(NVARCHAR(50), CSSTOR), '' - '', CASE WHEN [Type] = ''TOTAL_PRICE'' THEN ''Sales''
                                                                            WHEN [Type] = ''Margin'' THEN ''Margin''
                                                                            WHEN [Type] = ''TOTAL_Profit'' THEN ''Profit''
                                                                            WHEN [Type] = ''TOTAL_COST'' THEN ''Cost''
                                                                            ELSE '''' 
                                                                            END)
        FROM #Sales AS S
            UNPIVOT ([Value] FOR [Type] IN ([TOTAL_PRICE], [Margin], [TOTAL_Profit], [Total_COST])) AS Unpvt 
        )
    , CTE_PVT AS
        (
        SELECT ' + @CHeaderList + '
        FROM (SELECT [Value], CHeader FROM CTE_Unpvt AS p) AS up
            PIVOT (SUM([Value]) FOR CHeader IN (' + @CHeaderList + ')) AS pvt
        )
    SELECT * 
        , [TOTAL_SALES] = (SELECT SUM([TOTAL_PRICE]) FROM #Sales)
        , [TOTAL_MARGIN] = (SELECT SUM([Margin]) FROM #Sales)
        , [TOTAL_PROFIT] = (SELECT SUM([TOTAL_Profit]) FROM #Sales)
        , [TOTAL_COST] = (SELECT SUM([TOTAL_COST]) FROM #Sales)
    FROM CTE_PVT'
        
    EXEC (@SQLCMD)
    
    • 1

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve