【问题标题】:Bad performance when joining two sets based on a基于 a 加入两组时性能不佳
【发布时间】:2022-10-15 01:59:47
【问题描述】:

为了更好地说明我的问题图片,以下数据集包含包含“范围”动物的房间。为了表示范围,每只动物都在单独的表中分配了一个序列号。有不同的动物类型,每种动物的顺序都是“重置”的。

表 A

RoomId StartAnimal EndAnimal GroupType
1 Monkey Bee A
1 Lion Buffalo A
2 Ant Frog B

表 B

Animal Sequence Type
Monkey 1 A
Zebra 2 A
Bee 3 A
Turtle 4 A
Lion 5 A
Buffalo 6 A
Ant 1 B
Frog 2 B

期望的输出

根据开始-结束条目获取每个房间的所有动物,例如

RoomId Animal
1 Monkey
1 Zebra
1 Bee
1 Lion
1 Buffalo
2 Ant
2 Frog

我已经能够通过首先创建一个视图,其中房间具有其开始和结束序列号,然后将它们与比较范围的动物列表连接起来,从而获得所需的输出。

问题是这在我的真实数据集中表现不佳,其中有大约 1 万个房间和大约 34 万只动物。有没有我没有看到的不同(更好)的方法来解决这个问题?

我正在使用的示例小提琴:https://dbfiddle.uk/RnagCTf0

我试过的查询是

WITH fullAnimals AS (
    SELECT DISTINCT(RoomId), a.[Animal], ta.[GroupType], a.[sequence] s1, ae.[sequence] s2
    FROM  [TableA] ta
        LEFT JOIN [TableB] a ON a.[Animal] = ta.[StartAnimal] AND a.[Type] = ta.[GroupType]
        LEFT JOIN [TableB] ae ON ae.[Animal] = ta.[EndAnimal] AND ae.[Type] = a.[Type]
)
SELECT DISTINCT(r.Id), Name, b.[Animal], b.[Type]
FROM [TableB] b
    LEFT JOIN fullAnimals ON (b.[Sequence] >= s1 AND b.[Sequence] <= s2)
    INNER JOIN [Rooms] r ON (r.[Id] = fullAnimals.[RoomId]) --this is a third table that has more data from the rooms
WHERE b.[Type] = fullAnimals.[GroupType]

谢谢!

【问题讨论】:

  • 在问题本身中发布表定义和实际查询。 340K 不是很多数据。如果这些列被索引覆盖,那么您应该没有问题。
  • JOIN 和 sequence 列是否已编入索引?

标签: sql sql-server join set query-optimization


【解决方案1】:

删除聚合的一种选择是使用以下联接:

  • 在 TableA 和 TableB 之间,收集“a.StartAnimal“ ID
  • 在 TableA 和 TableB 之间,收集“a.EndAnimal“ ID
  • 在TableB和前两个Table之间,只有b.Sequence在两个值之间的行a.StartAnimal“身份证和”b.StartAnimal“ id,关于匹配”类型”。
  • 在表 A 和房间之间,收集房间信息
SELECT r.*, b.Animal, b.Type
FROM       TableA a
INNER JOIN TableB b1 ON a.StartAnimal = b1.Animal
INNER JOIN TableB b2 ON a.EndAnimal = b2.Animal
INNER JOIN TableB b  ON b.Sequence BETWEEN b1.Sequence AND b2.Sequence
                    AND a.GroupType = b.Type
INNER JOIN Rooms r   ON r.Id = a.roomId 

检查更新的演示 here

【讨论】:

  • 好像我把事情复杂化了,这更干净,而且我的表现更好。
  • 哦@lemon,如果有一个额外的表保存动物的历史记录,我们必须只返回动物在该表中的结果?需要注意的是,动物在历史表中可以有多个记录。如果我对该动物进行额外的 INNER JOIN,那么由于历史表中有多个条目,它会重复结果
  • 在这种情况下,您首先需要在该表上应用 DISTINCT,以检索每行(动物)的唯一值,然后在您当前获得的输出上应用“过滤连接”。该聚合的权重取决于这个新表的基数。最后,大量的连接和 Panagiotis 关于索引的建议只能进一步提高整个连接匹配操作的整体速度。
  • 我尝试通过使用 WITH AS(在此处获取不同的动物)语法来做到这一点(或类似的事情),然后在您的答案中使用该输出作为原始查询中的附加 INNER JOIN,但它似乎已经降低了性能。有没有办法在 JOIN 本身中获取唯一 ID?
  • 如果您想避免聚合,则需要应用过滤。您可以尝试计算 ROW_NUMBER() OVER(PARTITION BY Animal) AS rn,然后在连接条件中使用两个条件:一个将动物与动物匹配,另一个强制 rn = 1 (ON cte.Animal = b.Animal AND cte.rn = 1)。如果这表明效率低下,那么不幸的是,除了我们在 cmets 部分中已经说过的内容之外,您将无法做太多事情。
猜你喜欢
  • 1970-01-01
  • 2016-06-21
  • 1970-01-01
  • 1970-01-01
  • 2014-08-10
  • 2012-10-11
  • 1970-01-01
  • 2017-06-26
  • 1970-01-01
相关资源
最近更新 更多