SQL Server 为复杂筛选创建sql索引

woobm2wo  于 2022-11-28  发布在  其他
关注(0)|答案(4)|浏览(187)

在sql数据库 human 中有一个表。我有这个表的用户界面和过滤器表单,如下所示:x1c 0d1x
我只能设置一些值(例如仅限年龄和州)。如果未指定筛选项,则不会将其添加到sql WHERE 条件中。WHERE 条件按图片中描述的顺序组合。因此,如果我想为所有情况创建索引以提高性能,我需要创建以下索引:

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

我看起来不太好。我应该只选择最常用的组合吗?你觉得呢?

dgenwo3n

dgenwo3n1#

如果您有索引first name + last name + age + state,就不需要first name + last name + agefirst name + last namefirst name。如果您有索引first name + last name + age + state,而用户只搜索“名”和“姓”,数据库就可以使用该索引。只要用户按照与您的索引相同的从左到右顺序指定列,数据库将能够使用索引。
例如,如果您有索引first name + last name + age + state,并且用户指定了“名”和“姓”,则数据库将能够使用该索引跳转到匹配的行。然而,如果用户指定了“名”和“年龄”,或者“名”和“州”,则数据库将仅部分使用该索引跳转到具有匹配的名的行,但是它必须扫描与“age”或“state”匹配的行。如果您想了解为什么这是真的背后的技术细节,请阅读有关数据库索引和B+树的内容。This是一个很好的解释。
数据库在运行单个查询时也可以使用多个索引。如果您有

`last name`
`state`
`age`

并且用户搜索“last name”、“state”和“age”,数据库将能够使用所有三个索引来快速找到每个字段的匹配行,然后将结果合并,不匹配所有三个索引的行将不会被选中。如果查看一个执行计划,你可以看到它这样做,当然这比使用一个包含所有必要字段的索引要慢一点,但是它可以防止你使用大量的索引.
还要注意,即使索引存在,数据库也不一定使用该索引,因为进行行扫描可能更快。例如,以上面具有三个不同索引的示例为例,假设用户对“姓氏”、“名字”因为“姓”和“名”的组合具有如此高的选择性(这意味着该索引中的大多数值都是唯一的),则使用该索引获取与名和姓匹配的所有行,然后对这些行执行简单的迭代扫描以查找也具有匹配状态的行,也可以使用state索引,然后连接两个索引返回的行。
在设计索引时,索引不会给您带来太多的性能提升如果索引的选择性很低,那么索引的选择性可能比全表扫描更差。例如,Gender不是一个很好的索引字段,因为只有两个可能的值。如果用户只搜索Gender,无论是否使用索引,您都不会获得良好的性能,因为您将返回一半的行。
逐行扫描,全表扫描实际上比使用索引快,原因是当数据库进行表扫描时,它能够直接跳到磁盘上的数据页。当它使用索引时,它必须经过几个中间索引页才能真正到达数据在磁盘上的存储位置。对于像“gender”这样的字段,我们将选择一半的行,那么跟踪表中一半行的索引链接所增加的开销可能会超过不使用索引而扫描整个表的开销。
在您的示例中,我建议使用以下索引:

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

如果您有经常被搜索的特定字段组合,那么您也可以为它创建索引以加快搜索速度。但是,不要为每个字段组合都创建索引。
如果你使用“birthdate”而不是“birthday”,那么你就不需要“age”,因为你可以根据“birthdate”来计算它,然后对“birthdate”做一个between查询。如果你被迫为“birthday”和“age”设置单独的列,那么你也可以为“age”建立索引。然而,就像下面的另一个用户评论的那样,你必须不断更新你的年龄。我强烈建议你不要这样设计。
最后要考虑的一件事是是否尝试创建 * 覆盖索引 *。覆盖索引是指用户搜索的每个字段都是索引的一部分。例如,假设您的表中有100个字段,但用户通常只对根据姓名查找某人的州和年龄感兴趣。因此,很大一部分查询类似于以下内容

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

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

现在,假设您运行相同的查询,但您的索引为LASTNAME, FIRSTNAME, STATE, AGE。数据库引擎仍将使用您的索引来查找“Homer”和“Simpson”,但一旦找到相应的索引记录(与上面的工作方式完全相同),该索引记录已经具有STATEAGE。因此,数据库可以直接从索引中获得查询结果,而不必从磁盘读取数据页。
在表扫描的情况下,覆盖索引可以显著提高性能。假设表中有100个字段(因此单个行的大小为几百字节或更多)。现在用户运行查询

SELECT FIRSTNAME, LASTNAME, AGE FROM PEOPLE

数据库必须读取整个表(包括此查询不需要的所有100个字段)才能得到结果。如果您有一个索引LASTNAME, FIRSTNAME, AGE,那么数据库可以通过扫描整个索引而不是扫描整个表来得到结果。由于在这种情况下,单个索引元素的字节比单个数据行小得多,因此查询速度会快得多。
在表中字段很少的特定情况下,覆盖索引可能不是很有用,因为索引中的字段将与表中的字段相同,从而使整个目的失效。但是,对于具有几十个字段的表,其中只有少数字段是经常查询的,覆盖索引可能是加快查询速度的好方法。

ecfsfe2w

ecfsfe2w2#

大量的索引是一个“坏”主意。
在单个列上建立索引不会有多大帮助。
如果一个索引是另一个索引的“前缀”,则该索引是冗余的。
将不会使用“基数”较低的标志或列上的索引(例如gender)。
建议:从每列一个索引开始。然后向每个索引添加第二列。根据可能一起测试的内容选择第二列。避免同时使用(a,b)(b,a)
然后观察“真实的”用户生成了什么类型的查询。相应地调整索引列表。这些信息可能会导致一些3列索引。

ffscu2ro

ffscu2ro3#

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

(firstname, lastname, age, state)

适用于具有下列相等条件的where子句:

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

我建议您为常见的情况构建一组索引--三个或四个索引。向索引添加多个键,这样它就可以用于越来越精细的搜索。不要费心将低基数值,如gender作为索引中的第一个键,因为只使用性别过滤器的查询可能需要全表扫描。
如果这不能满足您的需要,您可能需要考虑其他访问数据的方法,如全文索引。

eqqqjvef

eqqqjvef4#

我会采用这种方法。
在索引上有一个键列对于筛选出行和进行精确搜索是很好的。但是对于你的表单,你需要很多键作为键列,但是有很多键列并不好,而且它也有限制。
所以我建议你确定一些列是唯一的或复合索引的字段不会是空的,如果你没有唯一的列,并创建一个聚集索引。
我会创建生日、年龄(只是一个想法,您也可以使用其他列)的聚集索引,然后创建一个带有如下默认参数的存储过程。

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

相关问题