3

我有一张HISTORY有 900 万条记录的表。我需要找到按年、按月创建的记录。我正在使用查询 1,但是它超时了几次。

SELECT 
    year(created) as year, 
    MONTHNAME(created) as month, 
    count(*) as ymcount  
FROM 
    HISTORY 
GROUP BY 
    year(created), MONTHNAME(created);

我决定添加where year(created),这次查询需要 30 分钟(是的,需要很长时间)才能执行。

SELECT 
    year(created) as year, 
    MONTHNAME(created) as month, 
    count(*) as ymcount  
FROM 
    HISTORY 
WHERE 
    year(created) = 2010
GROUP BY  
    year(created), MONTHNAME(created) ;

我打算在created时间戳列上添加一个索引,但是在这样做之前,我需要意见(因为索引这么大的表需要很长时间)。

created(timestamp)考虑到列上使用了年份函数,在列上添加索引会提高性能吗?

4

5 回答 5

4

索引并没有真正的帮助,因为您已经形成了查询,因此它必须执行完整的表扫描、索引或无索引。您必须形成where子句,使其格式为:

where field op constant

field当然,你的领域在哪里;op= <= => <> between in等,常量要么是直接常量,要么是42可以执行一次并缓存结果的操作,getdate()

像这样:

where created >= DateFromParts( @year, 1, 1 )
  and created < DateFromParts( @year + 1, 1, 1 )

DateFromParts函数将生成一个在查询期间保持有效的值。如果created被索引,那么现在优化器将能够准确地寻找正确日期的开始位置,并告诉该范围内的最后一个日期何时被处理并且它可以停止。您可以在其他任何地方保留year(created)- 只需从where子句中删除它即可。

这称为可搜索性,您可以在其上搜索各种好的信息。

PS 这是 Sql Server 格式,但您应该能够在您使用的任何 DBMS 中计算“指定年份的开始”和“指定年份之后的年初”。

于 2014-06-21T01:38:38.740 回答
1

您需要所谓的日历表(特定示例使用 SQL Server,但该解决方案应该具有适应性)。然后,您需要很多索引(因为写入很少,这是用于分析的主要维度表)。

假设您有一个如下所示的最小日历表:

CREATE TABLE Calendar (isoDate     DATE,
                       dayOfMonth  INTEGER,
                       month       INTEGER,
                       year        INTEGER);

...在 [ dayOfMonth, month, year, isoDate] 上有一个索引,您的查询可以这样重写:

SELECT Calendar.year, Calendar.month,
       COUNT(*) AS ymCount
FROM Calendar
JOIN History
  ON History.created >= Calendar.isoDate
     AND History.created < Calendar.isoDate + 1 MONTH
WHERE Calendar.dayOfMonth = 1
GROUP BY Calendar.year, Calendar.month

自动将WHERE Calendar.dayOfMonth = 1结果限制为每年 12 个。范围的开始通常与索引一起定位(给定 SARGable 数据),范围的结束也是如此(是的,在列上进行数学运算通常会使索引不合格......在使用数学的一侧。如果优化器非常聪明,它将生成一个包含范围开始/结束的虚拟中间表)。

因此,查询的基于索引(并且可能是仅索引)访问。学会喜欢索引维度表,它可用于范围查询(日历表是最有用的表之一)。

于 2014-06-22T22:43:19.117 回答
1

当索引有助于缩小读取的行数时,将使用索引。

当它完全避免读取表格时,它也将被使用。当索引包含查询中引用的所有列时就是这种情况。

在您的情况下,唯一引用的列是created,因此在此列上添加索引应该有助于减少必要的读取并改善查询的整体运行时间。但是,如果created是表中的唯一列,则索引不会在第一个查询中更改任何内容,因为它不会减少要读取的页数。

即使使用大表,您也可以测试索引是否有所作为。您可以仅将部分行复制到新表中,并比较新表上带有和不带有索引的执行计划,例如

insert into testhistory
select *
from history
fetch first 100000 rows only
于 2014-06-22T12:34:36.937 回答
0

我假设您正在使用基于标签的 SQL Server。

是的,索引将使您的查询更快。

我建议只使用“created”列作为索引的键,并且不要包含 History 表中的任何其他列,因为它们将不会被使用并且只会导致比必要的更多的读取。

当然,当您在具有大量 INSERT、UPDATE、DELETE 活动的表上创建索引时要注意,因为您的新索引会使这些操作在对表执行时更加昂贵。

于 2014-06-20T21:03:20.417 回答
0

如前所述,在您的情况下,不会使用索引,因为索引是在“created”列上创建的,并且您正在查询“year(created)”。

您可以做的是在您的表中添加两个生成的列 year_gen = year(create) 和 month_gen = MONTHNAME(created) 并索引这两个列。DB2 查询优化器将自动使用这两个生成的列,它还将使用在这些列上创建的索引。

代码应该类似于(但不是 100% 确定,因为我没有要测试的 DB2)

SET INTEGRITY FOR HISTORY OFF CASCADE DEFERRED @
ALTER TABLE HISTORY ADD COLUMN YEAR_GEN SMALLINT GENERATED ALWAYS AS (YEAR(CREATE)), 
ADD COLUMN MONTH_GEN VARCHAR(20) GENERATED ALWAYS AS (YEAR(CREATE)) @
SET INTEGRITY FOR HISTORY IMMEDIATE CHECKED FORCE GENERATED @
CREATE INDEX HISTORY_YEAR_IDX ON HISTORY YEAR_GEN ASC CLUSTER @
CREATE INDEX HISTORY_MONTH_IDX ON HISTORY YEAR_GEN ASC @

只是一个旁注:set integrity off添加生成的列是强制性的。在您将完整性重置为checked并强制重新计算生成的列之前,您的表是不可访问的(在您的情况下这可能需要一段时间)。将完整性设置为关闭cascade deferred也会将具有 HISTORY 表的外键的每个表设置为 OFF。您还必须手动重置这些表的完整性。如果我没记错的话,cascade deferred与传入的外键结合使用可能会导致 DB2 将表的完整性设置为“由用户检查”。

于 2014-06-24T13:18:18.650 回答