为复杂过滤创建sql索引

时间:2016-07-24 13:29:13

标签: mysql sql sql-server indexing

sql database human 中有表。我有这个表的ui和过滤器形式如下:enter image description here

我只能设置一些值(例如年龄和状态)。如果未指定过滤器项,则不会添加到sql WHERE 条件。 WHERE 条件按照图片描述的顺序组合。因此,如果我想为所有情况创建索引以获得性能提升,我需要创建这些索引:

  • 名字
  • 姓氏
  • 年龄
  • 状态
  • 生日
  • 性别
  • 名字+姓氏
  • 名字+姓氏+年龄
  • 名字+姓氏+年龄+州
  • ...
  • 州+生日
  • 州+生日+性别
  • ...
  • 州+性别

    对我来说这看起来很糟糕。我应该只选择最常用的组合吗?你觉得怎么样?

4 个答案:

答案 0 :(得分:6)

如果您的索引为first name + last name + age + state,则您还不需要first name + last name + agefirst name + last name以及first name。如果你有索引first name + last name + age + state并且用户只搜索"名字"和"姓氏",数据库将能够使用该索引。只要用户以相同的从左到右的顺序指定列作为索引,即使没有指定每列,数据库也能够使用索引。

例如,如果您有索引first name + last name + age + state并且用户指定"名字"和"姓氏",然后数据库将能够使用该索引跳转到匹配的行。但是,如果用户指定"名字"和"年龄",或"名字"和" state",然后数据库将仅部分使用索引跳转到具有匹配名字的行,但是它将必须扫描匹配" age&#34的行;或"州"。如果您想知道为什么这是真的背后的技术细节,请阅读有关数据库索引和B +树的信息。 This是一个很好的解释。

运行单个查询时,数据库也可以使用多个索引。如果你有索引

`last name`
`state`
`age`

用户搜索"姓氏","州"和"年龄",数据库将能够使用所有三个索引快速查找每个字段的匹配行,然后将合并结果,并且不会选择不匹配所有三个索引的行。如果您查看执行计划,您将能够看到它执行此操作。当然,这将比单个索引包含其中包含所有必需字段的速度慢一点,但它会阻止您拥有大量索引。

另请注意,即使存在索引,数据库也可能不一定使用该索引,因为执行行扫描可能更快。例如,以三个不同的索引为例,假设用户搜索"姓氏","名字"和"状态"。因为"姓氏和#34;的组合和#34;名字"具有如此高的选择性(意味着该索引中的大多数值都是唯一的),使用索引来获取与名字和姓氏匹配的所有行可能会更快,然后只需进行简单的迭代扫描这些行找到也具有匹配状态的行,而不是使用state索引,然后连接两个索引返回的行。

当您设计索引时,如果索引的选择性非常低,则索引不会给您带来很大的性能提升(实际上可能比执行全表扫描更糟糕)。例如,性别不是索引的好领域,因为您只有两个可能的值。如果用户只搜索性别,那么无论是否有索引,您都无法获得良好的性能,因为您将返回一半的行。

行换行,全表扫描实际上比使用索引更快。原因是当数据库执行表扫描时,它能够直接跳转到磁盘上的数据页。当它使用索引时,它必须经过一些中间索引页才能实际到达数据存储在磁盘上的位置。适用于像#34;性别"如果你要选择一半的行,那么跟踪表中一半行的索引链接所增加的开销可能会超过仅扫描整个表而不使用索引的成本。

我建议索引

`first name, last name`
`birthdate`
`state`

如果您经常搜索某个特定的字段组合,那么您也可以为其制作索引以加快速度。但是,不要为每个字段组合制作索引。

如果您使用" birthdate"而不是"生日",那么你不需要"年龄"因为你可以根据" birthdate"来计算然后对" birthdate"进行between查询。如果您被迫为"生日"和"年龄"然后你可以索引"年龄"同样。但是,与下面评论的其他用户一样,您必须不断更新您的年龄。我强烈建议不要使用该设计。

