连接表在SQL中使用SUM函数

时间:2013-01-02 12:25:59

标签: sql inner-join sql-server-2012

我正在使用SQL Server 2012并拥有以下表格:OwnershipPropertyPerson

表格Person包含有关人名的信息,例如姓氏和姓氏,此表格中包含PersonId作为主键。

Property包含有关属性的信息,例如属性区域,属性描述..此表格以PropertyId为主键

因为每个人可以拥有多个属性,并且每个属性的所有权可以超过一个人,所以我们在PersonProperty之间存在多对多关系

所以我创建了表Ownership来打破这种关系,所以这个表有PersonIdPropertyId作为外键,以及以下列:PropertyId as“Primary键“,StartDateEndDateOwnershipPercent

Start DateEnd Date是指某人拥有该财产的时间段,而OwnershipPercent是指该人在该财产中所占的份额。

现在我想编写一个查询,以便同时返回多个人拥有的任何属性超过100%

例如:

Id=1属性属于#1从1-1-2010到1-1-2012的人,他在此属性中的份额为90%,此属性也属于另一个#2来自的人1-1-2010到1-1-2012,他在这个房产中所占的份额是80%..我们看是否同时将90 + 80 = 170%相加而这是错误的(因为它会低于100%)同时)

我写了以下查询:

SELECT A.PropertyId
FROM Ownership A INNER JOIN Ownership B
ON a.PersonId <> b.PersonId
AND A.PropertyId = B.PropertyId
AND A.StartDate <= B.EndDate
AND A.EndDate >= B.StartDate
group by A.PropertyId
Having (sum(A.OwnershipPercent)) <=100; 

但是如果我们有一个属于5个人的属性,它会使(5×4)= 20个总和,这是错误的

如何解决这个问题?

4 个答案:

答案 0 :(得分:2)

我认为在所有权表上加入的方法并不完全正确。我看到你想要做什么,但是联接正在创造一对所有者。您想要考虑所有者的集合。

我的方法是创建一个包含每个属性的所有重要日期的表。这将是OwnerShip表中的StartDate和EndDate。然后,让我们看看这些日期的所有权百分比:

select os.PropertyId, thedate, SUM(os.OwnershipPercent)
from ((select PropertyId, StartDate as thedate
       from ownership
      )union
      (select PropertyId, EndDate
       from ownership
      )
     ) driver join
     OwnerShip os
     on driver.PropertyId = os.PropertyId and
        driver.thedate between os.StartDate and os.EndDate
group by os.PropertyId, thedate
having SUM(os.OwnershipPercent) <= 100  -- Do you really want > 100 here?

一个关键的区别是此查询是在PropertyId和日期上聚合的。这是有道理的,因为所有权的数量会随着时间的推移而改变。

答案 1 :(得分:1)

DISTINCT会做对,

SELECT A.PropertyId
FROM Ownership A INNER JOIN Ownership B
ON a.PersonId <> b.PersonId
AND A.PropertyId = B.PropertyId
AND A.StartDate <= B.EndDate
AND A.EndDate >= B.StartDate
group by A.PropertyId
Having (sum(distinct A.OwnershipPercent)) <=100; 

答案 2 :(得分:0)

您可能需要此请求

SELECT PropertyID,
FROM dbo.Ownership
GROUP BY PropertyID, StartDate, EndDate
HAVING COUNT(PersonID) > 1 
  AND SUM(OwnershipPercent) <= 100 --in your question you want > 100

答案 3 :(得分:0)

以下内容类似于@Gordon Linoff's suggestion,因为它还将范围列表“分解”为开始日期和结束日期列表。但是,它在结果列表中使用不同的技术。它还假设只有开始日期是包含性的,而结束日期则不是。

WITH unpivoted AS (
  SELECT
    PropertyId,
    EventDate,
    OwnershipPercent,
    PercentFactor = CASE EventDateType WHEN 'EndDate' THEN -1 ELSE 1 END
  FROM Ownership
  UNPIVOT (
    EventDate FOR EventDateType IN (StartDate, EndDate)
  ) u
)
, summedup AS (
  SELECT DISTINCT
    PropertyId,
    EventDate,
    TotalPercent = SUM(OwnershipPercent * PercentFactor)
                   OVER (PARTITION BY PropertyId ORDER BY EventDate)
  FROM unpivoted
)
SELECT
  s.EventDate,
  s.TotalPercent,
  o.PropertyId,
  o.PersonId,
  o.StartDate,
  o.EndDate,
  o.OwnershipPercent
FROM summedup s
  INNER JOIN Ownership o
     ON s.PropertyId = o.PropertyId
    AND s.EventDate >= o.StartDate
    AND s.EventDate <  o.EndDate
WHERE TotalPercent > 100  -- changed from the original "<= 100"
                          -- based on the verbal description
;

为了解释这是如何工作的,我将假设Ownership的内容如下:

PropertyId PersonId StartDate  EndDate    OwnershipPercent
---------- -------- ---------- ---------- ----------------
1          1        2010-01-01 2012-01-01 80
1          2        2011-01-01 2011-03-01 20
1          3        2011-02-01 2011-04-01 10
1          4        2011-05-01 2011-07-01 40

现在,您可以看到,在第一步,不显示原始表的每一行都被替换为两行,而且每个百分比值都被标记为增量(PercentFactor = 1)或者递减(PercentFactor = -1),取决于它是与起始日期一起出现还是与结束日期一起出现。因此,unpivoted CTE评估以下结果集:

PropertyId EventDate  OwnershipPercent PercentFactor
---------- ---------- ---------------- -------------
1          2010-01-01 80               1
1          2011-01-01 20               1
1          2011-02-01 10               1
1          2011-03-01 20               -1
1          2011-04-01 10               -1
1          2011-05-01 40               1
1          2011-07-01 40               -1
1          2012-01-01 80               -1

此时,我们的想法基本上是为每OwnershipPercent计算每EventDatePropertyId的运行总计,同时考虑该值是递增还是递减。 (事实上​​,你可以在第一阶段将标志合并到OwnershipPercent而不是分配PercentFactor的单独列。我选择后者更好地说明了这个想法,但应该没有表现如果你更喜欢前者,则会受到惩罚。)这就是你在计算总跑数(这是第二次CTE,summedup之后)得到的结果:

PropertyId EventDate  TotalPercent
---------- ---------- ------------
1          2010-01-01 80
1          2011-01-01 100
1          2011-02-01 110
1          2011-03-01 90
1          2011-04-01 80
1          2011-05-01 120
1          2011-07-01 80
1          2012-01-01 0

但请注意,此结果集可能包含重复的行。特别是,如果对于相同的PropertyId,某些范围同时开始或结束,或者某些范围恰好在另一范围的开始日期结束。这就是为什么你可以在这个阶段看到DISTINCT。

现在已知关键日期的总百分比值,那些不超过100的百分比值可以被过滤掉,其余的则加入Ownership,以便访问有助于获得总计的所有权的详细信息。因此,主查询会将此作为最终结果:

EventDate  TotalPercent PropertyId PersonId StartDate  EndDate    OwnershipPercent
---------- ------------ ---------- -------- ---------- ---------- ----------------
2011-02-01 110          1          1        2010-01-01 2012-01-01 80
2011-02-01 110          1          2        2011-01-01 2011-03-01 20
2011-02-01 110          1          3        2011-02-01 2011-04-01 10
2011-05-01 120          1          1        2010-01-01 2012-01-01 80
2011-05-01 120          1          4        2011-05-01 2011-07-01 40

您还可以查看(以及使用)此查询at SQL Fiddle