0

SQL Server Management Studio に関して非常に役立つと思われる情報を探しています。

varchar日付、数値、および文字列を格納するタイプの列を持つテーブルがあります。

これらの日付は、次の形式で保存されます。

dd/mm/aaaa

一致する行を検索するクエリがあり、1 つの要件は、ユーザーが日付 (期間) 間で検索できる必要があることです。

日付だけがあれば、ミステリーはありません。クエリを使用できます。

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

問題は、値が日付ではない行に到達すると、このクエリが失敗することです。

検索する行が数千になる可能性があるため、そのクエリを実行する効率的な方法は何でしょうか?

4

2 に答える 2

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. CONVERTSQL Server がフィルターの後に実行しようとすることを実際に制御することはできません。

サブクエリや 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ここでも、ビューを照会するときにa を発行する必要があります。


一時テーブルを使用します。

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

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

とユーザー設定DATEFORMATの間の競合から身を守るために、引き続き使用したい場合があります。ISDATE


いいえ、別の(現在は削除された)回答で提案されているように、文字列パターン マッチングを使用して文字列を日付として検証しようとしないでください。

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

うるう年を含むすべての有効な日付を処理するには、かなり複雑で手間のかかる検証が必要です。

于 2013-01-20T04:08:44.987 に答える
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データ型はありませんが、概念は変わりません。

于 2013-01-20T04:54:13.880 に答える