门店组A与B销售对比实现、SQL查询效率优化及增长计算逻辑改进技术咨询
Hey HamzaB, let's break down your questions and tackle each one step by step to get you the results you need, plus better query performance:
Your current subquery (SELECT top 1 AVG(store_id) FROM store WHERE store_group = 'A' ) doesn't actually calculate sales totals for group A—instead, it's averaging store IDs, which isn't relevant to sales performance.
To properly sum sales for groups A and B (and compute their difference), use conditional aggregation in your query. This lets you calculate both totals in a single pass over the data, which is far more efficient than separate subqueries.
Example logic for these columns:
SUM(CASE WHEN ST.store_group = 'A' THEN SL.spend ELSE 0 END) AS GroupA_Total_Spend, SUM(CASE WHEN ST.store_group = 'B' THEN SL.spend ELSE 0 END) AS GroupB_Total_Spend, SUM(CASE WHEN ST.store_group = 'A' THEN SL.spend ELSE 0 END) - SUM(CASE WHEN ST.store_group = 'B' THEN SL.spend ELSE 0 END) AS GroupAB_Spend_Difference
Raw spend values for next year don't give context about growth magnitude—converting this to a percentage makes it much easier to evaluate performance across different time periods or store sizes.
Here's how to adjust that calculation (with handling for cases where spend is 0 to avoid division by zero):
CASE WHEN SL.spend = 0 THEN NULL -- Avoid division by zero ELSE ROUND(((LEAD(SL.spend, 12) OVER (PARTITION BY ST.store_group ORDER BY DS.date_id ASC) - SL.spend) / SL.spend) * 100, 2) END AS NextYearGrowth_Percent
Note: I added PARTITION BY ST.store_group here to ensure the LEAD function calculates growth within each store group, which makes the metric more meaningful.
You mentioned wanting an output like |Store A | Store B| 数据求和 | 数据求和 |—I assume you want grouped totals (e.g., by month/period) showing each group's sales. The revised query below will structure results to show group-specific totals alongside your other metrics, grouped by the date dimensions you need.
Your current query has a few inefficiencies:
- The
DISTINCTis redundant since you're already usingGROUP BY - The subquery in the SELECT clause runs once per row, which is slow on large datasets
- Grouping by too many granular columns (like individual
store_idanddate_id) might be preventing aggregation that would speed things up
Key Optimizations:
- Use conditional aggregation instead of row-level subqueries to calculate group totals in one pass
- Add appropriate indexes to speed up joins and window functions:
- On
sales:CREATE NONCLUSTERED INDEX IX_Sales_Store_Date ON sales(store_id, date_id) INCLUDE (baskets, spend); - On
store:CREATE NONCLUSTERED INDEX IX_Store_Group_ID ON store(store_group, store_id);
- On
- Simplify grouping by aggregating at the date dimension level (e.g.,
calendar_month) instead of individualdate_idif that aligns with your reporting needs
Revised Full Query
SELECT DS.calendar_month, DS.iso_week, DS.iso_period, -- Group A/B sales totals SUM(CASE WHEN ST.store_group = 'A' THEN SL.spend ELSE 0 END) AS GroupA_Total_Spend, SUM(CASE WHEN ST.store_group = 'B' THEN SL.spend ELSE 0 END) AS GroupB_Total_Spend, SUM(CASE WHEN ST.store_group = 'A' THEN SL.spend ELSE 0 END) - SUM(CASE WHEN ST.store_group = 'B' THEN SL.spend ELSE 0 END) AS GroupAB_Spend_Difference, -- Aggregated baskets (adjust if you need per-group baskets too) SUM(SL.baskets) AS Total_Baskets, -- Spend growth vs previous period SUM(SL.spend) - LAG(SUM(SL.spend)) OVER (ORDER BY DS.calendar_month ASC) AS Period_Spend_Growth, -- Next year growth as percentage (per group) AVG(CASE WHEN SL.spend = 0 THEN NULL ELSE ROUND(((LEAD(SL.spend, 12) OVER (PARTITION BY ST.store_group ORDER BY DS.date_id ASC) - SL.spend) / SL.spend) * 100, 2) END) AS Avg_NextYearGrowth_Percent, -- Spend groups (aggregated count or percentage) COUNT(CASE WHEN SL.spend BETWEEN 1000 AND 5000 THEN 1 END) AS Low_Spend_Count, COUNT(CASE WHEN SL.spend BETWEEN 5001 AND 15000 THEN 1 END) AS Moderate_Spend_Count, COUNT(CASE WHEN SL.spend > 15000 THEN 1 END) AS High_Spend_Count FROM sales SL INNER JOIN store ST ON ST.store_id = SL.store_id INNER JOIN dates4 DS ON DS.date_id = SL.date_id WHERE ST.store_group IN ('A', 'B') -- Filter to only relevant groups early GROUP BY DS.calendar_month, DS.iso_week, DS.iso_period ORDER BY DS.calendar_month ASC;
Notes on the Revised Query:
- Filters to only groups A/B upfront to reduce data processing
- Aggregates at the month/week/period level (adjust grouping columns if you need more granularity)
- Replaces your original
SpendGroupswith counts for each category (easier to analyze than row-level labels) - Removes redundant
DISTINCTand inefficient subqueries
内容的提问来源于stack exchange,提问作者HamzaB