最后要考虑的是是否尝试制作覆盖索引。覆盖索引是用户搜索的每个字段都是索引的一部分。例如,假设您的表中包含100个字段,但用户通常只对根据其姓名查找某人的州和年龄感兴趣。因此,很大一部分查询看起来像这样

SELECT STATE, AGE FROM PEOPLE WHERE FIRSTNAME = 'Homer' AND LASTNAME = 'Simpson'

如果您的索引是LASTNAME, FIRSTNAME,那么数据库将会查找" Homer"和"辛普森"在您的索引中(将涉及从磁盘读取一些索引页),使用索引指针转到存储数据记录的磁盘页,读取整个数据页,将其解析为字段,然后返回状态和年龄。

现在,假设您运行相同的查询,但索引为LASTNAME, FIRSTNAME, STATE, AGE。数据库引擎仍然会使用你的索引查找" Homer"和#34; Simpson",但一旦找到合适的索引记录(与上面的工作方式完全相同),该索引记录已经有STATEAGE。因此,数据库可以直接从索引获取查询结果,而无需从磁盘读取数据页。

在表扫描的情况下,覆盖索引可以显着提高性能的情况。假设您的表中有100个字段(因此单行的大小为几百字节或更多)。现在用户运行查询

SELECT FIRSTNAME, LASTNAME, AGE FROM PEOPLE

数据库必须读取整个表格(包括此查询所不需要的所有100个字段)才能获得结果。如果您有索引LASTNAME, FIRSTNAME, AGE,那么数据库可以通过扫描整个索引而不是扫描整个表来获得结果。因为在这种情况下,单个索引元素的字节比单个数据行小得多,所以查询速度会快得多。

在你的表中字段很少的特殊情况下,覆盖索引可能不会非常有用,因为索引中的字段与表中的字段相同,从而无法完成整个目的。但是,对于包含数十个字段的表,其中通常只查询少数字段,覆盖索引可以是加快查询速度的好方法。

答案 1 :(得分:2)

许多指数是一个“坏”的想法 各列的索引无济于事 一个索引是另一个的“前缀”是多余的 不会使用低'基数'(例如gender)的标志或列的索引。

建议:从每列一个索引开始。然后在每个索引的第二列上添加。根据可能一起测试的内容选择第二列。避免同时使用(a,b)(b,a)

然后观看“真实”用户生成的查询类型。相应地调整索引列表。此信息可能会导致一些3列索引。

答案 2 :(得分:-1)

我会采用这种方法..

在索引上有一个关键列非常适合过滤掉行并完全搜索。但是对于你的表单,你需要很多键作为键列,但是有很多键列并不好,它也有一个限制。

因此,如果您没有唯一的列并创建聚簇索引,我建议您确定一些具有唯一或复合索引的列,其中的字段不为null。

我会在生日,年龄上创建聚集索引(只是一个想法,你也可以使用其他列),然后用下面的默认参数创建一个存储过程..

create proc usp_getformdata
(
@firstname varchar(200)= null,
@lastname varchar(200)=null,
@age int=null,
@state varchar(20)=null,
@birthday datetime =null,
@gender varchar(10)=null
)
As
Begin
select 
* from
yourtable
where 
firstname=@firstname
and 
lastname=@lastname

--do for all columns
End

答案 3 :(得分:-1)

一个索引可以用于多个where子句。所以:

(firstname, lastname, age, state)

适用于具有以下等同条件的where子句:

firstname
firstname & lastname
firstname & lastname & age
firstname & lastname & age & state

我建议您为常见案例构建一组索引 - 三个或四个索引。向索引添加多个键,因此可用于越来越精细的搜索。不要将低基数值(例如gender)作为索引中的第一个键,因为仅使用性别过滤器的查询可能需要进行全表扫描。

如果这不能满足您的需求,您可能需要考虑其他访问数据的方法,例如全文索引。