【问题标题】:sql server multi columns index & query filter has the first and the last columns in the index keyssql server 多列索引和查询过滤器在索引键中有第一列和最后一列
【发布时间】:2020-01-10 14:54:05
【问题描述】:

如果我有带有列键的多列索引:col_1、col_2 和 col_3

如果查询在 WHERE 子句中有以下条件,是否会使用此索引: col_1 = any_value AND col_3 = any_value

(索引键中的第二列未添加到 WHERE 子句中)

这是另一个例子: 如果索引有 10 列并且列键按此顺序: col_1, col_2, ...., col_10 然后,我运行了这个查询: 从 X 中选择 col_1,col_2, ..., col_10 其中 col_1 = any_value AND col_5 = any_value AND col_10 = any_value

还有我的问题:在这种情况下是否会使用索引??

【问题讨论】:

  • 我认为你需要更好地解释你的问题是什么
  • 检查执行计划。索引可能仅用于col_1,优化器将根据实际数据的统计信息来决定。如果例如col_1 有很少的不同值,那么使用这个索引是没有意义的

标签: sql-server sql-server-2012


【解决方案1】:

新的答案,因为你的问题现在对我来说更清楚了

不,不会使用索引。只有在查询col_1 OR col_1/col_2 OR col_1/col_2/col_3 时,才会/可能使用索引。请检查您的查询的执行计划。多列索引的顺序确实很重要:查看这个问题以了解有关此主题的一些讨论 Multiple Indexes vs Multi-Column Indexes

如果您认为您更有可能查询col_1col_3,为什么不在这两列上创建一个多列索引?

【讨论】:

  • 谢谢。但是在给定的链接中,如果我在 WHERE-cluase 中有“state”和“zip”,并且知道索引中列键的顺序如下:“state”、“county”、“zip”。 (索引中的第二列在 WHERE-cluase 中没有提到)
  • 索引可能被使用,如果优化器认为这比使用不同的索引更便宜时寻找col_1
  • 重写了我的答案,因为您的问题现在得到了更好的描述。
  • @PanagiotisKanavos 是的。事实上,正如您所说:应该检查执行计划以真正了解 SQL Server 如何处理查询。
  • 如果我将 WHERE 子句更新为如下所示,我能否强制优化器使用此索引: col_1 = any_value AND (col_2 is null OR col_2 is not null) AND col_3 = any_value
【解决方案2】:

它可能会被使用。这取决于许多因素,主要是您的数据(以及有关您的数据的统计信息)和您的查询。

TL/DR;您需要根据自己的数据和自己的查询对此进行测试。可能会使用索引。

您应该对您拥有或期望拥有的数据进行尝试。创建一些测试数据非常容易,您可以在这些数据上测试查询并尝试不同的索引。您可能还需要重新考虑索引中列的顺序,col_1 真的是索引中最好的列吗?

以下是一个非常具体的场景,我们只能从中得出结论,有时可以在与您的类似场景中使用索引。

考虑下面的这种情况;该表包含 1M 行,并且在 (a, b, c) 上只有一个非聚集索引。请注意,D 列中的值非常大。

下面的前 4 个查询使用了索引,只有第五个查询没有。

为什么?

Sql Server 需要弄清楚如何在读取最少数据的同时完成查询。有时,即使查询过滤器与索引不完全匹配,SQL Server 也更容易读取索引而不是表。

在查询 1 和 2 中,查询实际上会在索引上执行搜索,这非常好。它知道 A 列是执行 Seek 的好候选。

在查询 3 和 4 中,它需要扫描整个索引以找到匹配的行。它仍然使用索引。

在查询 5 中,SQL Server 意识到扫描实际表而不是索引更容易。

IF OBJECT_ID('tempdb..#peter') IS NOT NULL DROP TABLE #peter;
CREATE TABLE #peter(a INT, b INT, c VARCHAR(100), d VARCHAR(MAX));

WITH baserows AS (
    SELECT * FROM master..spt_values WHERE type = 'P'
),
numbered AS (
SELECT TOP 1000000 
    a.*, rn = ROW_NUMBER() OVER(ORDER BY (SELECT null))
FROM baserows a, baserows b, baserows c
)

INSERT #peter
        ( a, b, c, d )
SELECT 
    rn % 100, rn % 10, CHAR(65 + (rn % 60)), REPLICATE(CHAR(65 + (rn % 60)), rn)
FROM numbered

CREATE INDEX ix_peter ON #peter(a, b, c);



-- First query does Seek on the index + RID Lookup.
SELECT * FROM #peter WHERE a = 55 AND c = 'P'
-- Second Query does Seek on the index.
SELECT a, b, c FROM #peter WHERE a = 55 AND c = 'P'
-- Third query does Scan on the index because the index is smaller to scan than the full table.
SELECT a, b, c FROM #peter WHERE c = 'P'
-- Fourth query does a scan on the index
SELECT a, b, c FROM #peter WHERE b = 22
-- Fifth query scans the table and not the index
SELECT MAX(d) FROM #peter

在 SQL Server 2014 上测试。

【讨论】:

    【解决方案3】:

    索引肯定会被使用,但不会有效。

    我做了一个实验 (SQL Server),结果如下 [IX_AB 是 a、b 上的索引],我可以将您的问题与它联系起来。

    这些是结论

    1. 如果您使用 col1、col2 和 col3 创建索引并仅传递 col1 和 col3,则索引将仅过滤 col1 值,然后将从那里检索的数据以编程方式过滤 O(N),其中 N 是标记为的记录索引。
    2. 将中间值作为“not null”或“null”传递没有帮助。

    【讨论】:

      猜你喜欢
      • 2012-07-31
      • 2020-08-24
      • 1970-01-01
      • 1970-01-01
      • 2023-03-04
      • 1970-01-01
      • 1970-01-01
      • 2015-12-29
      • 1970-01-01
      相关资源
      最近更新 更多