在构建 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 查询方面需要帮助的主要内容,如果未能解决你的问题,请参考以下文章