【问题标题】:Query on table with nullable foreign keys使用可为空的外键查询表
【发布时间】:2013-02-21 07:44:36
【问题描述】:

假设我有一张表,上面有 5 个可以为空的外键。对于每一行,其中只有 1 个具有值。

现在,这个表已经增长了很多,并且包含大约 220 万条记录。

在大多数情况下,我只对查询表 c.q 的“部分”感兴趣。那些可以为空的外键具有值的记录。毫不奇怪,仅获取所需的子集需要很长时间,使用以下查询:28 秒来获取一个特定子集的 170 万条记录。

SELECT * FROM Table WHERE Col IS NOT NULL

我真的很感兴趣如何加快速度,尤其是在不更改数据模型的情况下是否可以实现这一点。

【问题讨论】:

  • 哪种类型的 RDBMS?还是您打算将 [sql] 表示为 [sql-server]?
  • 1.7 / 2.2 约为 75%。这表明您的查询可能必须获取 每个 磁盘页面。仅当您的工作集适合核心(可能确实如此)时,索引才会有所帮助
  • 如果 1.7m 行代表一个子集,它必须比其他子集大得多(总共约 0.5m 行)。在调整具有如此大量数据的查询时,您的选择有限。
  • 是的,这是一个极端情况:tje 子集真的这么大:(
  • 您是从编程语言中调用它吗?当我从 Ruby on Rails 调用数据库时,ActiveRecord 需要很长时间,尽管数据库响应很快。

标签: sql sql-server optimization normalization


【解决方案1】:

您是否尝试过将该表与相关外键引用的另一个表连接起来?这将删除外键为空的所有条目。
我预计加入运行会非常缓慢,但您可能会感到惊喜。

另外,您是否有相关外键的索引?

【讨论】:

    【解决方案2】:

    如果您要根据这些表是否为空来对数据进行分区,您可能会加快速度 - 所有带有Col != NULL 的数据都将位于一个分区中。当然,这假设这 5 列是该表中最重要的部分。

    CREATE PARTITION FUNCTION MyCol1 (int)
    AS RANGE LEFT FOR VALUES (NULL)
    
    CREATE PARTITION SCHEME MySchemeForCol1
    AS PARTITION MyCol1
    TO Col1FileGroup
    

    不过,重构表会有所改善,而且我会在将太多时间用于对数据进行分区之前先去那里。像这样的分区仍然会将事物保留在同一个表中,但会在磁盘上隔离......但是将表重构为一系列链接表可能更划算。

    如果您在分区方面有任何运气,请务必让大家知道并发布解决方案。

    【讨论】:

      【解决方案3】:

      如果我无法更改基表,我将创建一个视图,将五个非规范化列转换为两列:原点和值。所以假设你有这个:

      Table notNormal (
      primaryKey,
      someValue,
      foreignNotNormal01,
      foreignNotNormal02,
      foreignNotNormal03,
      foreignNotNormal04,
      foreignNotNormal05
      )
      

      我会创建这个:

      CREATE MATERIALIZED VIEW fixedThis AS
      SELECT primaryKey, someValue, 'FKCOL01' AS foreignNotNormalOrigin, foreignNotNormal01 AS foreignNotNormalColValue
      WHERE foreignNotNormal01 IS NOT NULL
      UNION
       ...
       ...
      SELECT primaryKey, someValue, 'FKCOL01' AS foreignNotNormalOrigin, foreignNotNormal05 AS foreignNotNormalColValue
      WHERE foreignNotNormal05 IS NOT NULL
      

      然后我对 fixedThis 的 SELECTS 只显示外键列中的值的结果,无论它们可能是什么。

      如果您只是想要一个简单的加速,那么索引是可行的方法,但实际上,您需要修复数据模型。

      【讨论】:

        【解决方案4】:

        唯一可以加速的东西

        SELECT * FROM Table WHERE Col IS NOT NULL
        

        是上列的索引。

        但是,将 170 万条记录从服务器传输到数据库客户端可能需要一段时间,尤其是通过网络传输时。你唯一能做的就是减少你发送的数据量,比如在 SELECT 中只指定你需要的字段。

        【讨论】:

        • 这是假设你不能对客户端和服务器之间的网络连接做任何事情。
        • 我正在执行的测试是在我的本地开发机器上进行的,这是一台正常的机器。索引已经存在于列中。我宁愿有多张桌子。
        • 另外,@wildplasser 可能是对的。使用索引,数据库可能知道要选择哪些记录,但磁盘访问(以及所需时间)可能类似于非索引查询。
        • 我的意思是,如果您在本地计算机上无需担心网络延迟,并且该列已编入索引,那么您就无法减少时间。
        • 我们或许应该重构模型的这一部分。我们从来没有真正感到舒服,但有点强加于人。
        猜你喜欢
        • 2014-08-16
        • 2017-11-25
        • 1970-01-01
        • 1970-01-01
        • 2016-10-12
        • 2018-06-18
        • 2017-01-04
        • 2014-01-17
        • 1970-01-01
        相关资源
        最近更新 更多