4

我正在使用 SQL Server 2012 并有以下表格:Ownership、、、PropertyPerson

该表Person包含有关人的信息,例如名字,姓氏,并且该表具有PersonId作为主键。

该表Property包含有关属性的信息,例如属性区域,属性描述..并且该表具有PropertyId作为主键

因为每个人可以拥有不止一个财产,并且每个财产的所有权可以不止一个人,所以我们之间存在多对多的关系PersonProperty

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

Start DateEnd Date指财产为某人所有的期间,指OwnershipPercent该人在该财产中的份额。

现在我要写一个查询来返回任何一个人同时拥有超过 100% 的财产

例如:

从 2010 年 1 月 1 日到 2012 年 1 月 1 日,财产Id=1属于 #1 的人,他在该财产中的份额为 90%,并且该财产也属于另一个从 2010 年 1 月 1 日到 1- 的 #2 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

4 回答 4

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 和日期。这是有道理的,因为所有权的数量会随着时间而改变。

于 2013-01-02T15:03:33.903 回答
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; 
于 2013-01-02T12:54:39.837 回答
0

以下类似于@Gordon Linoff 的建议,因为它还将范围列表“分解”为开始和结束日期列表。但是,它在结果列表上使用了不同的技术。它还假设只有开始日期包括在内,而结束日期不包括在内。

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),具体取决于它是否与开始日期或结束日期一起出现。因此,unpivotedCTE 评估为以下结果集:

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

在这一点上,这个想法基本上是计算OwnershipPercentat every EventDatefor every的运行总计PropertyId,同时考虑值是增加还是减少。(实际上,您可以在第一阶段将符号合并到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

您还可以在 SQL Fiddle查看(以及玩弄)此查询。

于 2013-01-03T10:29:12.947 回答
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
于 2013-01-02T15:44:38.097 回答