5

我继承了一个新系统,我正在尝试对数据进行一些改进。我正在尝试改进这张表,但似乎无法理解我的发现。

我有以下表结构:

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 条记录。我需要返回日期大于特定日期的所有记录。在这种情况下,2009 年 12 月 1 日。此查询将返回大约 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'
4

5 回答 5

4

SELECT *通常会表现不佳。

要么索引将被忽略,要么您最终会通过键/书签查找聚集索引。没关系:两者都可能运行不佳。

例如,如果您有此查询,并且 TicketRecvdDateTime 上的索引包含CallStatus,那么它很可能会按预期运行。这将覆盖

SELECT CallStatus
FROM _FirstSlaLevel_Tickets_New
WHERE TicketRecvdDateTime >= '12/01/2009'

这是 Randy Minder 回答的补充:键/书签查找对于少数几行可能足够便宜,但对于大部分表数据来说却不是。

于 2010-11-29T19:14:53.950 回答
4

您可能会遇到所谓的 SQL Server 中的临界点。即使您在列上有适当的索引,如果返回的预期行数超过某个阈值(“临界点”),SQL Server 仍可能决定执行表扫描。

在您的示例中,这似乎很可能,因为您正在转动数据库中行数的 1/4。以下是一篇很好的文章,解释了这一点:http://www.sqlskills.com/BLOGS/KIMBERLY/category/The-Tipping-Point.aspx

于 2010-11-29T19:06:39.797 回答
3

您的查询在没有索引的情况下更快(或者更准确地说,在没有索引的情况下速度相同),因为RecvdDate在像. 这是一个不支持 SARG 的表达式,因为它需要通过函数转换列。为了考虑这个索引事件,您必须准确地在被索引的列上表达您的过滤条件,而不是基于它的表达式。这将是第一步。CAST(RecvdDate AS DATE) >= '12/01/2009'

还有更多步骤:

  • 去掉日期的 VARCHAR(10) 列并将其替换为适当的 DATE 或 DATETIME 列。将日期和/或时间存储为字符串充满了问题。不仅为了索引,而且为了正确性。
  • 经常在基于列的范围内扫描的表(大多数此类调用日志表都是)应按该列聚集。
  • 您不太可能真的需要yrandmnth列。如果您确实需要它们,那么您可能需要它们作为计算列。

.

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';

当然,表和索引的正确结构应该是仔细分析的结果,考虑到所有涉及的因素,包括更新性能、其他查询等。我建议您从设计索引中包含的所有主题开始。

于 2010-11-29T19:47:01.120 回答
0

你能改变你的查询吗?如果需要很少的列,您可以更改 SELECT 子句以返回更少的列。然后,您可以创建一个包含所有引用列的覆盖索引,包括TicketRecvdDateTime.

您可能会在 上创建索引TicketRecvdDateTime,但您可能无法避免@Randy Minder 讨论的临界点。但是,对较小索引(小于表扫描)的扫描将返回更少的页面。

于 2010-11-29T19:18:28.320 回答
0

假设 RecvdDate 是您正在谈论的 TicketRecvdDateTime :

如果字段类型为 DATE,SQL Server 仅比较单引号中的日期。您的查询可能会将它们作为 VARCHAR 进行比较。尝试使用“99/99/0001”添加一行,看看它是否显示在底部。

如果是这样,您的查询结果不正确。将类型更改为 DATE。

请注意, VARCHAR 不能很好地索引, DATETIME 可以。

检查查询计划以查看其是否使用索引。如果数据库与可用 RAM 相比较小,它可能会简单地进行表扫描并将所有内容保存在内存中。

编辑:看到您的 CAST/DATETIME 编辑后,让我指出从 VARCHAR 解析日期是一项非常昂贵的操作。你这样做了 220k 次。这会扼杀性能。

此外,您不再检查索引字段。与涉及索引字段的表达式进行比较不使用索引。

于 2010-11-29T19:18:46.693 回答