2009-08-20 204 views
7

我想知道是否有一个性能良好的查询从SQL Server中的日期时间字段的表中选择不同日期(忽略时间)。如何从日期/时间字段快速选择DISTINCT日期,SQL Server

我的问题是没有得到服务器来实际做到这一点(我已经见过this question了,而且我们已经有类似的东西使用DISTINCT)。问题在于是否有任何技巧可以让它更快完成。使用我们正在使用的数据,我们当前的查询返回大约80个不同日期,其中有约40,000行数据(在另一个索引列上过滤之后),日期列上有一个索引,并且查询始终设法执行5+秒。这太慢了。

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

回答

6

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

你真正需要的是有一个能够在瞬间产生响应的索引。您可以拥有一个持久计算列,其中包含索引(需要表格结构更改)或索引视图(requires Enterprise Edition for QO to consider the index开箱即用)。

持久化计算柱:

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将使用此索引视图。技术上仍然是一次扫描,但是在已经是“独特”的索引上,因此只扫描所需的记录。我认为它是一种黑客,我不会推荐它用于现场制作代码。

AFAIK SQL Server不具备通过跳过重复扫描真正索引的功能,即,寻求最高,然后寻求超越最高,然后连续追求超过最后发现。

+0

有没有办法使用'跳过'SQL Server' SCAN'?我只是想你的解决方案在'2M'表,并在'DATETIME'场了'用'哈希匹配Aggregate' 850个ms'得到更糟糕('DISTINCT CAST(...)','DISTINCT date'了带有“流聚合”的1800毫秒)。 'Oracle'和'MySQL'只会跳过索引中的不同字段,'SQL Server'不会这样做。 – Quassnoi 2009-08-20 17:25:13

+0

您需要在创建索引后选择不同的date_only。 – 2009-08-20 17:59:34

+0

'@ Remus':我确实创建了一个索引,并且优化器确实使用了它。 – Quassnoi 2009-08-20 18:09:36

9

我使用以下:

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

这通过将其转换为float和截断断“时间”的部分,这是float的十进制将删除日期的时间。

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

3

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

2

更新:下面

解测试效率上的2M表,并采取但40 ms

普通DISTINCT在索引计算列上花了9 seconds

业绩详情,请参见我的博客此项:


不幸的是,SQL Server的优化可以做既不Oracle的SKIP SCAN也不MySQLINDEX 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

+0

建立一个日期表,然后半接合到原来是很棒的解决恕我直言,带索引或索引视图的持久列的额外开销只有在必须非常频繁地执行此操作时才有意义(任意猜测:如每天几百次)。我总是希望首先尝试提出一个更好的查询,而不是将更多的复杂性/开销添加到数据库结构中。 – 2013-03-08 16:02:34

0

更有效重新格式化日期 - 这可能是延迟的主要原因(通过强制执行全表扫描) - 除此之外,您别无选择,只能将日期仅存储在日期时间的一部分,不幸的是需要更改数据库结构。

如果您使用SQL Server 2005或更高版本,然后一个持久化计算领域是去

 
Unless otherwise specified, computed columns are virtual columns that are 
not physically stored in the table. Their values are recalculated every 
time they are referenced in a query. The Database Engine uses the PERSISTED 
keyword in the CREATE TABLE and ALTER TABLE statements to physically store 
computed columns in the table. Their values are updated when any columns 
that are part of their calculation change. By marking a computed column as 
PERSISTED, you can create an index on a computed column that is deterministic 
but not precise. 
+1

延迟的主要原因是扫描和排序以产生截然不同的结果。除非在标量操作中出现* extreamly * complex,否则数据库中的延迟总是与数据访问有关,而不是与标量操作有关。 – 2009-08-20 19:58:16

+0

这是延迟的主要原因,因为它迫使全表扫描 - 对不起,应该已经说的很清楚 – Cruachan 2009-08-23 12:18:21

0

你对其他过滤柱谓词的方式吗?您是否尝试过是否从其他过滤列上的索引获得改进,然后是日期时间字段?

我猜主要是在这里,但5秒钟过滤一组也许是10万行到40000,然后做一个排序(这大概是发生的事情)似乎并不像一个不合理的时间给我。你为什么说它太慢?因为它不符合预期?

3

我不确定为什么你现有的查询将花费超过5秒40,000行。

我刚刚对一个有100,000行的表格尝试了下面的查询,并且返回的时间少于0.1s。

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

(请注意,此查询可能不会能够采取日期列上的任何索引的优势,但它应该是相当快,假设你不执行它几十次每秒。)

+0

简单,清洗容易,这应该是正确的答案 – 2017-09-30 18:22:43

0

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

1

我用这个

SELECT 
DISTINCT DATE_FORMAT(your_date_column,'%Y-%m-%d') AS date 
FROM ... 
+0

没有把握效率,但这绝对是最好的方式。 – ylnor 2016-11-26 17:20:14

5

这个工作对我来说:

SELECT distinct(CONVERT(varchar(10), {your date column}, 111)) 
FROM {your table name} 
相关问题