我想知道是否有一个性能良好的查询来从SQL Server中带有日期时间字段的表中选择不同的日期(忽略时间)。
我的问题是没有让服务器实际执行此操作(我已经看过this question,并且我们已经使用DISTINCT实现了类似的功能)。问题在于是否有任何技巧可以更快地完成任务。使用我们正在使用的数据,我们当前的查询返回大约80个不同的日子,其中有大约40,000行数据(在另一个索引列上过滤后),日期列上有一个索引,查询总是设法采取5秒以上。这太慢了。
更改数据库结构可能是一种选择,但不太理想。
答案 0 :(得分:9)
我使用了以下内容:
CAST(FLOOR(CAST(@date as FLOAT)) as DateTime);
这会从日期中删除时间,将其转换为float
并截断“时间”部分,即float
的小数点。
看起来有点笨重,但在我全天重复使用的大型数据集(约100,000行)上效果很好。
答案 1 :(得分:5)
在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无法通过跳过重复来扫描真实索引,即。寻求顶部,然后寻求超过顶部,然后成功寻求比最后发现更大。
答案 2 :(得分:5)
这对我有用:
SELECT distinct(CONVERT(varchar(10), {your date column}, 111))
FROM {your table name}
答案 3 :(得分:3)
最简单的方法是仅为日期部分添加计算列,然后选择该日期部分。如果您不想更改表格,可以在视图中执行此操作。
答案 4 :(得分:3)
我不确定为什么现有的查询会占用40,000行的5s。
我刚尝试对具有100,000行的表进行以下查询,并且返回时间小于0.1秒。
SELECT DISTINCT DATEADD(day, 0, DATEDIFF(day, 0, your_date_column))
FROM your_table
(请注意,此查询可能无法利用日期列上的任何索引,但它应该相当快,假设您没有每秒执行数十次。)
答案 5 :(得分:2)
<强>更新强>
下面的解决方案在2M
表格上测试效率,但只需40 ms
。
索引计算列上的普通DISTINCT
占用了9 seconds
。
有关效果详情,请参阅我的博客中的此条目:
不幸的是,SQL Server
的优化程序既不能使用Oracle的SKIP SCAN
也不能MySQL
的{{1}}。
总是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)
答案 6 :(得分:1)
我用过这个
SELECT
DISTINCT DATE_FORMAT(your_date_column,'%Y-%m-%d') AS date
FROM ...
答案 7 :(得分: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.
答案 8 :(得分:0)
您对其他已过滤列的谓词是什么?您是否尝试过从其他已过滤列的索引获得改进,然后是datetime字段?
我在这里猜测,但是将5万秒过滤掉一组大约100000行到40000然后进行排序(这可能是继续发生的事情)对我来说似乎不是一个不合理的时间。为什么你说这太慢了?因为它与期望不符?
答案 9 :(得分:0)
只需转换日期:dateadd(dd,0, datediff(dd,0,[Some_Column]))