返回

如何用SQL找出每月每日最便宜商品的平均价格?

mysql

如何用SQL找出每月每日最便宜商品的平均价格?

你是否曾为了寻找最划算的购物时机而烦恼?想象一下,如果能从海量销售数据中,精准定位到每月每日最便宜商品的平均价格,就能轻松掌握最佳购物窗口,省钱又省心!

本文将以一个实际案例出发,带你一步步使用 SQL 解锁这个隐藏技能。

假设我们拥有一份包含商品名称、商店名称、销售日期、销售价格等信息的超市销售数据表(Grocery_Data)。我们的目标是找出每种商品在一年中哪个月份、哪一天的平均售价最低。

你可能尝试了一些 SQL 语句,却发现结果不尽如人意。别担心,让我们先分析一些常见的错误,然后提供最佳解决方案。

错误尝试分析

陷阱一:

SELECT Grocery_Item, Store_Name, MONTH(Sales_Date) AS Month, Sales_Day, Sales_Price
FROM Grocery_Data
WHERE Sales_Price < (SELECT AVG(Sales_Price) FROM Grocery_Data)
ORDER BY Grocery_Item, Sales_Price;

这段代码的思路是先计算所有商品的平均售价,再筛选出价格低于平均售价的记录。问题在于,子查询 (SELECT AVG(Sales_Price) FROM Grocery_Data) 计算的是所有商品的总平均售价 ,而不是针对每种商品、每个月份、每一天计算的平均售价。

陷阱二:

SELECT Grocery_Item, MONTH(Sales_Date) AS Month, Sales_Date,ROUND(AVG(DISTINCT Sales_Price), 2) AS Avg_Sales_Price 
FROM Grocery_Data 
GROUP BY Grocery_Item, Month, Sales_Date 
HAVING AVG(Sales_Price) < (SELECT MIN(Avg_Sales_Price) FROM (SELECT Grocery_Item, AVG(Sales_Price) AS Avg_Sales_Price FROM Grocery_Data GROUP BY Grocery_Item) AS Subquery) 
ORDER BY Avg_Sales_Price;

这段代码尝试先计算每种商品在每个月份、每一天的平均售价,再筛选出平均售价低于该商品所有平均售价最小值的记录。然而,HAVING 子句中的子查询语法存在错误,导致无法正确执行。

陷阱三:

SELECT Store_Name, Grocery_Item, Month, Sales_Day, AVG_Sales_Price
FROM (
    SELECT Store_Name, Grocery_Item, MONTH(Sales_Date) AS Month, Sales_Day,
        ROUND(AVG(Sales_Price), 2) AS AVG_Sales_Price,
        ROW_NUMBER() OVER (PARTITION BY Grocery_Item ORDER BY AVG(Sales_Price)) AS rank
    FROM Grocery_Data
    GROUP BY Grocery_Item, Month, Sales_Day
) ranked_data
WHERE rank = 1;

这段代码的思路是使用窗口函数 ROW_NUMBER() 对每种商品在每个月份、每一天的平均售价进行排名,并筛选出排名第一的记录。这个方法虽然接近目标,但它只能找到每个月最低平均售价的一天 ,而不是所有日期中平均售价最低的一天。

最佳解决方案

为了找到每种商品每月每日最便宜的平均价格,我们需要结合分组、排序和子查询等技巧。以下SQL语句可以实现这个目标:

WITH DailyAveragePrices AS (
  SELECT 
    Grocery_Item,
    MONTH(Sales_Date) AS Month,
    DAY(Sales_Date) AS Day,
    AVG(Sales_Price) AS AvgPrice
  FROM Grocery_Data
  GROUP BY 
    Grocery_Item,
    MONTH(Sales_Date),
    DAY(Sales_Date)
),
RankedPrices AS (
  SELECT 
    Grocery_Item,
    Month,
    Day,
    AvgPrice,
    ROW_NUMBER() OVER (PARTITION BY Grocery_Item, Month ORDER BY AvgPrice ASC) AS rn
  FROM DailyAveragePrices
)
SELECT 
  Grocery_Item,
  Month,
  Day,
  AvgPrice
FROM RankedPrices
WHERE rn = 1
ORDER BY 
  Grocery_Item,
  Month,
  Day;

代码解析

  1. 创建 DailyAveragePrices CTE: 首先,我们创建一个名为 DailyAveragePrices 的公用表表达式 (CTE),用于计算每种商品在每个月份、每一天的平均售价。
  2. 创建 RankedPrices CTE: 接着,我们创建另一个名为 RankedPrices 的 CTE,使用窗口函数 ROW_NUMBER() 对每种商品在每个月份的平均售价进行排名,并按照价格升序排列。
  3. 最终查询: 最后,我们从 RankedPrices CTE 中选择排名第一的记录,即平均售价最低的记录,并将结果按商品、月份和日期排序。

通过这段 SQL 语句,我们可以直接从数据库中获取每种商品在一年中哪个月份、哪一天的平均售价最低,无需再进行繁琐的数据导出和整理。

常见问题解答

问题一: 为什么使用 CTE 而不是直接嵌套子查询?

解答: 使用 CTE 可以使代码更易读、易理解,尤其是在处理复杂的查询逻辑时。CTE 可以将复杂的查询分解成多个逻辑步骤,使代码更清晰易懂。

问题二: ROW_NUMBER() 函数的作用是什么?

解答: ROW_NUMBER() 函数是一个窗口函数,它可以为查询结果集中的每一行分配一个唯一的行号。在本例中,我们使用 ROW_NUMBER() 函数对每种商品在每个月份的平均售价进行排名。

问题三: 如何修改代码以查找每月最便宜的平均售价,而不考虑具体日期?

解答: 只需修改 RankedPrices CTE 中的 PARTITION BY 子句,将其改为 PARTITION BY Grocery_Item, Month,然后将最终查询中的 Day 字段移除即可。

问题四: 如何将结果保存到新的数据表中?

解答: 可以使用 CREATE TABLE AS 语句将查询结果保存到新的数据表中。例如:

CREATE TABLE CheapestPrices AS
-- 将上面的最终查询语句粘贴到这里
;

问题五: 如何根据不同的数据库系统调整代码?

解答: 虽然本文使用的 SQL 语法适用于大多数主流数据库系统,但部分数据库系统可能存在语法差异。建议查阅相关数据库系统的文档以获取准确的语法信息。

希望本文能帮助你轻松掌握使用 SQL 查找每月每日最便宜商品的平均价格的方法!