【问题标题】:Query Performance with NULLNULL 查询性能
【发布时间】:2009-07-01 17:19:11
【问题描述】:

我想知道 NULL 值如何影响 SQL Server 2005 中的查询性能。

我有一个类似这样的表(简化):

ID | ImportantData | QuickPickOrder
--------------------------
1  | 'Some Text'   | NULL
2  | 'Other Text'  | 3
3  | 'abcdefg'     | NULL
4  | 'whatever'    | 4
5  | 'it is'       | 2
6  | 'technically' | NULL
7  | 'a varchar'   | NULL
8  | 'of course'   | 1
9  | 'but that'    | NULL
10 | 'is not'      | NULL
11 | 'important'   | 5

我正在对它进行这样的查询:

SELECT   *
FROM     MyTable
WHERE    QuickPickOrder IS NOT NULL
ORDER BY QuickPickOrder

所以 QuickPickOrder 基本上是一个用于从较大列表中挑选出一些常用项目的列。它还提供了它们向用户显示的顺序。 NULL 值表示它不会出现在快速选择列表中。

我总是被告知,数据库中的 NULL 值在某种程度上是邪恶的,至少从规范化的角度来看,但它是否可以在 WHERE 约束中过滤掉不需要的行?

使用特定的数值(如 -1 或 0)来表示不需要的项目会更好吗?还有其他选择吗?

编辑: 该示例不能准确地表示实际值与 NULL 的比率。一个更好的示例可能会为每个非 NULL 显示至少 10 个 NULL。表大小可能是 100 到 200 行。这是一个参考表,所以很少更新。

【问题讨论】:

    标签: sql sql-server database performance


    【解决方案1】:

    SQL Server 索引 NULL 值,所以这很可能只是在 QuickPickOrder 上的索引上使用 Index Seek,用于过滤和排序。

    【讨论】:

    • 如果表的列 50% 为空(类似于给定的示例数据),我认为它可能倾向于进行索引扫描
    • @KM:为什么要在这里进行索引扫描?它可能会执行表扫描/聚集索引扫描来避免 RID 查找/键查找,但我们这里有一个范围条件,因此索引查找总是更好。
    • 由于大多数值都相同(null),它不会忽略索引并扫描吗?
    • @KM:它可能会这样做,但它不会是索引扫描,它将是表扫描或集群索引扫描(与集群表的表扫描相同)。索引扫描意味着遍历 QuickPickOrder 上的整个索引,过滤掉错误的值,然后使用 Key Lookup / RID Lookup 与表连接以获取 SELECT 子句请求的 *。 Index Seek 的作用相同,但从第一个非 NULL 值开始,所以 NULL 只是剩下的。
    【解决方案2】:

    另一种选择是两个表:

    MyTable:
    
    ID | ImportantData
    ------------------
    1  | 'Some Text'
    2  | 'Other Text'
    3  | 'abcdefg'
    4  | 'whatever'
    5  | 'it is'
    6  | 'technically'
    7  | 'a varchar'
    8  | 'of course'
    9  | 'but that'
    10 | 'is not'
    11 | 'important'
    
    QuickPicks:
    
    MyTableID   | QuickPickOrder
    --------------------------
    2           | 3
    4           | 4
    5           | 2
    8           | 1
    11          | 5
    
    SELECT   MyTable.*
    FROM     MyTable JOIN QuickPicks ON QuickPickOrder.MyTableID = MyTable.ID
    ORDER BY QuickPickOrder
    

    这将允许更新 QuickPickOrder 而不锁定 MyTable 中的任何内容或记录该表的完整行事务。因此,根据 MyTable 的大小以及更新 QuickPickOrder 的频率,可能会有可扩展性优势。

    此外,拥有一个单独的表将允许您在 QuickPickOrder 上添加唯一索引以确保不重复,并且以后可以更轻松地扩展以允许不同类型的 QuickPicks,使其特定于某些上下文或用户等。

    【讨论】:

      【解决方案3】:

      它们对数据库没有负面的性能影响。请记住,NULL 与其说是值,不如说是一种状态。检查 NOT NULL 与将该值设置为 -1 没有区别,除了 -1 可能会破坏您的数据完整性,imo。

      【讨论】:

        【解决方案4】:

        在您的数据库中使用 NULLS 可能会影响 SQL Server 的性能。这有几个原因。

        首先,出现在固定长度列 (CHAR) 中的 NULLS 占据了整个列的大小。因此,如果您有一个 25 个字符宽的列,并且其中存储了 NULL,那么 SQL Server 必须存储 25 个字符来表示 NULL 值。增加的空间会增加数据库的大小,这反过来意味着需要更多的 I/O 开销来查找您要查找的数据。当然,解决这个问题的一种方法是使用可变长度字段。将 NULL 添加到可变长度列时,不会像固定长度列那样不必要地浪费空间。

        第二,在WHERE子句中使用IS NULL子句意味着查询不能使用索引,会进行表扫描。这会大大降低性能。

        第三,使用 NULLS 会导致 Transact-SQL 代码错综复杂,这可能意味着代码运行效率不高或存在错误。

        理想情况下,您的 SQL Server 数据库中应避免使用 NULL。

        不要使用 NULL,而是在您的数据库中使用与此类似的编码方案:

        • NA:不适用
        • NYN:未知
        • TUN:真正未知

        这种方案提供了使用 NULL 的好处,但没有缺点。

        【讨论】:

        【解决方案5】:

        NULL 对我来说看起来不错。性能可能与非空列和常量值基本相同,或者过滤掉所有NULLs 可能更好。

        【讨论】:

          【解决方案6】:

          替代方法是将 QuickPickOrder 规范化为具有外键的表,然后执行内连接以过滤掉空值(或使用 where 子句的左连接来过滤掉非空值)。

          【讨论】:

            【解决方案7】:

            NULL 对我来说也不错。 SQL Server 有多种索引可供选择。我忘记了哪些是这样做的,但有些只是给定范围内的索引值。如果你在被测试的列上有这种索引,NULL 值的记录就不会在索引中,索引扫描会很快。

            【讨论】:

              【解决方案8】:

              在具有索引(或以索引开头)的列中包含大量 NULL 通常有利于这种查询。

              NULL 值不会输入到索引中,这意味着在其中插入/更新具有 NULL 的行不会影响必须更新另一个二级索引的性能。例如,如果只有 0.001% 的行在该列中有非空值,则 IS NOT NULL 查询变得非常高效,因为它只扫描相对较小的索引。

              当然,所有这些都是相对的,如果你的桌子很小,那也没什么区别。

              【讨论】:

                猜你喜欢
                • 2020-04-22
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-04-29
                • 1970-01-01
                • 2011-05-06
                • 2013-08-04
                相关资源
                最近更新 更多