哪个是在SQL Server中计算年/月的最佳方法?

时间:2017-10-20 08:58:12

标签: sql sql-server tsql

我需要按年/月分组数据。数据存储在数据类型为year的列中。有几种方法可以切断当天的信息并留下年月。

我觉得使用内置monthYYYYMM函数应该比字符串操作更好。我玩了一下,想知道这是否属实:在性能方面,我发现使用yearmonth函数计算left(.., 7)和字符串之间没有任何区别操作YYYY-MM返回值declare @t1 table (mydate date) insert @t1 select '2017-01-30' select year(mydate) * 100 + month(mydate) from @t1 select left(mydate, 7) from @t1

我看不出执行计划的数字有什么不同,即使在比这里显示的更复杂的情况下也是如此。

void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *hpcd)

相同的计划,相同的I / O,相同的CPU,相同的......

这可能是真的吗?

或者简单地说:最好的方法是什么?

6 个答案:

答案 0 :(得分:2)

您遗漏了关于SQL的一个非常重要的观点:数据库旨在管理和处理数据。也许是因为数据的阅读,编写和缓存隐藏在selectfrom之后,该语言的用户不一定会对这些操作有太多的考虑。

然而。将数据从磁盘移动到内存主导着大多数查询。

您正在比较单个字段上的非常小的操作。与读取和写入数据以将数据存储在内存中的开销相比,这些操作可以忽略不计。

道德是专注于使代码可读和可维护,而不是微观优化。

注意:某些功能可能会有很高的开销,即使相对于数据的移动也是如此。对于大型字符串,对象,XML等的操作尤其如此。

答案 1 :(得分:1)

根据您的结果需要,方法很少:

此外 - 如果经常这样做,也许您可​​以单独存储年份和月份列并按其分组。

答案 2 :(得分:1)

如果我们想讨论性能,那么让我们来看看性能。

set statistics io on;
set statistics time on;

我的[datelist]测试表有大约9200万行。

使用您最新信息中的查询1-4,以下是IO&时间结果。

由于它是一个列表,因此IO是统一的。所有四个查询都返回了这个:

(208 rows affected)
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-
ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead 
reads 0.
Table 'Workfile'. Scan count 0, logical reads 0, physical reads 0, read-
ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead 
reads 0.
Table 'datelist'. Scan count 9, logical reads 136071, physical reads 0, 
read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-
ahead reads 0.

但是,您会看到,从时间角度来看,选项1和4是最高效的,使用本机日期操作,而选项3,使用日期和字符串函数的组合,是最远和最少的高效。

YEAR和MONTH功能带来了一天。

q1

SQL Server Execution Times:
CPU time = 61064 ms,  elapsed time = 7699 ms.

q2

SQL Server Execution Times:
CPU time = 89064 ms,  elapsed time = 11191 ms.

q3

SQL Server Execution Times:
CPU time = 103875 ms,  elapsed time = 13113 ms.

q4

SQL Server Execution Times:
CPU time = 68125 ms,  elapsed time = 8558 ms.

答案 3 :(得分:0)

获取yyyymm类型的值:

select cast(datepart(year,mydate) as nvarchar(4)) + right('0' + cast(datepart(month,mydate) as nvarchar(2)),2)

答案 4 :(得分:0)

这不是我自己的问题的答案,而是对此的更新。我检查了一张约40 Mio的桌子。行:

create table xxx.dbo.test (mydate date)

insert xxx.dbo.test select [some date field] from [some table]

- 39.861.927行

- 462,055 MB

以下是建议的查询和结果计划:

1

 select count(*), year(mydate) * 100 + month(mydate)
 from xxx.dbo.test
 group by year(mydate) * 100 + month(mydate)

https://www.brentozar.com/pastetheplan/?id=SJHPmuDTZ

2

select  count(*), left(mydate, 7)
from xxx.dbo.test
group by left(mydate, 7)

https://www.brentozar.com/pastetheplan/?id=BJZ67uPT-

3

select count(*), cast(datepart(year,mydate) as nvarchar(4)) + right('0' + cast(datepart(month,mydate) as nvarchar(2)),2)
from xxx.dbo.test
group by cast(datepart(year,mydate) as nvarchar(4)) + right('0' + cast(datepart(month,mydate) as nvarchar(2)),2)

https://www.brentozar.com/pastetheplan/?id=ry8bVdv6Z

4

select count(*), DATEPART(YEAR, mydate), DATEPART(MONTH, mydate) 
from xxx.dbo.test
group by DATEPART(YEAR, mydate), DATEPART(MONTH, mydate) 

https://www.brentozar.com/pastetheplan/?id=rkQSNuDTW

正如你所看到的,它们的表现大致相同(#1领先)。

所以不要担心?

答案 5 :(得分:0)

或者,如果此年/月部分是或将成为主要业务对象,则应考虑为年和月创建两个新计算的INT列的选项。这样你将来会获得很多东西,其中sql将在insert上计算* 1并在任何日期更新上计算* 1(如果有的话),而不是每次执行它时使用它所使用的每个不同的select语句。