如何使用索引优化选择查询

Posted

技术标签:

【中文标题】如何使用索引优化选择查询【英文标题】:How to optimize select query with index 【发布时间】:2014-02-01 12:52:13 【问题描述】:

我不是专家。 我有以下查询,其中包含 10 个表 MainTable 有 10 个字段 1st Prime Key 和 9 个名为 TableE1 - 10 的表的其余外键。

以下查询在每个表中进行外连接,我想用索引优化这个查询。

我想知道,我们如何使用索引优化查询,这个查询在 36 秒内获取 10 lacs(100 万)条记录,我们可以减少多少时间?

MainTable 包含 10 lacs(100 万)条记录,TableE1-9 每个表包含 5000 条记录

select M.RecID, 
M.E1, E1.Descr as E1_D, 
M.E2, E2.Descr as E2_D, 
M.E3, E3.Descr as E3_D, 
M.E4, E4.Descr as E4_D, 
M.E5, E5.Descr as E5_D, 
M.E6, E6.Descr as E6_D, 
M.E7, E7.Descr as E7_D, 
M.E8, E8.Descr as E8_D, 
M.E9, E9.Descr as E9_D
from ((((((((tableMain M  
    Left Outer Join TableE1 E1 ON (E1.RecID = M.E1) )
    Left Outer Join TableE2 E2 ON (E2.RecID = M.E2) )
    Left Outer Join TableE3 E3 ON (E3.RecID = M.E3) )
    Left Outer Join TableE4 E4 ON (E4.RecID = M.E4) )
    Left Outer Join TableE5 E5 ON (E5.RecID = M.E5) )
    Left Outer Join TableE6 E6 ON (E6.RecID = M.E6) )
    Left Outer Join TableE7 E7 ON (E7.RecID = M.E7) )
    Left Outer Join TableE8 E8 ON (E8.RecID = M.E8) )
    Left Outer Join TableE9 E9 ON (E9.RecID = M.E9)
Order by RecID

【问题讨论】:

我想在我的应用程序的 datagridview 中显示这个 【参考方案1】:

索引可能对这个查询没有多大帮助,因为这个查询没有过滤。您正在检索一百万条记录。查询花费了多少时间来检索值以及处理查询花费了多少时间?

SQL Server 有一个很好的优化器,它将使用复杂的连接算法进行连接。即使没有索引,查询也很有可能运行良好。

也就是说,每个带有RecIdDescr 的“E”表上的索引可以帮助查询:E1(RecId, Descr)E2(RecID, Descr) 等等。这些是覆盖索引。对于此查询,SQL Server 将使用这些索引,而无需从数据页中读取。仅索引RecId 将无法正常工作,因为仍需要在数据页上查找Descr 数据。

请注意,如果RecId 已经是主键并且Descr 是表中的唯一列,则这些索引将是不必要的(冗余?)。

编辑:

评论太长了(我认为)。

以下是优化此查询的一些想法:

首先,所有行都是必需的吗?例如,您可以添加top 1000 来获得您需要的东西吗?仅将行传递回应用程序就花费了大量时间。考虑将它们放入临时表 (select into)。这可能会运行得更快。

其次,order by 占用了多少时间?尝试在不使用 order by 的情况下运行查询,看看这是否占主导地位。

第三,descr 字段有多长?如果它们很长,即使只有几千也可能占据数据的大小。注意这里的“很长”是指很多 kbytes,而不是几百个字节。

第四,是descr 字段varchar()char()(或nvarchar()nchar())。 char()nchar() 是非常糟糕的选择,因为它们在结果集中占用了大量空间。

第五(可能应该是第一个),看执行计划。您已经提出了一个非常简单的场景,所以我假设执行计划是对第一个表的扫描,并对每个表进行索引查找。如果计划看起来不像这样,那么可能会有优化的机会。

编辑二:

我会重复。将数百兆字节从服务器传输到应用程序需要时间,而 30 秒左右的时间并非不合理。 (返回集有 10 个 id = 40 字节加上描述字段,每条记录可能有 100 字节。)问题在于数据库和应用程序之间的层的设计,而不是数据库性能。

【讨论】:

yes RecID 是主键,descr 是表中的唯一字段,尽管我应该使用 (recid, descr) 创建索引吗? 按照您的描述创建索引后,仍然需要同样的时间 @HaiderAliWajihi 。 . .正如我在答案中所说,如果这些是表中唯一的字段,那么索引就不是必需的。查询的性能取决于正在处理的数据量。 @HaiderAliWajihi 。 . .如果您要将一百万行从数据库传输到应用程序,那么我的第一个猜测是您的应用程序设计很差。您应该至少在数据库中进行一些处理。 @HaiderAliWajihi 。 . .我会重复一遍。将数百兆字节从服务器传输到应用程序需要时间,而 30 秒左右的时间并非不合理。 (返回集有 10 个 ids = 40 字节加上描述字段。)问题是数据库和应用程序之间的层的设计,而不是数据库性能。【参考方案2】:

