SQL查询以在给定条件下明确获取总和
Posted
技术标签:
【中文标题】SQL查询以在给定条件下明确获取总和【英文标题】:SQL Query to get the sum distinctly in given condition 【发布时间】:2016-07-20 07:44:12 【问题描述】:如何仅根据相似的列变量获取列的总和?请查看给出的示例以进一步了解问题。
这是样本数据:
Location TENANT Date Sales Area
AMALL Tenant1 1/1/2016 1,000.00 50
AMALL Tenant1 1/2/2016 0.00 50
AMALL Tenant1 1/3/2016 0.00 50
So on..
AMALL Tenant2 1/1/2016 500.00 60
AMALL Tenant2 1/2/2016 0.00 60
AMALL Tenant2 1/3/2016 0.00 60
So on..
AMALL Tenant1 2/1/2016 800.00 50
AMALL Tenant1 2/2/2016 200.00 50
AMALL Tenant1 2/3/2016 0.00 50
So on..
AMALL Tenant3 2/1/2016 600.00 50
AMALL Tenant3 2/2/2016 600.00 50
AMALL Tenant3 2/3/2016 0.00 50
So on..
如您所见,在一个给定位置,其中包含两个或多个不同的租户。每个人每天都有销售。此外,每个租户都有唯一的平方米或面积。我想要实现的是下面的示例输出。
预期输出
Location Month Total Sales Total Area
Amall January 1,500.00 110
Amall February 2,200.00 100
请查看给定的预期输出。根据结果 1,500 是 1 月份的总销售额,包括租户 1 和 2 的销售额(1000 + 500 = 1,500),而总面积仅包括租户 1 和 2 的不同面积(50 + 60 = 110)
在 2 月份,只有租户 1 和 3 是贡献者,假设租户 2 已关闭并且根本没有销售。
基于总销售额,它是 2,200,来自 1000(租户 1)和 1200(租户 3)。这次的总面积应该是100(租户 1 的 50 + 租户 3 的 50)。租户 2 的 60 面积价值当然不包括在内,因为它在给定月份(2 月)没有销售
当前的错误输出
Location Month Total Sales Total Area
Amall January 1,500.00 110
Amall February 2,200.00 50
根据应用的更新逻辑和代码,我能够更正 1 月总面积的输出,在我的代码上使用 Distinct 命令,但是我在 2 月不正确,只有 50 个,它应该是 100,因为租户 1 和租户 3 是不同的租户,应该总结他们各自的区域,由于我使用的是 Distinct 命令,系统将他们的区域视为一个,因此不总结他们的区域。
我怎样才能得到他们的总和,因为,Area 来自不同或不同的租户
这是我的更新代码
SELECT location , tenant , a.date , sales, area
INTO #Temptable1
FROM SalesTable
SELECT tenants.location , months.number ,months.MonthName
,(sum(case when year(DATE) = 2016 then sales end)) as 'Total Sales'
,(sum(Distinct(case when year(DATE) = 2016 then area end))) as 'Total Area'
FROM
(
SELECT Number , DATENAME(MONTH, '2015-' + CAST(Number as varchar(2)) + '-1')
'MonthName'
FROM master..spt_values
WHERE Type = 'P' and Number between 1 and 12
) months
CROSS JOIN
(
SELECT DISTINCT locationd , location
FROM #TempTable1
) tenants
LEFT JOIN #TempTable1 t
ON months.monthname = datename(month,t.date) and tenants.location = t.location
GROUP BY months.monthname , tenants.location , months.number
ORDER BY datepart(MM,months.monthname + '01 2000');
根据一位专家的建议,我刚刚在最后一行添加了一个 Distinct 命令,仍然没有给出答案。
希望您就主要问题分享您的专家建议。
【问题讨论】:
您可以在SUM
语句中使用DISTINCT
,例如:,(sum(DISTINCT case when year(DATE) = 2016 then area end)) as 'Total Area'
嗨,这还不够,因为在一个租户与另一个租户拥有相同区域的情况下,总面积的总和将受到影响,因为它不会添加正确的总和 @gofr1 I将更新我的问题
我已经为你添加了示例@gofr1
谢谢,确实我错了。看我的回答。
【参考方案1】:
这应该可以工作
为了能够测试和验证它,我创建了一个临时表
CREATE TABLE #Test
(
Location varchar(20),
Tenant varchar(20),
[Date] date,
Sales decimal,
Area int
)
INSERT INTO #TEST
SELECT 'AMALL', 'Tenant1', '1/1/2016', 1000.00, 50
UNION
SELECT 'AMALL', 'Tenant1', '1/2/2016', 0.00, 50
UNION
SELECT 'AMALL', 'Tenant1', '1/3/2016', 0.00, 50
UNION
SELECT 'AMALL', 'Tenant1', '1/4/2016', 0.00, 50
UNION
SELECT 'AMALL', 'Tenant1', '1/5/2016', 0.00, 50
UNION
SELECT 'AMALL', 'Tenant2', '1/1/2016', 500.00, 60
UNION
SELECT 'AMALL', 'Tenant2', '1/2/2016', 0.00, 60
UNION
SELECT 'AMALL', 'Tenant2', '1/3/2016', 0.00, 60
UNION
SELECT 'AMALL', 'Tenant2', '1/4/2016', 0.00, 60
UNION
SELECT 'AMALL', 'Tenant2', '1/5/2016', 0.00, 60
新的解决方案,在审查 cmets 之后
;WITH ATL(Area, Tenant, Location) AS
(
SELECT MAX(Area) Area, Tenant, Location
FROM #Test
GROUP BY Tenant, Location
),
AreaLocation(Area, Location)
AS
(
SELECT SUM(Area) Area, Location
FROM ATL
GROUP BY Location
)
SELECT
T.Location,
DATEPART(MONTH, T.[Date]) Month,
SUM(T.Sales) [Total Sales],
MAX(AL.Area) [Total Area]
FROM
#Test T
JOIN
AreaLocation AL ON T.Location = AL.Location
GROUP BY
T.Location, DATEPART(MONTH, [Date])
问题的旧解决方案:
SELECT
Location,
DATEPART(MONTH, [Date]) Month,
SUM(Sales) [Total Sales],
SUM(DISTINCT Area) [Total Area]
FROM
#Test
GROUP BY
Location, DATEPART(MONTH, [Date])
而且我还想删除表,以便在我进行更改时再次运行它
DROP TABLE #Test
【讨论】:
对!子查询没用。我会删除我的答案。你的更好 @JoeTaras 谢谢 ;) 另请注意,区域的区别应基于每个租户,因为不同租户具有相同区域的情况。例如 (T1 = 80, T2 = 80) ,总面积应该是 160 而不是 80 条件是:区域与特定租户不同。当检测到相同的区域但属于另一个或不同的租户时,它将被计算在内。 @乔尼 我更新了我的答案以处理同一区域的多个租户【参考方案2】:我已尝试重现此问题:
;WITH Temptable1 AS (
SELECT *
FROM (VALUES
('AMALL', 'Tenant1', '1/1/2016', 1000.00, 50),
('AMALL', 'Tenant1', '1/2/2016', 0.00, 50),
('AMALL', 'Tenant1', '1/3/2016', 0.00, 50),
('AMALL', 'Tenant2', '1/1/2016', 500.00, 60),
('AMALL', 'Tenant2', '1/2/2016', 0.00, 60),
('AMALL', 'Tenant2', '1/3/2016', 0.00, 60),
('AMALL', 'Tenant1', '2/1/2016', 800.00, 50),
('AMALL', 'Tenant1', '2/2/2016', 200.00, 50),
('AMALL', 'Tenant1', '2/3/2016', 0.00, 50),
('AMALL', 'Tenant3', '2/1/2016', 600.00, 50),
('AMALL', 'Tenant3', '2/2/2016', 600.00, 50),
('AMALL', 'Tenant3', '2/3/2016', 0.00, 50)
) as t([location], tenant, [date], sales, area)
)
SELECT tenants.location , months.number ,months.MonthName
,(sum(case when year(DATE) = 2016 then sales end)) as 'Total Sales'
,(sum(Distinct(case when year(DATE) = 2016 then area end))) as 'Total Area'
FROM
(
SELECT Number , DATENAME(MONTH, '2015-' + CAST(Number as varchar(2)) + '-1')
'MonthName'
FROM master..spt_values
WHERE Type = 'P' and Number between 1 and 12
) months
CROSS JOIN
(
SELECT DISTINCT location
FROM TempTable1
) tenants
LEFT JOIN TempTable1 t
ON months.monthname = datename(month,t.date) and tenants.location = t.location
GROUP BY months.monthname , tenants.location , months.number
ORDER BY datepart(MM,months.monthname + '01 2000');
我得到了:
location number MonthName Total Sales Total Area
AMALL 1 January 1500.00 110
AMALL 2 February 2200.00 50
AMALL 3 March NULL NULL
...
AMALL 12 December NULL NULL
2 月必须有100
,而我们只得到50
和DISTINCT
。我的错。
所以我删除 DISTINCT
并添加 CTE 以获得您需要的区域:
;WITH cte AS (
SELECT [location],
tenant,
area,
DATEPART(month,[date]) as m,
ROW_NUMBER() OVER (PARTITION BY [location], tenant, DATEPART(month,[date]) ORDER BY [date]) as rn
FROM Temptable1
)
SELECT res.*,
SUM(c.area) as [Total Area]
FROM (
SELECT tenants.[location],
months.number,
months.[MonthName],
SUM(case when year(DATE) = 2016 then t.sales end) as [Total Sales]
FROM
(
SELECT Number,
DATENAME(MONTH, '2015-' + CAST(Number as varchar(2)) + '-1')as [MonthName]
FROM master..spt_values
WHERE Type = 'P' and Number between 1 and 12
) months
CROSS JOIN
(
SELECT DISTINCT [location]
FROM TempTable1
) tenants
LEFT JOIN TempTable1 t
ON months.[monthname] = datename(month,t.[date]) and tenants.[location] = t.[location]
GROUP BY months.[monthname] , tenants.[location] , months.number
) as res
OUTER APPLY (SELECT * FROM cte WHERE [location] = res.location and m = res.number and rn= 1) as c
GROUP BY res.[location], res.number,res.[MonthName]
,res.[Total Sales]
ORDER BY datepart(MM,res.[monthname] + '01 2000');
这个 CTE 产生这个:
location tenant area m rn
AMALL Tenant1 50 1 1 --we need this area
AMALL Tenant1 50 1 2
AMALL Tenant1 50 1 3
AMALL Tenant1 50 2 1 --and this
AMALL Tenant1 50 2 2
AMALL Tenant1 50 2 3
AMALL Tenant2 60 1 1 --and this
AMALL Tenant2 60 1 2
AMALL Tenant2 60 1 3
AMALL Tenant3 50 2 1 -- and this
AMALL Tenant3 50 2 2
AMALL Tenant3 50 2 3
全部带有rn = 1
。然后用你的结果和输出加入(外部应用)这个 CTE:
location number MonthName Total Sales Total Area
AMALL 1 January 1500.00 110
AMALL 2 February 2200.00 100
...
【讨论】:
嗨!只想说我已经用你的概念得到了我想要的答案或代码...... 但是Outer比较复杂,无法成功运行代码,但是你的想法/概念太棒了,我能够理解它并应用到我自己的逻辑中。我还应该将您的答案标记为答案吗? 很高兴您解决了您的问题!我认为您应该将您的答案标记为已接受 - 您的解决方案比我的更简单和清晰(我猜这有点矫枉过正)! 谢谢,没有你和你对分区的想法,我不会进入更简单的解决方案,所以谢谢先生!【参考方案3】:基于 GOFR1 的答案,这非常有帮助,我只需从现有代码中包含两行代码就可以得到答案..
SELECT location , tenant , a.date , sales, area,
, ROW_NUMBER() OVER (PARTITION BY location, tenantcode, DATEPART(month,[date]) ORDER BY a.date) as rn
INTO #Temptable1
FROM SalesTable
SELECT tenants.location , months.number ,months.MonthName
,(sum(case when year(DATE) = 2016 then sales end)) as 'Total Sales'
,(sum(case when year(DATE) =2016 and rn = 1 then area end)) as 'Total Area'
FROM
(
SELECT Number , DATENAME(MONTH, '2015-' + CAST(Number as varchar(2)) + '-1')
'MonthName'
FROM master..spt_values
WHERE Type = 'P' and Number between 1 and 12
) months
CROSS JOIN
(
SELECT DISTINCT locationd , location
FROM #TempTable1
) tenants
LEFT JOIN #TempTable1 t
ON months.monthname = datename(month,t.date) and tenants.location = t.location
GROUP BY months.monthname , tenants.location , months.number
ORDER BY datepart(MM,months.monthname + '01 2000');
刚刚添加了这些行
-
,ROW_NUMBER() OVER (PARTITION BY location,tenantcode, DATEPART(month,[date]) ORDER BY a.date) as rn
根据 Gofr1 的想法添加了该列
-
,(sum(case when year(t.DATE) = @Year1 and rn = 1 then t.sqm end)) as 'Total Area'
替换
(sum(distinct((case when year(DATE) = 2016 then t.sqm end)))) as 'Total Area'
成功了
【讨论】:
【参考方案4】:此表达式将日期截断为月份边界:
DECLARE @VarStart date = '2001-01-01';
DATEADD(month, DATEDIFF(month, @VarStart, [date]), @VarStart)
您可以使用当月第一天的任何开始日期。 可以使用相同的方法将日期截断到任何其他边界(周、日、小时、分钟等)
依赖不同租户在您的样本数据中具有不同区域这一事实是不明智的。
分别计算每月总计,然后在位置和月份上将它们结合在一起。
CTE_MonthlySales
计算每个位置每月的总销售额。
CTE_TenantAreas
为每个在一个月内至少进行过一次销售的租户返回区域。这在CTE_MonthlyAreas
中进一步分组,以获得一个月内销售的总面积。
CTE_Months
生成月份列表。
CTE_Locations
是所有不同位置的列表。
最终的SELECT
(左)将所有内容连接在一起。
WITH
CTE_MonthlySales
AS
(
SELECT
Location
,DATEADD(month, DATEDIFF(month, @VarStart, [date]), @VarStart) AS DateMonth
,SUM(Sales) AS TotalSales
FROM #TempTable1
GROUP BY
Location
,DATEADD(month, DATEDIFF(month, @VarStart, [date]), @VarStart)
)
,CTE_TenantAreas
AS
(
SELECT
Location
,Tenant
,DATEADD(month, DATEDIFF(month, @VarStart, [date]), @VarStart) AS DateMonth
,MIN(Area) AS TenantArea
FROM #TempTable1
WHERE Sales <> 0
GROUP BY
Location
,Tenant
,DATEADD(month, DATEDIFF(month, @VarStart, [date]), @VarStart)
)
,CTE_MonthlyAreas
AS
(
SELECT
Location
,DateMonth
,SUM(TenantArea) AS TotalArea
FROM CTE_TenantAreas
GROUP BY
Location
,DateMonth
)
,CTE_Months
AS
(
SELECT DATEADD(month, Number-1, '2016-01-01') AS DateMonth
FROM master..spt_values
WHERE Type = 'P' and Number between 1 and 12
)
,CTE_Locations
AS
(
SELECT
Location
FROM #TempTable1
GROUP BY
Location
)
SELECT
CTE_Locations.Location
,CTE_Months.DateMonth
,ISNULL(CTE_MonthlySales.TotalSales, 0) AS TotalSales
,ISNULL(CTE_MonthlyAreas.TotalArea, 0) AS TotalArea
FROM
CTE_Months
CROSS JOIN CTE_Locations
LEFT JOIN CTE_MonthlySales
ON CTE_MonthlySales.Location = CTE_Locations.Location
AND CTE_MonthlySales.DateMonth = CTE_Months.DateMonth
LEFT JOIN CTE_MonthlyAreas
ON CTE_MonthlyAreas.Location = CTE_Locations.Location
AND CTE_MonthlyAreas.DateMonth = CTE_Months.DateMonth
ORDER BY
CTE_Locations.Location
,CTE_Months.DateMonth
;
【讨论】:
DATEPART 比计算容易 @Jonny,对于DATEPART
,您必须考虑年份和排序。一般来说,处理正常的完整日期比处理日期部分更容易。有了日期,您可以轻松地对它们进行排序,例如,从 11 月到明年 2 月。您的期限可以超过一年,例如 27 个月。以上是关于SQL查询以在给定条件下明确获取总和的主要内容,如果未能解决你的问题,请参考以下文章
SQL Server:查询以获取表 1 的 Col1 中的值的总和,以获取表 2 的 Col2 中的条件