1.關(guān)于字符型和DATETIME類(lèi)型的轉化
由于時(shí)間類(lèi)型的優(yōu)先級高于字符型,所以當比較一個(gè)DATETIME類(lèi)型的數據和一個(gè)字符串的時(shí)候,字符串首先會(huì )轉化成DATETIME類(lèi)型。
A '20060102' '090807' '20090203 12:11:11.111'轉化成時(shí)間類(lèi)型的時(shí)候
B '2009-02-03 12:11:11.111'轉化成時(shí)間類(lèi)型的時(shí)候
不受會(huì )話(huà)語(yǔ)言設置影響(SET LANGUAGE) 注意B類(lèi)型轉換的時(shí)候不訥訥個(gè)省略它的時(shí)間部分。
2.字符型和DATETIME類(lèi)型轉換精度問(wèn)題
--字符串轉化成datetime ,轉化后的時(shí)間比字符串慢了1毫秒,這是因為自動(dòng)舍入到最近的百分之三秒
declare @s varchar(100)
set @s='2005-09-23 03:23:43:001'
SELECT CAST(@s AS datetime)
/*
-----------------------
2005-09-23 03:23:43.000
*/
-------------------------
--字符串轉化成smalldatetime ,轉化后的時(shí)間比字符串快了大約秒,這是因為自動(dòng)舍入到最近的分鐘數
declare @s varchar(100)
set @s='2005-09-23 03:23:43:001'
SELECT CAST(@s AS Smalldatetime)
/*
-----------------------
2005-09-23 03:24:00
*/
------------------
還要注意 它們之間的轉化并不是什么簡(jiǎn)單的截斷數據 有精確到毫秒時(shí)候 毫秒部分匹配【0-9】【0-9】【037】
SQL SERVER 有時(shí)候無(wú)法精確表示某個(gè)時(shí)間的瞬間,比如‘20090202 23:59:59.999’,自動(dòng)舍入為
‘20090203 00:00:00.000’
declare @s table (dates datetime)
insert @s
select '20090211 00:00:00.000' union all
select '20090212 00:00:00.000'
select * from @s
where dates between '20090211 00:00:00:000' and '20090211 23:59:59.999' --轉化成-02-12 00:00:00.000
/*
dates
-----------------------
2009-02-11 00:00:00.000
2009-02-12 00:00:00.000
*/
select * from @s
where dates >='20090211' and dates< '20090212' --SARG,優(yōu)化器考慮使用Index Seek
---------------
2009-02-11 00:00:00.000
*/
select * from @s
where convert(varchar(8),dates,112)='20090211'
/*
Dates
-----------------------
2009-02-11 00:00:00.000
*/
3.Datetime和SmallDatetime類(lèi)型的內部存儲
時(shí)間類(lèi)型在存儲時(shí)候被分成2日期和時(shí)間2獨立部分.
日期部分存儲的是和1900-01-01的天數差,時(shí)間部分存儲,如果是Datetime,午夜0點(diǎn)后時(shí)鐘周期數,一個(gè)時(shí)鐘周期數為1/300秒,即3.33毫秒.如果是smalldatetime,時(shí)間部分存儲的是午夜0點(diǎn)以后的分鐘數.
我們只要將時(shí)間轉換成16進(jìn)制就可以很清楚的看到了。接下來(lái)演示一下:
declare @day1 datetime
set @day1=current_timestamp
--datetime
select @day1,--2010-03-07 17:29:49.637
CONVERT(varbinary(8),@day1),-- 0x00009D32012057FB
CONVERT(int,SUBSTRING(CONVERT(varbinary(8),@day1),1,4)),--40242
CONVERT(int,SUBSTRING(CONVERT(varbinary(8),@day1),5,4))--18876708*3.33/1000/60/60 =17.46
4.Datetime 相關(guān)查詢(xún)問(wèn)題
a.生日問(wèn)題
這里只考慮特殊問(wèn)題
使用DATEADD函數 為某年的2月29日加1年,下年由于不是閏年,所以系統默認是XXXX年2月28日。
很多人在非閏年慶祝2月29生日 都放在3月1日(書(shū)上所說(shuō),HOHO)
例子:我們來(lái)求每個(gè)員工離今天最近的生日。如果過(guò)了生日今天 那么返回明年的生日 否則 返回今年(環(huán)境2005)
SET NOCOUNT ON;
WITH Args1 AS
(
SELECT Name, BirthDate,
DATEDIFF(year, BirthDate, GETDATE()) AS Diff,--出生日期和今天的相差年份
CAST(CONVERT(CHAR(8), GETDATE(), 112) AS DATETIME) AS Today --今天的午夜時(shí)間
FROM dbo.Employees
),
Args2 AS
(
SELECT Name, BirthDate, Today,
DATEADD(year, Diff, BirthDate) AS BDCur,--這個(gè)是今年的生日
DATEADD(year, Diff + 1, BirthDate) AS BDNxt--這個(gè)是明年的生日
FROM Args1
),
Args3 AS
(
SELECT LastName, FirstName, BirthDate, Today,
BDCur + CASE WHEN DAY(BirthDate) = 29 AND DAY(BDCur) = 28
THEN 1 ELSE 0 END AS BDCur,--這里需要判斷如果出生的那天是閏年的月日,而今年的生日是日,那么+1天(號過(guò)生日)
BDNxt + CASE WHEN DAY(BirthDate) = 29 AND DAY(BDNxt) = 28
THEN 1 ELSE 0 END AS BDNxt ,--這里需要判斷如果出生的那天是閏年的月日,而明年的生日是日,那么+1天(號過(guò)生日)
FROM Args2
)
SELECT Name, BirthDate,
CASE WHEN BDCur >= Today THEN BDCur ELSE BDNxt END AS BirthDay--如果今年的生日還沒(méi)過(guò)(比今天晚),那么就取今年的反之取下一年
FROM Args3;
b.標識星期數
大家都知道SQL默認的每個(gè)星期第一天是星期日 這樣和我們的習慣相違背 這個(gè)時(shí)候會(huì )在做查詢(xún)的時(shí)候非常不方便
大家可以通過(guò)會(huì )話(huà)的選項 DATEFIRST設置星期的第一天
我要說(shuō)的是不依賴(lài)這個(gè)設置
有條經(jīng)典的話(huà):DATEFIRST的設置值和DATEPART函數返回的值存在反比關(guān)系。
什么意思呢?假設DATEFIRST的設置值 為F DATEPART函數返回的星期數 D 如果F增加N 那么D減小N
如果DATEFIRST 設置為1 今天是星期2 那么 DATEPART 返回2
如果DATEFIRST 設置為2 (1+1) 今天是星期2 那么 DATEPART 返回1 (2-1)
給出經(jīng)典等式:
DATEPART(WEEKDAY,DT+@@DATEFIRST-N)--這里的N為你想星期幾為第一條就設置幾
DATEPART(WEEKDAY,DT+@@DATEFIRST-1)--大家平時(shí)的生活習慣
問(wèn)題:如果要查某個(gè)表里所有星期2的生產(chǎn)數量
上面的思路做就是:
select * from tn where DATEPART(WEEKDAY,DT+@@DATEFIRST-1)=2
下面我說(shuō)一個(gè)精巧的思路:
如果你知道某個(gè)具體日期的是星期 比如‘19000102’ (這里寫(xiě)這個(gè)原因我想是因為日期夠小 方便下面減出來(lái)一定是正數)
select * from tn where datediff(WEEKDAY,'19000102’,DT)%7=0
這里的'19000102’起到一個(gè)參照作用.
c.按周分組
問(wèn)題描述:如果讓你求每周的訂單數量 有人會(huì )想到用DATEPART(week,DT)來(lái)分組
這樣有一個(gè)問(wèn)題:對于一周跨年的日期,你會(huì )得到2個(gè)不同的周數
解決方法:
由于每周的分組因子是一樣的(這里的分組因子我理解是你在一周內用DATEPART(WEEKDAY,DT)發(fā)生星期數是唯一的在這周內)
書(shū)上的意思就是 任意一條記錄 減去日期的星期數+1 一定是可以返回本周的第一天日期 加上7天-日期的星期數 就是這周的最后一天了
這樣就可以根據一周的第一天和最后一天分組了(相當巧妙我覺(jué)得)
SELECT od - wd + 1 AS week_start, od + 7 - wd AS week_end,
COUNT(*) AS numorders
FROM (SELECT OrderID AS oid, OrderDate AS od,
DATEPART(weekday, OrderDate + @@DATEFIRST - 1) AS wd
FROM dbo.Orders) AS D
GROUP BY od - wd + 1, od + 7 - wd;
例子可以很好說(shuō)明問(wèn)題,不解釋了
d.計算工作日
--計算個(gè)日期之間的工作日(周末雙休,不考慮特殊節日)
create function workday
(
@beg_time datetime,
@end_time datetime
)
returns int
as
begin
declare @n int
set @n=(
select 工作日=days/7*5+days%7
-case when 6 between wd and wd+days%7-1 then 1 end
-case when 7 between wd and wd+days%7-1 then 1 end
from(select datediff(day,@beg_time,@end_time)+1 as days,
datepart(weekday,@beg_time+@@datefirst-1) as wd
) k
)
return @N
end
go
declare @beg_time datetime,@end_time datetime
set @beg_time='2010-01-01'
set @end_time='2010-06-28'
--執行函數
select 工作日=dbo.workday(@beg_time,@end_time)
/*
工作日
-----------
127
*/
e.生產(chǎn)日期的方法
這里首先要提到2個(gè)參數臨時(shí)表的方法
/*
生產(chǎn)一百萬(wàn)連續數據之臨時(shí)表
*/
SET NOCOUNT ON
CREATE TABLE NUM(N INT PRIMARY KEY)
INSERT NUM VALUES(1);
DECLARE @MAX INT,@RC INT
SET @MAX=1000000
SET @RC=1
WHILE @RC*2<=@MAX
BEGIN
INSERT NUM
SELECT N+@RC FROM NUM
SET @RC=@RC*2
END
INSERT NUM
SELECT N+@RC FROM NUM WHERE N+@RC<=@MAX
SELECT * FROM NUM
go
-----------------
/*
函數生產(chǎn)W連續數據
*/
create function dbo.fn_nums(@n as bigint )
returns table
as
return
with
L0 as (select 1 as c union all select 1),
L1 as (select 1 as c from L0 AS A,L0 AS B),
L2 AS (SELECT 1 AS C FROM L1 AS A,L1 AS B),
L3 AS (SELECT 1 AS C FROM L2 AS A,L2 AS B),
L4 AS (SELECT 1 AS C FROM L3 AS A,L3 AS B),
L5 AS (SELECT 1 AS C FROM L4 AS A,L4 AS B),
NUMS AS (SELECT ROw_number() over (order by c) as n from L5)
select n from nums where n<=@n
select * from dbo.fn_nums(1000000)
然后我們再來(lái)寫(xiě)返回個(gè)日期間的日期列表
DECLARE @s AS DATETIME, @e AS DATETIME;
SET @s = '20060101';
SET @e = '20061231';
SELECT @s + n - 1 AS dt
FROM dbo.Nums -這里的表就是上面的臨時(shí)表 你也可以用函數來(lái)替代
WHERE n <= DATEDIFF(day, @s, @e) + 1;
我今天就看了這么多,時(shí)間不夠
PS:此文章是我看書(shū)總結而來(lái) 拿出來(lái)的目的是大家學(xué)習交流用 (部分代碼是書(shū)上代碼)不對的地方大家可以拍磚~
聯(lián)系客服