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,而我们只得到50DISTINCT。我的错。

所以我删除 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 中的条件

SQL 查询中分组 COUNT 的总和

sql查询提供的jtable列总和

sql怎么添加自定义列并且将此列的数据作为条件查询

SQL 连接查询以获取借方余额不等于 0 的借方余额的项目名称、日期和总和?

我可以从 JPA 查询对象中获取 SQL 字符串吗?