【问题标题】:Speeding up SQL query when sorting on foreign keys外键排序时加快 SQL 查询
【发布时间】:2009-11-05 20:30:07
【问题描述】:

这更像是一个通用的 SQL 问题,但如果有人知道 Firebird/Interbase 特定优化,我将使用 Firebird 2.5。首先,以下是一个简化的示例架构,用于说明我要解决的问题:

CREATE TABLE users
(
   id INTEGER PRIMARY KEY,
   name VARCHAR(16)
);

CREATE TABLE data_set
(
   id INTEGER PRIMARY KEY,
   name VARCHAR(64)
);

CREATE UNIQUE INDEX data_set_name_idx ON data_set(name);

CREATE TABLE data
(
   user_id INTEGER,
   data_set_id INTEGER,
   data BLOB,
   PRIMARY KEY(user_id, data_set_id)
);

CREATE INDEX data_user_id_idx ON data(user_id);
CREATE INDEX data_data_set_id_idx ON data(data_set_id);

我尝试运行的查询如下:

SELECT users.name, data_set.name, data FROM users, data_set, data
WHERE user_id=XXX AND user_id=users.id AND data_set_id=data_set.id
ORDER BY data_set.name;

用我想要的 user_id 填写“XXX”。所以我正在做的是从 data 表中选择特定用户拥有的所有行,并根据 data_set 名称对结果进行排序。

按原样工作,但问题是 data 表中有超过十亿行,而 data_set 表也不小。单个用户 ID 的结果集可能有数亿行。发生的情况是,为了使 ORDER BY 工作,数据库必须创建大量临时数据,这些数据非常慢并且使用大量磁盘空间。如果没有 ORDER BY,它会很快,但显然不像我需要的那样排序。

一种解决方案是获取 data_set.name 值并将它们放在 data 的 varchar 列中。然后可以将其编入索引并快速排序。这种方法的问题是它会有大量的重复数据并使数据库变得非常庞大。

另一种解决方案是索引视图或索引计算列。据我所知,Firebird 都不支持这些。

还有其他想法吗?

【问题讨论】:

  • 我没有明确的答案,但我认为您可以更多地使用您的索引并使用查询对它们进行基准测试。
  • 为什么要为单个用户提取“数亿行”?不仅处理查询而且将数据传输到客户端都需要时间。
  • @liggett78:哦,我同意传输它需要时间,因为它有很多数据。这就是为什么我要尽可能加快速度。它不需要那么慢,因为它浪费了大量时间为排序创建临时数据。

标签: sql foreign-keys query-optimization firebird firebird2.5


【解决方案1】:

这是相当推测的,但我想知道是否可以将其重组为:

  1. 用户和数据集之间的笛卡尔积,包括关于用户的谓词。
  2. 按数据集名称排序
  3. 加入数据

... 会更有效,尤其是如果您只对查询的前几行感兴趣。

在 Oracle 中,我认为这不会是因为嵌套循环连接的效率比哈希连接低得多,但恐怕我根本不熟悉 firebird。

【讨论】:

    【解决方案2】:

    为什么不索引 data_set.name?

    另外,我会跳过事实表(数据)的主键定义,并为外键放置两个单独的索引,以加快连接速度。 (当然,如果要插入大量记录,索引可能会对插入产生影响)

    如果您需要确保事实表的唯一约束,您可以从将数据传输到该表的作业中完成(我没有关于此的详细信息:)。

    【讨论】:

    • 对不起,我忘了包括那个。是的,data_set.name 已编入索引。我将更新示例架构。索引它并不能解决问题。
    • 然后您应该检查执行计划。可能是您必须进行特定于数据库的优化...
    • 就执行计划而言,数据库基本上是获取整个未排序的结果集并将其放入临时表中,然后它可以索引 ORDER BY 列。本质上就像我在 data 中添加了一个包含 data_set.name 的列,只是在结果集上。由于结果集的大小,这非常慢。我想知道是否有某种方法可以在外键引用的 data 中创建索引,除了使用它引用的值(data_set.name)而不是整数值。
    【解决方案3】:

    尝试在 data_set(id, name) 上定义一个索引并进行试验 - 可能与此处的其他建议结合使用。如果您的要求要求并且 Firebird 支持唯一约束,您可以将现有的 UNIQUE 索引更改为 UNIQUE CONSTRAINT。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-08-10
      • 1970-01-01
      • 1970-01-01
      • 2017-11-25
      • 2011-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多