【问题标题】:SQL - Joining multiple tables with shared indexSQL - 使用共享索引连接多个表
【发布时间】:2021-05-24 12:37:28
【问题描述】:

我有三个不同列的数据表(每个

它们看起来像:

表A

   id    A1    A2   ...      
   1    xxx   xxx   ...
   2    xxx   xxx   ...
  ...   ...   ...   ...  

表 B

   id1    B1    B2   ...  
   1    xxx   xxx   ...
   2    xxx   xxx   ...
  ...   ...   ...   ... 

表 C

   id2    C1    C2   ...  
   1    xxx   xxx   ...
   2    xxx   xxx   ...
  ...   ...   ...   ... 

我的目标是让他们加入类似的活动:

   id    A1    A2   ...   B1    B2   ...   C1    C2   ...  
   1    xxx   xxx   ...  xxx   xxx   ...  xxx   xxx   ...
   2    xxx   xxx   ...  xxx   xxx   ...  xxx   xxx   ...
  ...   ...   ...   ...  ...   ...   ...  ...   ...   ...

我试图将它们结合在一起使用

CREATE TABLE my_table
SELECT *
FROM table_A
LEFT OUTER JOIN table_B
ON table_A.id = table_B.id1
LEFT OUTER JOIN table_C
ON table_A.id = table_C.id2;

这需要几个小时。但是加入其中两个只需不到 5 分钟的时间:

CREATE TABLE my_table
SELECT *
FROM table_A
LEFT OUTER JOIN table_B
ON table_A.id = table_B.id1

我尝试使用EXPLAIN,结果如下:

   id  select_type    table    type  posibble_keys    key    key_len    ref      rows  filtered   Extra
   1    SIMPLE       table_A    ALL     (Null)      (Null)    (Null)   (Null)   59670   100       
   1    SIMPLE       table_B    ALL     (Null)      (Null)    (Null)   (Null)   39776   100    Using; Using join buffer (Block Nested Loop) where
   1    SIMPLE       table_C    ALL     (Null)      (Null)    (Null)   (Null)   50208   100    Using; Using join buffer (Block Nested Loop) where

我四处搜索,发现帖子说“使用连接缓冲区(块嵌套循环)”是一种低效率的方式,并建议使用 SET optimizer_switch='block_nested_loop=off'; 禁用它。但是,当我尝试这样做时,即使加入两个表也需要 10 多分钟,这似乎是性能的巨大下降。

似乎只有在没有要加入的索引时才使用 BNL,鉴于所有三个表都有“id”列,这是不正确的?

我真的想知道是否有某种方法可以更快地连接这些表。

  1. 也许我应该调整加入代码的方式?
  2. 也许我应该打开/关闭某些选项?
  3. 有什么建议吗?

【问题讨论】:

  • 您需要获取表格的所有列吗?否则你不需要使用select *
  • @D-Shih 是的,不幸的是我真的需要所有这些:(
  • 请提供SHOW CREATE TABLE

标签: mysql sql join pivot-table


【解决方案1】:

如果较小的连接工作得更快,请尝试按这些较小的步骤进行。

从类似的东西开始

CREATE temporary TABLE my_table_AB
SELECT *
FROM table_A
LEFT OUTER JOIN table_B
ON table_A.id = table_B.id

然后

CREATE TABLE my_table
SELECT *
FROM my_table_AB
LEFT OUTER JOIN table_C
ON my_table_AB.id = table_C.id

另一件事是 - 你需要在这里有LEFT JOIN吗?


由于它被标记为已解决,并且我们在讨论过程中找到了解决方案,所以我将其放在这里仅供参考 - 缺少主键的问题。添加后,它按预期工作。

【讨论】:

  • 感谢亚当!当以较小的步骤完成时,第一部分很快约 5 分钟,但据我尝试,第二部分仍然需要很长时间(1 小时 + 并且我终止了查询)。
  • 我想我需要LEFT JOIN?你有什么建议吗?
  • 我本可以预料到您会尝试 :) 当您以不同的顺序执行此操作时,这些时间是否相同?例如,将表 A 与 C 连接,然后与 B 连接,或者从 C 和 B 开始,然后 A?
  • 只有当表 A 中有一些行在表 B 中没有匹配行时,才需要 LEFT JOIN。如果不是这种情况,则应使用 INNER JOIN。跨度>
  • 您是否在这些表上正确创建了主键和外键?如果即使一个值也需要很长时间,则表明它可能正在执行一些表扫描而不是使用索引。
【解决方案2】:

它可能会令人窒息,因为您正在尝试从选择中创建表。问题是新表不能与表中的列名重复。您可能需要明确,例如

CREATE TABLE my_table
SELECT
      a.id,
      a.A1,
      a.A2,
      a.[rest of columns],
      b.B1,
      b.B2,
      b.[rest of columns],
      c.C1,
      c.C2,
      c.[rest of columns]
   FROM 
      table_A a
         LEFT JOIN table_B b
            ON a.id = a.id
         LEFT JOIN table_C c
            ON a.id = c.id

500 行应该几乎是瞬时的

【讨论】:

  • 感谢@DRapp!我确信列名都是不同的。有 1000 个不同的列,所以恐怕我必须使用 SELECT * 而不是全部输入。不幸的是,还有 50000+ 行而不是 500 行
  • @GrumpyCivet,DUPLICATE 列是表 B 和 C 上的“ID”。你有 A.id、B.id、c.id,这会让它窒息。此外,拥有 1000 多个不同的列会散发出糟糕的表结构定义
  • 哦,我实际上有它们,如表 A 中的 id、表 B 中的 id1 和表 C 中的 id2 ...... - 我的错!应该在问题中说清楚。
  • @GrumpyCivet,也就是说,select * 应该很快,但如果它每个表有 1000 列,那真的闻起来很糟糕,应该考虑重构。这可能就是让引擎窒息的原因!
【解决方案3】:

由于我们需要基于所有表的 id 列连接的所有 3 个表中的所有列,所以我们不能在这里使用 INNER JOIN 而不是左连接吗?

【讨论】:

  • 表 A 就像一个内容表 - 我需要表 A 中的所有行:(
  • CREATE TABLE #TempTable (- 所有列定义都在这里 -- 将 Id 作为索引列 id INT PRIMARY KEY CLUSTERED ) INSERT INTO #TempTable (- 在此处列出所有必需的列) SELECT --在此处列出所有必需的列 FROM table_A LEFT JOIN table_B ON table_A.id = table_B.id SELECT * FROM #TempTable A LEFT JOIN table_C ON A.id = table_C.id;
【解决方案4】:

您可能正在三个表之间生成笛卡尔积。您可以使用以下方法计算结果集中的总行数:

select sum(a.cnt * coalesce(b.cnt, 1) * coalesce(c.cnt, 1))
from (select id, count(*) as cnt from a group by id) a left join
     (select id, count(*) as cnt from b group by id) b
     on a.id = b.id left join
     (select id, count(*) as cnt from c group by id) c
     on a.id = c.id;

我的猜测是这个数字比你预期的要大得多。这是因为bc 对于某些ids 都有多行。您还没有解释在这种情况下您想要什么结果,因此很难为您的问题提供实际的解决方案。但这应该可以解释性能问题。

【讨论】:

  • 谢谢!虽然我 100% 确定每个 id 在每个表中只出现一次,例如任何表中都不能有两个 id "1"。
  • 它有 59838。顺便说一句,我认为你的第 5 行有任何额外的右括号
  • @GrumpyCivet 。 . .那不是那么大。查询不应该花那么长时间。
猜你喜欢
  • 2015-09-08
  • 2013-03-29
  • 1970-01-01
  • 1970-01-01
  • 2015-02-10
  • 2015-09-23
  • 1970-01-01
  • 1970-01-01
  • 2021-07-09
相关资源
最近更新 更多