有几天,我一直在努力提高我的数据库的性能,并且有些问题我仍然对SQL Server数据库中的索引感到困惑。
我会尽量提供尽可能多的信息。
我的数据库目前包含大约10万行,并且会继续增长,因此我正试图找到一种方法让它更快地运行。
我也写信给这张桌子,所以如果你的建议会大大缩短写作时间,请告诉我。
总体目标是选择具有日期范围内特定名称的所有行。
通常会选择超过3,000行...
表架构:
CREATE TABLE [dbo].[reports]
(
[id] [int] IDENTITY(1,1) NOT NULL,
[IsDuplicate] [bit] NOT NULL,
[IsNotValid] [bit] NOT NULL,
[Time] [datetime] NOT NULL,
[ShortDate] [date] NOT NULL,
[Source] [nvarchar](350) NULL,
[Email] [nvarchar](350) NULL,
CONSTRAINT [PK_dbo.reports]
PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
这是我正在使用的SQL查询:
SELECT *
FROM [db].[dbo].[reports]
WHERE Source = 'name1'
AND ShortDate BETWEEN '2017-10-13' AND '2017-10-15'
据我了解,我提高效率的最佳方法是在Source
和ShortDate
上创建非聚集索引,而不会影响写入时间。
我喜欢这样,索引架构:
CREATE NONCLUSTERED INDEX [Source&Time]
ON [dbo].[reports]([Source] ASC, [ShortDate] ASC)
现在我们遇到了让我完全迷失的棘手部分,上面的索引有时会起作用,有时候一半是有效的,有时根本不起作用....
(不确定它是否重要但目前90%的数据库行具有相同的Source,尽管这不会长久保持这样)
通过下面的查询,根本没有使用索引,我正在使用SQL Server 2014,而在执行计划中,它说它只使用聚集索引扫描:
SELECT *
FROM [db].[dbo].[reports]
WHERE Source = 'name1'
AND ShortDate BETWEEN '2017-10-10' AND '2017-10-15'
使用此查询,根本不使用索引,虽然我从SQL Server得到一个建议,创建一个日期优先和源秒的索引...我读到索引应该是查询的顺序是什么?它还说要包括我选择的所有列,这是必须的吗?...再次我读到我应该只在索引中包含我正在搜索的列。
SELECT *
FROM [db].[dbo].[reports]
WHERE Source = 'name1'
AND ShortDate = '2017-10-13'
SQL Server索引建议 -
/* The Query Processor estimates that implementing the following
index could improve the query cost by 86.2728%. */
/*
USE [db]
GO
CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>]
ON [dbo].[reports] ([ShortDate], [Source])
INCLUDE ([id], [IsDuplicate], [IsNotValid], [Time], [Email])
GO
*/
现在我尝试使用SQL Server建议我制作的索引并且它有效,似乎它使用上述查询的100%非聚集索引。
我尝试使用此索引但删除了包含的列并且它不起作用...似乎我必须在索引中包含我正在选择的所有列?
顺便说一下,如果我包含了所有列,那么在使用我制作的索引时也可以。
总结一下:看起来索引的顺序无关紧要,因为它在创建Source + ShortDate
和ShortDate + Source
但由于某种原因,它必须包含所有列......(这将极大地影响对该表的写入?)
非常感谢阅读,我的目标是了解为什么会发生这种事情以及我应该做些什么(不仅仅是解决方案,因为我需要将其应用于其他项目)。
干杯:)
答案 0 :(得分:7)
SQL Server中的索引是长期经验(以及许多小时的挫折)的部分专有技术,也是部分黑魔法。不要过多地打败自己 - 这就像SO这样的地方是理想的 - 很多大脑,经过多个小时的优化,你可以利用的经验。
我读到索引应该按查询的顺序排列?
如果您读到这一点 - 它绝对不正确 - 列的顺序相关 - 但以不同的方式:复合索引(由多个组成)如果您在查询中的索引定义中指定 n最左侧列,则只会考虑列。
经典示例:索引为(city,lastname,firstname)的电话簿。可以使用:这样的索引:
WHERE
子句city
和lastname
的查询中(在“底特律”中找到所有“米勒”)但是,如果您只想搜索firstname
..... 关于您需要的复合索引的技巧,它可以永远不会意识到。但是如果你总是使用索引中的所有列,它们的排序通常并不真正相关 - 查询优化器会为你处理这个。
至于包含的列 - 这些仅存储在非聚集索引的叶级别中 - 它们是 NOT 搜索的一部分索引的结构,您不能为WHERE
子句中包含的列指定过滤器值。
这些包含列的主要好处是:如果您在非聚集索引中搜索,最后,您实际上找到了您正在寻找的值 - 那时您有什么可用的?非聚集索引将列存储在非聚集索引定义(ShortDate
和Source
)中,它将存储聚类键(如果您有一个 - 并且您应该!) - 但没有别的。
因此,在这种情况下,一旦找到匹配项,并且您的查询需要该表中的所有内容,SQL Server必须执行所谓的密钥查找(通常也称为书签查找),其中它采用聚簇键,然后针对聚簇索引执行 Seek 操作,以获取包含所有内容的实际数据页你正在寻找的价值。
如果索引中包含包含列,则非群集索引的叶级页面包含
INCLUDE
声明如果这些列“覆盖”您的查询,例如提供查询所需的所有值,然后SQL Server在找到您在非聚簇索引中搜索的值后完成 - 它可以从非聚簇索引的叶级页面获取所需的所有值,并且不需要在聚类索引中执行另一个(昂贵的)键查找以获取实际值。
因此,尝试始终明确指定只有SELECT
中真正需要的列才有用 - 在这种情况下,你或许可以创建一个有效的覆盖索引,为您提供SELECT
的所有值 - 始终使用SELECT *
使得真的很难或几乎不可能。 ....
答案 1 :(得分:1)
通常,您希望索引从最具选择性(即过滤掉最可能的记录)到最不具有选择性;如果列的基数较低,查询优化器可能会忽略它。
这很直观 - 如果你有一本电话簿,并且你正在寻找名为&#34; smith&#34;的人,最初的&#34; A&#34;,你想要开始寻找&#34;史密斯&#34;首先,然后是&#34; A&#34; s,而不是所有初始为&#34; A&#34;然后过滤掉那些名为&#34; Smith&#34;。毕竟,可能性是26人中有一人拥有最初的&#34; A&#34;。
因此,在您的示例中,我猜您在短日期内拥有大量值 - 因此这是查询优化器尝试过滤掉的第一列。你说你在&#34; source&#34;中有几个不同的值,所以查询优化器可能决定忽略它;在这种情况下,该索引中的第二列也没用。
索引中where子句的顺序无关紧要 - 您可以将它们交换并获得完全相同的结果,因此查询优化器会忽略它们。
编辑:
所以,是的,制作索引。想象一下,你有一堆卡要排序 - 在你的第一次运行中,你想要删除尽可能多的卡。假设它全部均匀分布 - 如果你有超过一百万行的1000个单独的short_dates,这意味着如果你的第一次运行开始于short_date,你最终会得到1000个项目;如果按源排序,则有100000行。
答案 2 :(得分:0)
索引的包含列适用于您选择的列。
由于您执行select *
(这不是一个好习惯),因此无法使用索引,因为它必须查找整个表以获取列的值。
对于您的场景,我会删除默认聚簇索引(如果有)并使用以下语句创建新的聚簇索引:
USE [db]
GO
CREATE CLUSTERED INDEX CIX_reports
ON [dbo].[reports] ([ShortDate],[Source])
GO