【问题标题】:Need help in framing SQL Query在构建 SQL 查询方面需要帮助
【发布时间】:2021-03-02 13:43:10
【问题描述】:

表-1

OrderDate CustomerNo ProductId Quantity
2021-03-01 1 1 10
2021-03-01 1 3 20
2021-03-02 1 2 15
2021-03-02 1 3 10
2021-03-03 1 1 10
2021-03-03 1 5 25

此表还包含其他客户的数据以及客户 2、3、4 ...

表 2

ProductId ProductName
1 P1
2 P2
3 P3
4 P4
5 P5

产品不固定,可加P6,P7 ...

结果:

OrderDate P1 P2 P3 P4 P5
2021-03-03 10 25
2021-03-02 15 10
2021-03-01 10 20

我需要这个结果,这可能使用 Pivot / UnPivot

【问题讨论】:

  • 到目前为止你尝试了什么?
  • Pivot 是最简单的,但您可以使用内联选择来完成
  • Select-subqueries 可用于 FROM-、JOIN- 和 WHERE 语句,但绝不应在 SELECT 部分中使用,因为它对性能极为不利。但在这种情况下,这些选项都不是最佳选择。
  • 这能回答你的问题吗? T-SQL dynamic pivot
  • Pivot 有什么问题?问题在于 SQL 中的列,没有办法解决它,我们必须以 some 方式定义它们,如果动态 SQL 是您的解决方案,那么我只需动态构造 PIVOT 查询。 ..

标签: sql sql-server select


【解决方案1】:
Here is the dynamic PIVOT for the query
DECLARE @SQL  AS VARCHAR(MAX)
, @cols_ AS  vARCHAR(MAX) 

--Making the column list dynamically 
SELECT @cols_   = STUFF((SELECT DISTINCT ', '+QUOTENAME(  [T2].[ProductName]) 
               FROM  [TABLE_2] [T2]                
               FOR XML PATH('')), 1, 1, '')              


print @cols_
--preparing PIVOT query dynamically. 

SET @SQL = ' SELECT
       pivoted.* 
      into #Temp_data
      FROM 
      (
    SELECT 
         [T1].[OrderDate],           
         [T2].[ProductName], 
         SUM([T1].[Quantity] ) AS  [Quantity]
    FROM [TABLE_1] [T1] 
    INNER JOIN [TABLE_2] [T2] 
      ON [T1].[ProductId] =  [T2].[ProductId]
    GROUP BY  [T1].[OrderDate],              
         [T2].[ProductName] 
      ) AS [p]
      PIVOT
      (
         MIN([P].[Quantity]) 
         FOR  [P].[ProductName]  IN (' + @cols_ + ')
      ) AS pivoted;



      select *        
       from #Temp_data [B]
      -- GROUP BY [B].[OrderDate]  

      drop table #Temp_data
   ';

   PRINT( @SQL)
   EXEC (@SQL)

结果: Dynamic PIVOT