如果您的表TableE1-TableE9 有很多记录,则需要在所有九个表中的RecID 上创建索引。 我猜你目前有很多记录并且没有索引,因为对于这样一个简单的查询来说,30 秒真的很慢。

【讨论】:

我在所有primekeys上都有聚集索引,TableE1 - 9,在每个表中它包含5000条记录【参考方案3】:

让我们尝试以下步骤。

在包含所有列(E1 到 E9)的主表上创建一个非聚集索引。 在 RecID 列的每个子表上创建一个非聚集索引。

让我知道 cmets/结果。

【讨论】:

我在所有素数键 TableE1 - 9 上都有聚集索引,在每个表中它包含 5000 条记录。聚集索引值得吗?或者我必须只制作非聚集索引 我创建了你描述的索引,但它仍然需要同样的时间 好的。删除子表上的非聚集索引。只保留第一个,然后试一试。 相同的结果,没有任何改变 将主表结果保存在临时表中并加入所有表。【参考方案4】:

试试这个:

SELECT  pvt.*
FROM 
(
    SELECT z.RecID, /*z.ID1, z.ID2, z.ID3 ...,*/ z.FK_ID_Type, t.Descr
    FROM 
    (
        SELECT  x.RecID, /*x.ID1, x.ID2, x.ID3 ...,*/ y.*
        FROM    dbo.MainTable x 
        CROSS APPLY 
        (
            SELECT  x.E1, CONVERT(TINYINT, 1) -- E1
            UNION ALL 
            SELECT  x.E2, CONVERT(TINYINT, 2) -- E2
            UNION ALL 
            SELECT  x.E3, CONVERT(TINYINT, 3) -- E4
            UNION ALL 
            SELECT  x.E4, CONVERT(TINYINT, 4) -- E4
            UNION ALL 
            SELECT  x.E5, CONVERT(TINYINT, 5) -- E5
            UNION ALL 
            SELECT  x.E6, CONVERT(TINYINT, 6) -- E6
            UNION ALL 
            SELECT  x.E7, CONVERT(TINYINT, 7) -- E7
            UNION ALL 
            SELECT  x.E8, CONVERT(TINYINT, 8) -- E8
            UNION ALL 
            SELECT  x.E9, CONVERT(TINYINT, 9) -- E9
        ) y (FK_ID, [FK_ID_Type])
    ) z INNER HASH JOIN -- or INNER MERGE JOIN
    (
        SELECT t1.RecID, t1.Descr, CONVERT(TINYINT, 1) FROM dbo.TableE1 t1
        UNION ALL 
        SELECT t2.RecID, t2.Descr, CONVERT(TINYINT, 2) FROM dbo.TableE2 t2
        UNION ALL 
        SELECT t3.RecID, t3.Descr, CONVERT(TINYINT, 3) FROM dbo.TableE3 t3
        UNION ALL 
        SELECT t4.RecID, t4.Descr, CONVERT(TINYINT, 4) FROM dbo.TableE4 t4
        UNION ALL
        SELECT t5.RecID, t5.Descr, CONVERT(TINYINT, 5) FROM dbo.TableE5 t5
        UNION ALL
        SELECT t6.RecID, t6.Descr, CONVERT(TINYINT, 6) FROM dbo.TableE6 t6
        UNION ALL
        SELECT t7.RecID, t7.Descr, CONVERT(TINYINT, 7) FROM dbo.TableE7 t7
        UNION ALL
        SELECT t8.RecID, t8.Descr, CONVERT(TINYINT, 8) FROM dbo.TableE8 t8
        UNION ALL
        SELECT t9.RecID, t9.Descr, CONVERT(TINYINT, 9) FROM dbo.TableE9 t9
    ) t(ID, Descr, [Type]) ON z.FK_ID = t.ID AND z.FK_ID_Type = t.[Type]
) v
PIVOT( MAX(v.Descr) FOR v.FK_ID_Type IN ([1], [2], [3], [4], [5], [6], [7], [8], [9]) ) pvt;

【讨论】:

thnx 回复,您可能已经花时间编写此 sql,我感谢您的回复。但是这个 sql 现在需要 31 秒 您的解决方案的持续时间是多少?您能否发布您的解决方案和我的解决方案的实际执行计划(使用 XML 格式)? 我的查询需要 36 秒 两种解决方案的执行计划(XML 格式)可能会有所帮助。 执行计划太长,这里不能附上

以上是关于如何使用索引优化选择查询的主要内容,如果未能解决你的问题,请参考以下文章

您如何优化这个复杂的 sql 查询,然后选择正确的表索引

mysql优化之索引优化

ElasticSearch中文索引优化问题

mysql索引和查询优化

mysql索引和查询优化

mysql性能优化-慢查询分析,优化索引最佳实践