在构建 SQL 查询方面需要帮助

Posted

技术标签:

【中文标题】在构建 SQL 查询方面需要帮助【英文标题】:Need help in framing SQL Query 【发布时间】: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 查询。 .. 【参考方案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,谢谢你给了我完美的答案

以上是关于在构建 SQL 查询方面需要帮助的主要内容,如果未能解决你的问题,请参考以下文章

帮助 SQL 查询 - 需要一些魔法

在使用 LINQ 的对面到内部联接查询方面需要帮助

Django Queryset:在优化这组查询方面需要帮助

需要帮助构建 SQLAlchemy 查询 + 子查询

需要优化SQL查询的帮助

在 hasAndBelongsToMany (HABTM) Cakephp 方面需要帮助