SQL Query在给定条件下明确获得总和

时间:2016-07-20 07:44:12

标签: sql sql-server sql-server-2008

如何仅根据类似的列变量获取列的总和?请参阅给出的示例以进一步了解该问题。

这是示例数据:

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月份的总销售额,包括TENANT 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

根据应用的更新逻辑和代码,我能够使用我的代码上的Distinct命令更正1月总面积的输出,但是我在2月份不正确,只有50,从租户1开始应该是100租户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命令,仍未给出答案。

希望您就主要问题分享您的专家意见。

4 个答案:

答案 0 :(得分:3)

这应该有效

为了能够测试和验证它,我创建了一个临时表

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

审核评论后的新解决方案

 ;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

答案 1 :(得分: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月必须10050只有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。然后使用您的结果和输出JOIN(OUTER APPLY)此CTE:

location    number  MonthName   Total Sales Total Area
AMALL       1       January     1500.00     110
AMALL       2       February    2200.00     100
...

答案 2 :(得分:2)

基于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');

刚刚添加了这些行

  1. ,ROW_NUMBER()OVER(按地点划分,租户代码,DATEPART(月,[日期])ORDER BY a.date)as rn
  2. 根据Gofr1的想法添加了该列

    1. ,(总和(年(t.DATE)= @ Year1和rn = 1然后t.sqm结束的情况))'总面积'
    2. 替换

      (总和(不同((年(DATE)= 2016年,然后t.sqm结束)))))'总面积'

      并且有效

答案 3 :(得分:1)

此表达式将日期截断为月边界:

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
;