我需要按年/月分组数据。数据存储在数据类型为year
的列中。有几种方法可以切断当天的信息并留下年月。
我觉得使用内置month
和YYYYMM
函数应该比字符串操作更好。我玩了一下,想知道这是否属实:在性能方面,我发现使用year
和month
函数计算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,相同的......
这可能是真的吗?
或者简单地说:最好的方法是什么?
答案 0 :(得分:2)
您遗漏了关于SQL的一个非常重要的观点:数据库旨在管理和处理数据。也许是因为数据的阅读,编写和缓存隐藏在select
和from
之后,该语言的用户不一定会对这些操作有太多的考虑。
然而。将数据从磁盘移动到内存主导着大多数查询。
您正在比较单个字段上的非常小的操作。与读取和写入数据以将数据存储在内存中的开销相比,这些操作可以忽略不计。
道德是专注于使代码可读和可维护,而不是微观优化。
注意:某些功能可能会有很高的开销,即使相对于数据的移动也是如此。对于大型字符串,对象,XML等的操作尤其如此。
答案 1 :(得分:1)
根据您的结果需要,方法很少:
EOMONTH(your_date_column);
分组。 https://docs.microsoft.com/en-us/sql/t-sql/functions/eomonth-transact-sql DATEPART(YEAR, your_date_column), DATEPART(MONTH, your_date_column);
此外 - 如果经常这样做,也许您可以单独存储年份和月份列并按其分组。
答案 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
以下是建议的查询和结果计划:
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
select count(*), left(mydate, 7)
from xxx.dbo.test
group by left(mydate, 7)
https://www.brentozar.com/pastetheplan/?id=BJZ67uPT-
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
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语句。