为一系列重叠事件对创建episodeID

问题描述 投票:1回答:1

我有两个可能发生的事件,每个事件可以多次发生,称之为eventA和eventB。 eventA和eventB以及它们的详细信息存在于EventA和EventB表中,它们有一个主键,分别是一个名为eventAID和eventBID的自动增量。

我想识别一个剧集,每个剧集至少包含一个eventA和一个eventB,但每个剧集可以有多个剧集。我想给每个剧集一个独特的剧集ID,剧集ID是一个任意的识别号码,每个剧集都必须是唯一的。

我有一些标准(和SQL语句)将告诉我给定的eventA和给定的eventB是否存在于同一集中。它输出一个表中包含eventAID和eventBID的表。我将此表称为Episode表。

该查询的格式为:

SELECT EventA.eventAID, EventB.eventBID 
INTO Episode
FROM EventA 
INNER JOIN EventB ON *some criteria* 
WHERE *some criteria*;

我有权访问并可以修改此查询。该配对涉及许多标准,这些标准无关紧要。

示例剧集表数据:

eventAID  eventBID
123       456
124       789
125       457
125       458
126       459
127       459
128       790
128       791
129       791

有些eventS和eventS没有合作伙伴,我想忽略它们。 (未显示,它们不出现在Episode表中)

大多数eventA将与一个eventB配对,反之亦然,其中每个都应该获得一个独特的episodeID。

episodeID  eventAID  eventBID
1          123       456
2          124       789

一些eventA将与多个eventB配对,反之亦然,每个eventA也应该获得相同的episodeID。

episodeID  eventAID  eventBID
3          125       457
3          125       458
4          126       459
4          127       459

一些eventA将与多个eventB配对,但其中一个eventB将与多个不同的eventA配对。所有这些都会收到相同的剧集ID。

episodeID  eventAID  eventBID
5          128       790
5          128       791
5          129       791

请注意,上面列出的表是基于条件的SQL语句生成的所有符合条件的eventA-eventB对的表的示例。它们是为了说明我所看到的数据中的某些情况。

对此的最终演示将在原始的EventA和EventB表中,其中已删除未配对的事件。如果您的解决方案不需要该Episode表,请随时取消它。

episodeID  eventAID  eventAdata
1          123       *
2          124       *
3          125       *
4          126       *
4          127       *
5          128       *
5          129       *

episodeID  eventBID  eventBdata
1          456       *
2          789       *
3          457       *
3          458       *
4          459       *
5          790       *
5          791       *

如果给出带有对的带标签的episodeID表(如上例所示),我可以很容易地做到这一点。


单对不是问题。

我可以检测到那些与多个eventB配对的eventA(通过HAVING COUNT(*)> 1)并过滤掉它们。对于具有多个eventA的eventB,反之亦然。

从所有对的列表中删除它们(通过EXCEPT)将只留下一对eventA,只有一个eventB,eventB只有一个eventA。 (示例中的第1集和第2集)

使用ROW_NUMBER将生成一系列递增的episodeID。

然而,问题是第3,4和5集的情况。

3和4是对称的,如果可以识别其对没有进一步配对的eventAID或eventBID(第5集的情况),则可以给出它们episodeID。

第5集要困难得多。 eventAID 128链接到eventBID 790和791,附加到它们的episodeID因此传播到eventAID 129.如果eventAID 129链接到不同的eventBID,那些也会获得相同的episodeID等。

这可能会无限期地持续下去,但是较长的链条比较短的链条更为罕见。

过去的解决方案是使用一个过程,将Episode表中的一行移到临时表中(从Episode中删除它们),然后将临时表中出现的事件ID的行移动到临时表,并重复直到临时表桌子停止增长。

然后所有这些行都获得相同的EpisodeID并写入输出表。清空临时表并重复直到Episode表为空。

这非常不理想,需要很长时间才能运行(1小时+ 15k行剧集表)。当episodeID必须定期重新生成时非常糟糕。

