SQL Server 索引问题 - 地址查找

Posted

技术标签:

【中文标题】SQL Server 索引问题 - 地址查找【英文标题】:SQL Server index question - address lookup 【发布时间】:2009-04-10 19:11:57 【问题描述】:

我有一个表,在应用程序的生命周期内可能有 10,000 到 1000 万行。此表包含 NACSZ 信息等,我需要编写一个查询来检查匹配的 NACSZ,如下所示:

select 
    * 
from 
    Profiles 
where 
    FirstName = 'chris' and
    LastName = 'test' and
    Address1 = '123 main st' and
    City = 'chicago' and
    State = 'il' and
    Zip = '11111'

我正在尝试优化此查询的表,该查询将在“if exists()”构造中运行,但运气不佳。没有索引和大约 110,000 行测试数据,我得到:

表“配置文件”。扫描计数 1, 逻辑读取 2021,物理读取 0, read-ahead 读取 0,lob 逻辑读取 0, lob 物理读取 0, lob 预读为 0。

(受影响的 1 行)

SQL Server 执行时间:CPU 时间 = 16 毫秒,经过的时间 = 70 毫秒。

SQL Server 执行时间:CPU 时间 = 0 毫秒,经过的时间 = 1 毫秒。

执行计划指示对主键进行聚集索引扫描。我考虑为所有值添加一个索引,但这会创建一个相当大的键,如果可能的话,我想尽量避免这种情况。我的下一个想法是对行之间存在合理差异的内容进行索引,因此我尝试对名字进行索引(尽管我可以使用地址第 1 行或 zip,例如),所以我创建了索引:

create index ix_profiles_firstName on profiles(FirstName)

现在,当我运行相同的查询时,我得到了

表“配置文件”。扫描计数 1, 逻辑读取 171,物理读取 0, read-ahead 读取 0,lob 逻辑读取 0, lob 物理读取 0, lob 预读为 0。

(受影响的 1 行)

SQL Server 执行时间:CPU 时间 = 0 毫秒,经过的时间 = 52 毫秒。

SQL Server 执行时间:CPU 时间 = 0 毫秒,经过的时间 = 1 毫秒。

显然,索引名字有很大的不同。我的问题是,我如何决定是否应该索引名字、姓氏、地址和邮政编码?是否有一个命令我可以运行我的示例数据来告诉我每个字段中值的唯一性?我的理解是我应该尝试索引具有最大唯一性的列,以使索引工作得最好,对吗?

【问题讨论】:

【参考方案1】:

对于您的查询,您应该在所有列上创建一个复合索引:(FirstName, LastName, address1, city, state, zip)

如果您希望在SQL Server 中使用某个索引,请发出:

SELECT  *
FROM    Profiles WITH (INDEX (index_name))
WHERE 
        FirstName = 'chris' and
        LastName = 'test' and
        Address1 = '123 main st' and
        City = 'chicago' and
        State = 'il' and
        Zip = '11111'

我的问题是,我如何决定是否应该索引名字、姓氏、地址和邮政编码?

索引您要过滤的所有这些值。

请注意,您可以有效地过滤索引中的第一列,例如:

SELECT  *
FROM    Profiles
WHERE   FirstName = 'chris'

将使用索引搜索FirstName

SELECT  *
FROM    Profiles
WHERE   FirstName = 'chris'
        AND LastName = 'test'

将使用索引同时搜索FirstNameLastName

SELECT  *
FROM    Profiles
WHERE   FirstName = 'chris'
        AND City = 'chicago'

将使用索引仅在FirstName上进行搜索(您不要在LastName上进行过滤,存在间隙,并且无法使用索引在其他列上进行搜索)

是否有一个命令可以运行在我的示例数据上,它可以告诉我每个字段中值的唯一性?

SELECT   COUNT(DISTINCT FirstName) / COUNT(*)
FROM     Profiles

将向您展示FirstName 互惠选择性。

这个值越大,索引的效率就越低。

我的理解是我应该尝试索引具有最大唯一性的列,以使索引工作得最好,对吗?

是的。

同样,在您的情况下,您应该索引所有列。大多数唯一性肯定是在所有列上。

【讨论】:

我之前尝试过复合索引,但没有得到很好的结果。无论出于何种原因,我只是再次尝试并获得了更好的结果。我使用 jmeter 将随机数据提交到调用此过程的网页,并且每秒能够处理 1500 多个查找/插入。太棒了。 带有复合索引:表'Profiles'。扫描计数 1,逻辑读取 3,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。【参考方案2】:

我的问题是,我如何决定是否应该索引名字、姓氏、地址和邮政编码?

收集您打算使用的所有查询(如果这是唯一一个,那么您就完成了)。然后将查询作为工作负载移交给索引调整向导,并查看建议。

我的理解是我应该尝试索引具有最大唯一性的列,以使索引工作得最好,对吗?

索引越独特,从实际表中查找的结果就越少。 索引越窄,读取速度越快。 (这条规则说明了为什么所有条件列上的复合索引都不好)。

【讨论】:

关于你最后的陈述,这就是我被教导/学到的。但是,在这种情况下,使用复合索引对性能的实际影响似乎并不存在或不相关。我将在周末加载几百万个测试行,看看是否会发生变化。【参考方案3】:

如果这个查询和看起来一样重要,那么我建议您在连接的字段上创建一个派生列;然后在您的查询中明确创建密钥。当然它是多余的,但如果你不必这样做,它最终可能比把它弄乱更简单。

【讨论】:

【参考方案4】:

您有几个选择。正如 Quassnoi 指出的那样,您可以创建一个复合索引。我在稍微不同的场景中使用的另一个选项是根据数据生成唯一密钥。在我的情况下,我正在比较地址并试图防止重复(因为我们将对任何新地址进行地理编码,并且每个地理编码成本 $$)。

无论如何,基本上我们获取了地址的关键部分并创建了一个新密钥(地址、州和邮编)。你可以做同样的事情,然后只比较一列。

一个问题是确保在记录更改时同步此列。您可以查看使用计算列并对可能有助于实现该效果的列进行索引。

【讨论】:

我以前用过你描述的方法。我为在这种情况下没有想到它而感到羞耻。考虑到我正在测试的特定场景,我认为 Quassnoi 的解决方案就足够了,但我也会记住你的。【参考方案5】:

除了其他答案...

您将运行哪种过滤器组合?尝试涵盖最流行的组合。

【讨论】:

查询将始终尝试匹配所有列 - 基本上是为了防止重复注册。 Ta。 Quassnoi 的答案是最好的

以上是关于SQL Server 索引问题 - 地址查找的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server索引的执行计划

SQL Server 查询性能 - 聚集索引查找

SQL Server索引进阶:第一级,索引简介

SQL Server 索引查找Index Seek 索引扫描 Index Scan与索引存储原理详解

为啥当 WHERE 子句包含参数化值时 SQL Server 使用索引扫描而不是索引查找

如何在 SQL Server 2012 中创建的临时表上查找索引列表