sql >> データベース >  >> RDS >> Sqlserver

日時値(SQL Server)の時間部分を削除するにはどうすればよいですか?

    SQLServer2008以降

    もちろん、SQL Server 2008以降では、最速の方法はConvert(date, @date)です。 。これは、datetimeにキャストバックできます またはdatetime2 必要に応じて。

    SQL Server 2005以前で本当に最高のものは何ですか?

    SQL Serverの日付から時間を切り捨てるのに最も速いものについて一貫性のない主張を見てきました。また、テストを行ったと言う人もいますが、私の経験は異なります。それでは、もっと厳しいテストを行い、全員にスクリプトを持たせて、間違いがあった場合に人々が私を訂正できるようにしましょう。

    フロート変換は正確ではありません

    まず、datetimeを変換しないようにします float 、正しく変換されないため。時間の削除を正確に行うことでうまくいくかもしれませんが、これは安全な操作であり、安全ではないことを開発者に暗黙的に伝えるため、これを使用することはお勧めできません。 。ご覧ください:

    declare @d datetime;
    set @d = '2010-09-12 00:00:00.003';
    select Convert(datetime, Convert(float, @d));
    -- result: 2010-09-12 00:00:00.000 -- oops
    

    これは、コードやオンラインの例で人々に教えるべきものではありません。

    また、それは最速の方法でもありません!

    証明–パフォーマンステスト

    いくつかのテストを自分で実行して、さまざまなメソッドが実際にどのように積み重なるかを確認したい場合は、このセットアップスクリプトを使用して、テストをさらに下に実行する必要があります。

    create table AllDay (Tm datetime NOT NULL CONSTRAINT PK_AllDay PRIMARY KEY CLUSTERED);
    declare @d datetime;
    set @d = DateDiff(Day, 0, GetDate());
    insert AllDay select @d;
    while @@ROWCOUNT != 0
       insert AllDay
       select * from (
          select Tm =
             DateAdd(ms, (select Max(DateDiff(ms, @d, Tm)) from AllDay) + 3, Tm)
          from AllDay
       ) X
       where Tm < DateAdd(Day, 1, @d);
    exec sp_spaceused AllDay;  -- 25,920,000 rows
    

    これにより、データベースに427.57 MBのテーブルが作成され、実行に15〜30分かかることに注意してください。データベースが小さく、10%の拡張に設定されている場合、最初に十分なサイズを設定した場合よりも時間がかかります。

    次に、実際のパフォーマンステストスクリプトについて説明します。行をクライアントに返さないことが目的であることに注意してください。これは、2600万行で非常に高価であり、メソッド間のパフォーマンスの違いを隠すためです。

    パフォーマンス結果

    set statistics time on;
    -- (All queries are the same on io: logical reads 54712)
    GO
    declare
        @dd date,
        @d datetime,
        @di int,
        @df float,
        @dv varchar(10);
    
    -- Round trip back to datetime
    select @d = CONVERT(date, Tm) from AllDay; -- CPU time = 21234 ms,  elapsed time = 22301 ms.
    select @d = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 23031 ms, elapsed = 24091 ms.
    select @d = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23782 ms, elapsed = 24818 ms.
    select @d = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 36891 ms, elapsed = 38414 ms.
    select @d = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 102984 ms, elapsed = 109897 ms.
    select @d = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 103390 ms,  elapsed = 108236 ms.
    select @d = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 123375 ms, elapsed = 135179 ms.
    
    -- Only to another type but not back
    select @dd = Tm from AllDay; -- CPU time = 19891 ms,  elapsed time = 20937 ms.
    select @di = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 21453 ms, elapsed = 23079 ms.
    select @di = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23218 ms, elapsed = 24700 ms
    select @df = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 29312 ms, elapsed = 31101 ms.
    select @dv = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 64016 ms, elapsed = 67815 ms.
    select @dv = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 64297 ms,  elapsed = 67987 ms.
    select @dv = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 65609 ms, elapsed = 68173 ms.
    GO
    set statistics time off;
    

    いくつかのとりとめのない分析

    これについてのいくつかのメモ。まず、GROUP BYまたは比較を実行するだけの場合は、datetimeに戻す必要はありません。 。したがって、表示目的で最終値が必要でない限り、これを回避することでCPUを節約できます。変換されていない値をGROUPBYして、変換をSELECT句にのみ入れることもできます:

    select Convert(datetime, DateDiff(dd, 0, Tm))
    from (select '2010-09-12 00:00:00.003') X (Tm)
    group by DateDiff(dd, 0, Tm)
    

    また、数値変換がdatetimeに戻るのに少しだけ時間がかかることも確認してください。 、ただしvarchar 変換はほぼ2倍になりますか?これにより、クエリの日付計算に専念しているCPUの部分が明らかになります。日付の計算を伴わないCPU使用率の部分があり、これは上記のクエリでは19875ミリ秒に近いもののようです。次に、変換には追加の量が必要になるため、変換が2つある場合、その量は約2回使い果たされます。

    さらに詳しく調べると、Convert(, 112)と比較して 、Convert(, 101) クエリには追加のCPUコストがかかります(より長いvarcharを使用するため) ?)、dateへの2回目の変換のため varcharへの最初の変換ほど費用はかかりません 、ただしConvert(, 112) 同じ20000ミリ秒のCPU基本コストに近いです。

    上記の分析に使用したCPU時間の計算は次のとおりです。

         method   round  single   base
    -----------  ------  ------  -----
           date   21324   19891  18458
            int   23031   21453  19875
       datediff   23782   23218  22654
          float   36891   29312  21733
    varchar-112  102984   64016  25048
    varchar-101  123375   65609   7843
    
    • ラウンド datetimeに戻るラウンドトリップのCPU時間です 。

    • シングル 代替データ型(時間部分を削除するという副作用があるもの)への単一の変換のCPU時間です。

    • ベース singleから減算する計算です 2つの呼び出しの違い:single - (round - single) 。これは、そのデータ型とdatetimeとの間の変換を想定した球場の図です。 どちらの方向でもほぼ同じです。この仮定は完全ではないようですが、1つの例外を除いて、値はすべて20000ミリ秒に近いため、近いです。

    もう1つの興味深い点は、基本コストが単一のConvert(date)とほぼ等しいことです。 メソッド(サーバーはdatetimeの最初の4バイトから整数の日部分を内部的に抽出できるため、コストはほぼ0である必要があります。 データ型)。

    結論

    つまり、一方向のvarcharは、次のようになります。 変換方法には約1.8μsかかり、一方向のDateDiff メソッドには約0.18μsかかります。これは、25,920,000行の合計18458ミリ秒のテストで最も保守的な「ベースCPU」時間に基づいているため、23218ミリ秒/25920000=0.18μsです。明らかな10倍の改善はかなりのように思えますが、数十万行を処理するまでは率直に言ってかなり小さいです(617k行=1秒の節約)。

    この小さな絶対的な改善があったとしても、私の意見では、DateAdd メソッドは、パフォーマンスと明快さの最良の組み合わせであるため、勝ちます。 0.50000004の「マジックナンバー」を必要とする答え いつか誰かを噛むことになるでしょう(5つのゼロまたは6つ???)、さらにそれは理解するのが難しいです。

    追記

    時間があれば、0.50000004を変更します to '12:00:00.003' そしてそれがどのように行われるかを見てください。同じdatetimeに変換されます 価値があり、覚えやすいと思います。

    興味のある方のために、上記のテストは@@Versionが以下を返すサーバーで実行されました。

    Microsoft SQL Server 2008(RTM)-10.0.1600.22(Intel X86)2008年7月9日14:43:34 Copyright(c)1988-2008 Microsoft Corporation Standard Edition on Windows NT 5.2(Build 3790:Service Pack 2)



    1. タスクリスト

    2. ラッパークラスの機能変換

    3. 行の値をCSVにマージします(SQL Serverの場合はGROUP_CONCATとも呼ばれます)

    4. ランニングトータル/ランニングバランスを計算する