更不用说非常复杂的错误了。

有一个更好的方法吗?我正在寻找通用算法描述而不是代码,但代码也很好。


编辑:一个简单的例子。

EventA表

eventAID  pid  eventAdata  eventAdata2
123       1    1           
124       1    2           
125       2    3           
126       3    4           
127       3    4           
128       3    5           1
129       3    6           
130       4    1

EventB表

eventBID  pid  eventBdata  eventBdata2
456       1    1           
789       1    2           
457       2    3           
458       2    3           
459       3    4           
790       3    5           
791       3    6           1

目前目前的查询

SELECT EventA.eventAID, EventB.eventBID 
INTO Episode
FROM EventA 
INNER JOIN EventB ON EventA.pid = EventB.pid
WHERE EventA.eventAdata = EventB.eventBdata
OR EventA.eventAdata2 = EventB.eventBdata2;

这将创建一个Episode表,其中包含作为剧集一部分的eventS和事件对。

eventAID  eventBID
123       456
124       789
125       457
125       458
126       459
127       459
128       790
128       791
129       791

查询及其标准在功能上仅仅是对“同一集的这些EventA和EventB部分”的测试。

我需要标记EventS和Event表,以便:

episodeID  eventAID  pid  eventAdata  eventAdata2
1          123       1    1           
2          124       1    2           
3          125       2    3           
4          126       3    4           
4          127       3    4           
5          128       3    5           1
5          129       3    6           

episodeID  eventBID  pid  eventBdata  eventBdata2
1          456       1    1           
2          789       1    2           
3          457       2    3           
3          458       2    3           
4          459       3    4           
5          790       3    5           
5          791       3    6           1

在这个简单的玩具示例中,事件数据和事件数据2是1到6的数字或null。 pid是来自另一个表的外键,用于标识属于单个人的记录组。

进一步要求:

剧集中的所有事件和事件必须属于同一个人。

剧集必须至少有一个事件和一个事件B.

奖励积分:

扩展解决方案以包含具有类似数据样式的EventC表...

sql sql-server
1个回答
0
投票

检查下一个查询。

with
  ee as (
    select *
    from(values
      (123, 456), (124, 789), (125, 457),
      (125, 458), (126, 459), (127, 459),
      (128, 790), (128, 791), (129, 791)
    ) t(eva, evb)
  ),
  a as (
    select *,
      row_number() over(order by eva, evb) rn,
      row_number() over(partition by eva order by evb) rna,
      row_number() over(partition by evb order by eva) rnb
    from ee
  )
select
  sum(iif(rna = 1 and rnb = 1, 1, 0)) over(order by rn) evid,
  *
from a;

evid    eva     evb
1       123     456
2       124     789
3       125     457
3       125     458
4       126     459
4       127     459
5       128     790
5       128     791
5       129     791

在以下查询中添加了C事件的数据。

with
  eee as (
    select *
    from(values
      (123, 456, 42), (124, 789, 42), (125, 457, 42), (125, 458, 170), (126, 459, 170),
      (127, 459, 170), (128, 790, 171), (128, 791, 171), (129, 791, 172), (130, 792, 173)
    ) t(eva, evb, evc)
  ),
  a as (
    select *,
      row_number() over(order by eva, evb, evc) rn,
      row_number() over(partition by eva order by evb, evc) rna,
      row_number() over(partition by evb order by eva, evc) rnb,
      row_number() over(partition by evc order by eva, evb) rnc
    from eee
  )
select
  sum(iif(rna = 1 and rnb = 1 and rnc = 1, 1, 0)) over(order by rn) evid,
  *
from a;

evid    eva     evb     evc
1       123     456     42
1       124     789     42
1       125     457     42
1       125     458     170
1       126     459     170
1       127     459     170
2       128     790     171
2       128     791     171
2       129     791     172
3       130     792     173

两个查询都可以检查here

© www.soinside.com 2019 - 2024. All rights reserved.