【问题标题】:Join tables In SQL with SUM function使用 SUM 函数在 SQL 中连接表
【发布时间】:2013-01-02 12:25:59
【问题描述】:

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

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

Property 保存有关属性的信息,例如属性区域、属性描述.. 并且此表以PropertyId 作为主键

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

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

Start DateEnd Date是指财产为某人所有的时期,OwnershipPercent是指该人的财产份额。

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

例如:

Id=1 的房产在 2010 年 1 月 1 日至 2012 年 1 月 1 日期间属于 #1 的人,他在该房产中的份额为 90%,该房产也属于另一个 1-2 号的人从 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 的总和,这是错误的

如何解决这个问题?

【问题讨论】:

  • 与其写太多的文本,不如只显示表格架构;)带有一些示例数据...
  • @MahmoudGamal 你能帮忙吗:D
  • 我不是在开玩笑。预期输出和样本数据更快地吸引答案。

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


【解决方案1】:

我认为连接所有权表的方法不太正确。我知道您正在尝试做什么,但是加入正在创建一对所有者。相反,您想考虑一组所有者。

我的方法是创建一个表格,其中包含每个属性的所有重要日期。这将是 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 和日期。这是有道理的,因为所有权的数量会随着时间而改变。

【讨论】:

    【解决方案2】:

    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; 
    

    【讨论】:

    • 您的查询返回零结果... ;)
    • 可能这就是你想要的。检查您的数据。
    • 您的查询不正确。两个不同的所有者可能具有相同的所有权百分比。你只会数一次。实际上,即使总所有权超过 100%,拥有 50% 所有权的三个所有者也会包含在此结果集中。
    • 是的。那么 sum() over (partition by personId) 会做对吗?
    【解决方案3】:

    这个请求对你来说可能是必要的

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

    【讨论】:

    • 如果我们假设重合范围只能完全相同,这将起作用。然而,实际上,范围可能会相交或嵌套,但并不完全相同。
    【解决方案4】:

    以下类似于@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
    

    此时,这个想法基本上是计算每个EventDateOwnershipPercent 对于每个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
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-01
      • 1970-01-01
      • 2013-10-26
      • 2017-04-26
      相关资源
      最近更新 更多