2013-01-20 42 views
0

我正在尋找我認爲是關於SQL Server Management Studio的非常有用的信息。如何將varchar轉換爲只包含有效日期的日期?

我有一個類型爲varchar的列的表,它存儲日期,數字和字符串。

這些日期存儲有以下格式:

dd/mm/aaaa 

我有searchs形式匹配的行和一個要求是,用戶必須能夠日期(時間週期)之間seacrh查詢。

沒有謎好像我剛剛日期,我可以用一個查詢:

where convert(datetime,a.valor,103) between '01/01/2013' and '03/01/2013' 

的問題是,這個查詢時達到行的值不是一個日期失敗。

什麼是執行該查詢的有效方式,因爲可能有數千行要搜索?

+0

需要首先執行不包括日期的查詢,然後對日期執行第二個查詢,最後將這兩個數據集綁定在一起? –

回答

3

典型的回答是添加一個WHERE子句:

WHERE ISDATE(a.valor) = 1 

然而,這是一對夫婦的原因,您的情況有問題:

  1. ISDATE()不一定會符合你的方式希望取決於服務器的區域設置,用戶的語言或日期格式選項等。例如:

    SET DATEFORMAT dmy; 
    SELECT ISDATE('13/01/2012'); -- 1 
    
    SET DATEFORMAT mdy; 
    SELECT ISDATE('13/01/2012'); -- 0 
    
  2. 您無法真正控制該SQL Server將嘗試並在篩選器後執行CONVERT

你甚至不能使用子查詢或CTE的嘗試和過濾器從CONVERT分開,因爲SQL Server 可以在任何順序它認爲更有效的查詢優化操作。

例如,用有限的樣本,你可能會發現,這個工作好:

SET DATEFORMAT dmy; 

SELECT valor, valor_date FROM (
    SELECT valor, valor_date = CONVERT(DATE, 
    CASE WHEN ISDATE(valor) = 1 THEN valor ELSE NULL END, 103) 
    FROM dbo.mytable 
    WHERE ISDATE(valor) = 1 
) AS sub WHERE valor_date BETWEEN '01/01/2012' AND '01/03/2012'; 

但我已經看到了其中的SQL Server試圖先評估過濾器,即使這個結構的情況下,導致你目前得到的錯誤。


幾個更安全的解決方法:


添加計算列,例如

ALTER TABLE dbo.mytable ADD valor_date 
    AS CONVERT(DATE, CASE WHEN ISDATE(valor) = 1 THEN valor 
    ELSE NULL END, 103); 

爲了防止您在運行時出現可能的錯誤解釋,您應該在發出引用計算列的查詢之前指定dateformat,例如,

SET DATEFORMAT dmy; 
SELECT valor, valor_date FROM dbo.mytable WHERE ...; 

創建一個視圖:

CREATE VIEW dbo.myview 
AS 
    SELECT valor, valor_date = CONVERT(DATE, 
    CASE WHEN ISDATE(valor) = 1 THEN valor ELSE NULL END, 103) 
    FROM dbo.mytable 
    WHERE ISDATE(valor) = 1; 

同樣,你想查詢視圖時發出SET DATEFORMAT


使用臨時表:

SELECT <cols> 
INTO #foo 
FROM dbo.mytable 
WHERE ISDATE(valor) = 1; 

SELECT <cols>, CONVERT(DATE, valor) FROM #foo WHERE ...; 

您可能仍然希望使用DATEFORMAT保護自己免受ISDATE和用戶設置之間的衝突。


不,你應該嘗試驗證自己的字符串作爲使用字符串模式匹配日期爲另一個(現已刪除)回答與會者提出:

like '%__/%' or like '%/%' 

你必須有一些非常複雜和嚴格的驗證來處理所有有效的日期,包括閏年。

0

您可以將表格與只包含日期的表格進行比較。這可能是值得創建每一天永久表,但你可以用一個CTE(高達32767個遞歸哪個讓你年至1923年):

create table tmpT (val nvarchar(255)) 
go 

insert into tmpT values ('01/01/2012') 
insert into tmpT values ('jellybeans') 
insert into tmpT values ('21/11/2002') 
insert into tmpT values ('ice cream') 
insert into tmpT values ('30/08/2012') 
go 
; 

with dates (d) as (
    select d = cast('1/19/2013' as datetime) -- quick way to drop hh:mm:ss 
    union all 
    select dateadd(dd, -1, d) 
    from dates where d > '01/01/1990' 
) 
select * 
From tmpt 
join dates 
    on convert(varchar, dates.d, 103) = tmpt.val 
where d between '01/01/2013' and '01/03/2013' 
option (maxrecursion 32767) -- max value: select datediff(dd, -32767, getdate()) = 1923 

ETA:是的,我坐在一個SQL 2005這樣的前沒有date數據類型對我來說,但概念保持不變。

+0

爲什麼不在這裏使用日期表? – dezso

+0

是的,我希望在遞歸CTE上使用固定表。查看最近的這個博客系列:http://www.sqlperformance.com/2013/01/t-sql-queries/generate-a-set-3 –

+0

同意。日期/詮釋表有很多用途。我一直都在使用它們。我在這裏介紹的這個解決方案不需要創建視圖,表格或列。 – Malk

相關問題