12

我想知道是否有一个性能良好的查询可以从 SQL Server 中具有日期时间字段的表中选择不同的日期(忽略时间)。

我的问题是没有让服务器真正做到这一点(我已经看到了这个问题,并且我们已经使用 DISTINCT 进行了类似的操作)。问题是是否有任何技巧可以更快地完成它。使用我们正在使用的数据,我们当前的查询返回约 80 个不同的日期,其中有约 40,000 行数据(在另一个索引列上过滤后),日期列上有一个索引,并且查询总是设法采取5 秒以上。这太慢了。

更改数据库结构可能是一种选择,但不太理想。

4

10 回答 10

11

我使用了以下内容:

CAST(FLOOR(CAST(@date as FLOAT)) as DateTime);

这通过将日期转换为 afloat并截断“时间”部分(即float.

看起来有点笨拙,但在我整天重复使用的大型数据集(约 100,000 行)上效果很好。

于 2009-08-20T16:34:40.877 回答
8

这对我有用:

SELECT distinct(CONVERT(varchar(10), {your date column}, 111)) 
FROM {your table name}
于 2011-10-10T12:21:30.913 回答
7

在日期时间字段上涉及 CAST 或 TRUNCATE 或 DATEPART 操作的每个选项都有相同的问题:查询必须扫描整个结果集(40k)才能找到不同的日期。各种实现之间的性能可能略有不同。

您真正需要的是有一个可以在眨眼间产生响应的索引。您可以拥有一个带有索引的持久计算列(需要更改表结构)或一个索引视图(需要企业版 QO 以考虑开箱即用的索引)。

持久计算列:

alter table foo add date_only as convert(char(8), [datetimecolumn], 112) persisted;
create index idx_foo_date_only on foo(date_only);

索引视图:

create view v_foo_with_date_only
with schemabinding as 
select id
    , convert(char(8), [datetimecolumn], 112) as date_only
from dbo.foo;   
create unique clustered index idx_v_foo on v_foo_with_date_only(date_only, id);

更新

要完全消除扫描,可以使用 GROUP BY 欺骗索引视图,如下所示:

create view v_foo_with_date_only
with schemabinding as 
select
    convert(char(8), [d], 112) as date_only
    , count_big(*) as [dummy]
from dbo.foo
group by convert(char(8), [d], 112)

create unique clustered index idx_v_foo on v_foo_with_date_only(date_only)

查询select distinct date_only from foo将改为使用此索引视图。在技​​术上仍然是扫描,但在已经“不同”的索引上,因此只扫描所需的记录。我认为它是一种 hack,我不建议将它用于实时生产代码。

AFAIK SQL Server 不具备通过跳过重复扫描真实索引的能力,即。寻找顶部,然后寻找大于顶部,然后连续寻找大于最后发现的东西。

于 2009-08-20T17:03:16.700 回答
3

最简单的方法是只为日期部分添加一个计算列,然后选择它。如果您不想更改表格,可以在视图中执行此操作。

于 2009-08-20T16:35:06.710 回答
3

我不确定为什么您现有的查询会占用 40,000 行超过 5 秒的时间。

我刚刚对一个有 100,000 行的表尝试了以下查询,它在不到 0.1 秒内返回。

SELECT DISTINCT DATEADD(day, 0, DATEDIFF(day, 0, your_date_column))
FROM your_table

(请注意,此查询可能无法利用日期列上的任何索引,但它应该相当快,假设您没有每秒执行数十次。)

于 2009-08-20T17:15:26.613 回答
2

更新:

下面的解决方案在桌子上测试效率2M并采用 but 40 ms

普通DISTINCT的索引计算列采用9 seconds.

有关性能详细信息,请参阅我的博客中的此条目:


不幸的是,SQL Server's 优化器既不能做 Oracle 的SKIP SCAN,也不能做MySQL's 的INDEX FOR GROUP-BY

总是Stream Aggregate需要很长时间。

您可以使用递归构建可能日期的列表CTE并将其与您的表连接:

WITH    rows AS (
        SELECT  CAST(CAST(CAST(MIN(date) AS FLOAT) AS INTEGER) AS DATETIME) AS mindate, MAX(date) AS maxdate
        FROM    mytable
        UNION ALL
        SELECT  mindate + 1, maxdate
        FROM    rows
        WHERE   mindate < maxdate
        )
SELECT  mindate
FROM    rows
WHERE   EXISTS
        (
        SELECT  NULL
        FROM    mytable
        WHERE   date >= mindate
                AND date < mindate + 1
        )
OPTION  (MAXRECURSION 0)

这将比Stream Aggregate

于 2009-08-20T16:42:33.810 回答
1

我用这个

SELECT
DISTINCT DATE_FORMAT(your_date_column,'%Y-%m-%d') AS date
FROM ...
于 2011-02-23T18:10:58.030 回答
0

如果您想避免步骤提取或重新格式化日期 - 这可能是延迟的主要原因(通过强制全表扫描) - 您别无选择,只能存储日期时间的一部分,不幸的是,这将需要更改数据库结构。

如果您使用的是 SQL Server 2005 或更高版本,那么持久计算字段是您的最佳选择

除非另有说明,否则计算列是虚拟列
没有物理存储在表中。他们的值每重新计算一次
在查询中引用它们的时间。数据库引擎使用 PERSISTED
CREATE TABLE 和 ALTER TABLE 语句中的关键字以物理存储
表中的计算列。当任何列时更新它们的值
这是他们计算变化的一部分。通过将计算列标记为
PERSISTED,您可以在确定性的计算列上创建索引
但不精确。
于 2009-08-20T16:44:19.267 回答
0

您对其他过滤列的谓词是什么?您是否尝试过是否从其他过滤列上的索引中获得改进,然后是 datetime 字段?

我在这里很大程度上是在猜测,但是 5 秒将一组可能 100000 行过滤到 40000 行,然后进行排序(这可能是正在发生的事情)对我来说似乎不是一个不合理的时间。为什么说它太慢?因为不符合预期?

于 2009-08-20T16:46:11.167 回答
0

只需转换日期:dateadd(dd,0, datediff(dd,0,[Some_Column]))

于 2009-08-20T17:20:24.340 回答