【讨论】:

    【解决方案2】:

    是的,可以使用 Pivot,但我个人不喜欢 Pivot,我使用 CASE WHEN 语句代替,因为它更简单且打字更少。如果我没记错的话,使用 CASE WHEN 而不是 Pivot 也不会降低性能。

    SELECT
        T1.OrderDate,
        [P1] = SUM(CASE WHEN T2.ProductName = 'P1' THEN Quantity END),
        [P2] = SUM(CASE WHEN T2.ProductName = 'P2' THEN Quantity END),
        [P3] = SUM(CASE WHEN T2.ProductName = 'P3' THEN Quantity END),
        [P4] = SUM(CASE WHEN T2.ProductName = 'P4' THEN Quantity END),
        [P5] = SUM(CASE WHEN T2.ProductName = 'P5' THEN Quantity END)
    FROM Table1 T1
    JOIN Table2 T2 ON T1.ProductId = T2.ProductID
    GROUP BY T1.OrderDate
    

    如果你不使用 SUM() 函数,你会在结果中得到 NULL 值,我建议你尝试一下,这样你就知道为什么需要 SUM() 函数了。

    旁注:Pivot 和 CASE WHEN 均未启用动态列数,您需要为此使用动态 SQL。

    【讨论】:

    • 就我而言,ProductNames 不固定。可以添加产品名称
    • 那么你需要使用游标(或者可能是另一种循环机制)和动态 SQL 以及我编写的解决方案来解决这个问题。
    • @Sathesh,这个站点上已经有很多动态 SQL 数据透视示例可用,例如 this one
    【解决方案3】:

    它不是超级漂亮,但您可以使用动态 SQL 从 Erik Blomgren 生成查询:

    DECLARE @cols NVARCHAR(MAX), @query NVARCHAR(MAX);
    SET @cols = STUFF(
        (
            SELECT 
                ','+QUOTENAME(ProductName) + '=SUM(CASE ProductId WHEN ' + CAST(ProductId as varchar) + ' THEN Quantity END)' 
            FROM Table2 FOR XML PATH(''), TYPE
        ).value('.', 'nvarchar(max)')
    , 1, 1, '');
    SELECT @query = 'SELECT OrderDate ' + @cols + ' FROM Table1 GROUP BY OrderDate';
    
    EXEC sp_executesql @query
    

    看到我们仍然在生成 SQL,实际查询匹配的是 Id,而不是 ProductName,所以我们根本不需要加入这个实例

    SELECT OrderDate
    ,[P1]=SUM(CASE ProductId WHEN 1 THEN Quantity END)
    ,[P2]=SUM(CASE ProductId WHEN 2 THEN Quantity END)
    ,[P3]=SUM(CASE ProductId WHEN 3 THEN Quantity END)
    ,[P4]=SUM(CASE ProductId WHEN 4 THEN Quantity END)
    ,[P5]=SUM(CASE ProductId WHEN 5 THEN Quantity END)
    FROM Table1 GROUP BY OrderDate
    

    您可以轻松地使用相同的技术来生成 PIVOT / UNPIVOT 查询,但您明确表示您不想要其中之一 ;)

    【讨论】:

    • 在执行之前查看SELECT @query 的结果,在我的测试中运行良好,但您的架构可能与您发布的略有不同。与所有动态 SQL 脚本一样,在调试时,输出实际语句,然后将其粘贴回 management studio 中直接执行。
    【解决方案4】:

    您需要获得的结果可以使用简单的case语句或使用Pivot和聚合来获得。我个人更喜欢 PIVOT,因为它可以做各种数据转换,我们可以随心所欲地获取数据。在这里,我添加了两种解决方案。

    解决方案 01:使用 PIVOT 并稍后聚合结果。这似乎更复杂,因为您需要同时了解 PIVOT 和聚合函数。

    SELECT  [B].[OrderDate] 
        , SUM([B].[P1]) AS [P1]
        , SUM([B].[P2]) AS [P2]
        , SUM([B].[P3]) AS [P3]
        , SUM([B].[P5]) AS [P5]            
    FROM
    (
    SELECT [PIVOTED].[OrderDate]
    , ISNULL( [PIVOTED].[P1] ,'') AS [P1]
    , ISNULL( [PIVOTED].[P2], '') AS [P2]
    , ISNULL( [PIVOTED].[P3], '') AS [P3]
    , ISNULL( [PIVOTED].[P5], '') AS [P5]
    
    FROM(
    
       SELECT 
         [T1].[OrderDate], 
         [T1].[ProductId], 
         [T2].[ProductName], 
         [T1].[Quantity] 
    FROM [TABLE_1] [T1] 
    INNER JOIN [TABLE_2] [T2] 
      ON [T1].[ProductId] =  [T2].[ProductId]
    
    ) P
    PIVOT
    (
       SUM([P].[Quantity])
       FOR [P].[ProductName] IN  ([P1],[P2],[P3],[P5])
    
    ) PIVOTED
    
    ) AS B
    GROUP BY [B].[OrderDate]  
    

    结果: Result for Solution 01:

    解决方案 02:使用简单的 case 语句:

    SELECT
     T1.OrderDate,
     [P1] = SUM(CASE WHEN T2.ProductName = 'P1' THEN Quantity END),
     [P2] = SUM(CASE WHEN T2.ProductName = 'P2' THEN Quantity END),
     [P3] = SUM(CASE WHEN T2.ProductName = 'P3' THEN Quantity END),     
     [P5] = SUM(CASE WHEN T2.ProductName = 'P5' THEN Quantity END)
    FROM TABLE_1 T1
    JOIN TABLE_2 T2 ON T1.[ProductId] = T2.[ProductID]
    GROUP BY T1.[OrderDate]
    

    结果 02: Result for Solution 02:

    注意:您需要在将新产品添加到表格时对其进行处理。正如您所看到的,这两种解决方案都对产品名称进行了硬编码,您需要对其进行处理。如果您需要更通用的解决方案,请告诉我。我将提供一个动态 PIVOT,您无需在添加新产品时对其进行处理。

    【讨论】:

    • 帮我处理动态 PIVOT,结果 02 正是我所需要的
    • 好的。既然你提到了 PIVOT,那也会产生输出。两者都需要硬编码。无论如何,希望这会有所帮助。谢谢
    • 如果您需要动态写入,请在此处告诉我。生病添加查询。这样您就不必担心产品名称。你不需要硬编码,让我知道。如果您觉得答案有用,请告诉我。谢谢:)
    • 发给我动态查询,
    • Gayani,谢谢你给了我完美的答案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-18
    相关资源
    最近更新 更多