我继承了一个新系统,我正在尝试对数据进行一些改进。我正在努力改进这个表,似乎无法理解我的发现。
我有以下表结构:
CREATE TABLE [dbo].[Calls](
[CallID] [varchar](8) NOT NULL PRIMARY KEY,
[RecvdDate] [varchar](10) NOT NULL,
[yr] [int] NOT NULL,
[Mnth] [int] NOT NULL,
[CallStatus] [varchar](50) NOT NULL,
[Category] [varchar](100) NOT NULL,
[QCall] [varchar](15) NOT NULL,
[KOUNT] [int] NOT NULL)
此表中包含约220k条记录。我需要返回日期大于特定日期的所有记录。在这种情况下12/1/2009。此查询将返回大约66k记录,运行大约需要4秒。从我过去的系统来看,这似乎很高。特别是考虑到表中的记录很少。所以我想把时间缩短。
所以我想知道将这种方法降低的一些好方法是什么?我尝试在表中添加日期列并将字符串日期转换为实际日期列。然后我在该日期列添加了索引,但时间保持不变。鉴于没有那么多记录,我可以看到表扫描如何快速但我认为索引可能会缩短时间。
我还考虑过查询月份和年份列。但我还没有尝试过。如果可能的话,我希望将其保留在日期栏之外。但如果不是,我可以改变它。
感谢任何帮助。
编辑:这是我试图运行的查询并测试表的速度。我通常把列放出来,但为了简单起见,我使用了*:
SELECT *
FROM _FirstSlaLevel_Tickets_New
WHERE TicketRecvdDateTime >= '12/01/2009'
编辑2:所以我提到我曾尝试使用包含recvddate数据的日期列创建一个表,但是作为日期而不是varchar。这就是TicketRecvdDateTime列在上面的查询中。我对这个表运行的原始查询是:
SELECT *
FROM Calls
WHERE CAST(RecvdDate AS DATE) >= '12/01/2009'
答案 0 :(得分:4)
您可能会遇到SQL Server中称为“临界点”的内容。即使列上有适当的索引,如果返回的预期行数超过某个阈值(“临界点”),SQL Server也可能决定进行表扫描。
在您的示例中,这可能是因为您正在转换数据库中行数的1/4。以下是一篇很好的文章,解释了这一点:http://www.sqlskills.com/BLOGS/KIMBERLY/category/The-Tipping-Point.aspx
答案 1 :(得分:4)
SELECT *
通常表现不佳。
索引将被忽略,或者您最终会在聚簇索引中使用键/书签查找。无论如何:两者都可能运行得很糟糕。
例如,如果您有此查询以及TicketRecvdDateTime INCLUDEd CallStatus上的索引,那么它很可能会按预期运行。这将是covering
SELECT CallStatus
FROM _FirstSlaLevel_Tickets_New
WHERE TicketRecvdDateTime >= '12/01/2009'
这是Randy Minder的回答:对于少量行,键/书签查找可能足够便宜,但对于大量的表数据则不行。
答案 2 :(得分:3)
你的查询没有索引更快(或者更准确地说,与indeX相同的速度是相同的)因为RecvdDate
上的索引总是是在像CAST(RecvdDate AS DATE) >= '12/01/2009'
这样的表达式中被忽略。这是一个非SARG表达式,因为它需要通过函数转换列。为了将此事件视为,您必须在要编入索引的列上表达您的过滤条件完全,而不是基于它的表达式。这将是第一步。
还有更多步骤:
yr
和mnth
列。如果你确实需要它们,那么你可能需要它们作为计算列。
CREATE TABLE [dbo].[Calls](
[CallID] [varchar](8) NOT NULL,
[RecvdDate] [datetime](10) NOT NULL,
[CallStatus] [varchar](50) NOT NULL,
[Category] [varchar](100) NOT NULL,
[QCall] [varchar](15) NOT NULL,
[KOUNT] [int] NOT NULL,
CONSTRAINT [PK_Calls_CallId] PRIMARY KEY NONCLUSTERED ([CallID]));
CREATE CLUSTERED INDEX cdxCalls ON Calls(RecvDate);
SELECT *
FROM Calls
WHERE RecvDate >= '12/01/2009';
当然,表格和索引的正确结构应该是仔细分析的结果,考虑所有因素,包括更新性能,其他查询等。我建议您首先浏览所有Designing Indexes中包含的主题。
答案 3 :(得分:0)
你能改变你的疑问吗?如果需要很少的列,则可以更改SELECT子句以返回更少的列。然后,您可以创建包含所有引用列的覆盖索引,包括TicketRecvdDateTime
。
您可以在TicketRecvdDateTime
上创建索引,但您可能无法避免@Randy Minder讨论的引爆点。但是,对较小索引(小于表扫描)的扫描将返回较少的页面。
答案 4 :(得分:0)
假设RecvdDate是您正在谈论的TicketRecvdDateTime:
如果字段类型为DATE,则SQL Server仅比较单引号中的日期。您的查询可能将它们作为VARCHAR进行比较。尝试添加一行'99 / 99/0001'并查看它是否显示在底部。
如果是这样,您的查询结果不正确。将类型更改为DATE。
请注意,VARCHAR索引不好,DATETIME会这样做。
检查查询计划以查看其是否使用索引。如果DB与可用RAM相比较小,则可以简单地进行表扫描并将所有内容保存在内存中。
编辑:在看到您的CAST / DATETIME编辑时,让我指出从VARCHAR解析日期是一项非常昂贵的操作。你这样做了220k次。这会扼杀性能。
此外,您不再检查索引字段。与涉及索引字段的表达式进行比较时不使用索引。