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

AT TIME ZONE – SQLServer2016の新しいお気に入りの機能


    画像©マークボイル|ニューサウスウェールズ州オーストラリアデイカウンシル。
    各アーティストの画像プロパティ。無断転載を禁じます。

    AT TIME ZONEはとてもクールな機能で、Microsoftが12月からそれについてのページを持っていたとしても、最近まで気づいていませんでした。

    私はオーストラリアのアデレードに住んでいます。そして、世界の他の10億人以上の人々のように、アデレードの人々は30分のタイムゾーンにいることに対処しなければなりません。冬はUTC+9:30、夏はUTC + 10:30です。北半球でこれを読んでいる場合を除いて、「冬」とは4月から10月を意味することを覚えておく必要があります。夏は10月から4月で、サンタクロースは冷たい飲み物を片手にビーチに座り、濃い赤いスーツとあごひげで汗を流します。もちろん、彼が命を救っていない限り。

    オーストラリア内には、3つの主要なタイムゾーン(西部、中央部、東部)がありますが、オーストラリアの北端に伸びる3つの州(西オーストラリア州、クイーンズランド州、NT州)にはないため、夏には5つになります。日光を節約してみてください。彼らは赤道に十分近く、気にしないか、そのようなものです。滑走路がニューサウスウェールズ州とクイーンズランド州の国境を越えているゴールドコースト空港にとっては、とても楽しいことです。

    SQL ServerでUTCとローカル(およびその逆)の間の変換を処理する必要がないため、データベースサーバーはUTCで実行されることがよくあります。何年も前に、応答時間とともに発生したインシデントをリストしたレポートを修正する必要があったことを覚えています(それ以来、これについてブログに書いています)。 SLAの測定は非常に簡単でした。お客様の勤務時間中に1つのインシデントが発生し、1時間以内に対応したことがわかりました。勤務時間外に別の事件が発生し、2時間以内に対応したことがわかりました。この問題は、タイムゾーンが変更された期間の終わりにレポートが作成され、実際に午後5時30分(時間外)に発生したインシデントが午後4時30分(時間内)に発生したかのようにリストされるときに発生しました。 。応答には約90分かかりましたが、問題はありませんでしたが、レポートにはそれ以外の結果が表示されていました。

    これはすべてSQLServer2016で修正されています。

    SQLServer2016でATTIMEZONEを使用する方法

    ここで、AT TIME ZONEを使用すると、「20160101 00:00 +10:30」と言う代わりに、タイムゾーンオフセットのない日時値から始めて、ATTIMEZONEを使用してアデレードにあることを説明できます。

    SELECT CONVERT(datetime,'20160101 00:00') 
        AT TIME ZONE 'Cen. Australia Standard Time';
     
    -- 2016-01-01 00:00:00.000 +10:30

    また、AT TIME ZONEを再度追加することで、これをアメリカの時間に変換できます。

    SELECT CONVERT(datetime,'20160101 00:00') 
        AT TIME ZONE 'Cen. Australia Standard Time' 
        AT TIME ZONE 'US Eastern Standard Time';
     
    -- 2015-12-31 08:30:00.000 -05:00

    今、私はこれがはるかに長蛇の列であることを知っています。また、次のようなエラーを回避するために、文字列を明示的に日時に変換する必要があります。

    引数のデータ型varcharは、ATTIMEZONE関数の引数1では無効です。

    しかし、それは長蛇の列ですが、私はそれが大好きです。アデレードが+10:30にあること、または東部が-5:00にあることを理解する必要がなかったため、名前でタイムゾーンを知る必要がありました。夏時間が適用されるべきかどうかを判断することは私のために処理され、ベースラインを確立するためにローカルからUTCへの変換を行う必要はありませんでした。

    すべての情報が含まれているWindowsレジストリを使用して機能しますが、残念ながら、過去を振り返ると完璧ではありません。オーストラリアは2008年に日付を変更し、米国は2005年に日付を変更しました。両国は、年間を通じて日光を節約しています。 ATTIMEZONEはこれを理解しています。しかし、2000年のオーストラリアでは、シドニーオリンピックのおかげで、オーストラリアが約2か月前に夏時間を開始したことを理解していないようです。これは少し苛立たしいことですが、SQLのせいではありません。Windowsのせいにする必要があります。 Windowsレジストリは、その年に使用された修正プログラムを覚えていないと思います。 (注意:Windowsチームの誰かに修正を依頼する必要があるかもしれません…)

    ただし、有用性は継続します!

    そのタイムゾーン名は定数である必要はありません。変数を渡すことができ、列を使用することもできます:

    WITH PeopleAndTZs AS
    (
      SELECT * FROM (VALUES 
        ('Rob',   'Cen. Australia Standard Time'),
        ('Paul',  'New Zealand Standard Time'),
        ('Aaron', 'US Eastern Standard Time')
      ) t (person, tz)
    )
    SELECT tz.person, SYSDATETIMEOFFSET() AT TIME ZONE tz.tz
      FROM PeopleAndTZs tz;
     
    /*
      Rob      2016-07-18 18:29:11.9749952 +09:30
      Paul     2016-07-18 20:59:11.9749952 +12:00
      Aaron    2016-07-18 04:59:11.9749952 -04:00
    */

    (ここアデレードでは午後6時30分前に実行したため、パウロがいるニュージーランドでは午後9時近く、アーロンがいるアメリカ東部では今朝5時近くになります。)

    これにより、世界中のどこにいても、人々が何時であるかを簡単に確認でき、手動の日時変換を実行しなくても、問題に対応するのに最適な人を確認できます。そしてそれ以上に、それは私が過去の人々のためにそれをすることを可能にするでしょう。営業時間中に最も多くのイベントが発生する可能性があるタイムゾーンを分析するレポートを作成できます。

    これらのタイムゾーンは、sys.time_zone_infoにリストされています。 、現在のオフセットとは何か、および夏時間が現在適用されているかどうか。

    name

    current_utc_offset

    is_currently_dst
    シンガポール標準時間

    +08:00

    0
    W。オーストラリア標準時

    +08:00

    0
    台北標準時

    +08:00

    0
    ウランバートル標準時

    +09:00

    1
    北朝鮮標準時間

    + 08:30

    0
    AusCentralW.標準時

    +08:45

    0
    ザバイカル標準時

    +09:00

    0
    東京標準時

    +09:00

    0

    sys.time_zone_infoからの行のサンプリング

    名前が何なのか本当に興味がありますが、とにかく。そして、15分に「AusCentralW.StandardTime」と呼ばれるタイムゾーンがあるのを見るのは興味深いことです。図に行きます。また、現在DSTを監視している場合でも、場所は標準時の名前を使用して参照されていることにも注意してください。上記のリストにあるウランバートルのように、ウランバートルの夏時間としてリストされていません。これにより、ATTIMEZONEの使用を開始したときにループが発生する可能性があります。

    タイムゾーンでパフォーマンスの問題が発生する可能性はありますか?

    さて、ATTIMEZONEを使用するとインデックス作成にどのような影響があるのか​​疑問に思っていると思います。

    計画の形に関しては、一般的な日時オフセットの処理と同じです。 AdventureWorks列Sales.SalesOrderHeader.OrderDate(rf_IXODというインデックスを作成した)のように日時の値がある場合は、次の両方のクエリを実行します。

    select OrderDate, SalesOrderID
      from Sales.SalesOrderHeader
      where OrderDate >= convert(datetime,'20110601 00:00') at time zone 'US Eastern Standard Time' 
        and OrderDate <  convert(datetime,'20110701 00:00') at time zone 'US Eastern Standard Time' ;

    そしてこのクエリ:

    select OrderDate, SalesOrderID
      from Sales.SalesOrderHeader
      where OrderDate >= convert(datetimeoffset,'20110601 00:00 -04:00')
        and OrderDate <  convert(datetimeoffset,'20110701 00:00 -04:00');

    どちらの場合も、次のようなプランが表示されます。

    しかし、もう少し詳しく調べてみると、問題があります。

    AT TIME ZONEを使用しているものは、統計をあまりうまく使用していません。実際には217行しかないのに、そのインデックスシークから5,170行が出てくると考えています。なぜ5,170行なのですか。さて、アーロンの最近の投稿「見積もりに注意を払う」は、ポールからの投稿「複数の述語のカーディナリティ見積もり」を参照してそれを説明しています。 5,170は31,465(表の行)* 0.3 * sqrt(0.3)です。

    2番目のクエリは正しくなり、217と推定されます。関数は含まれていません。

    これはおそらく十分に公平です。つまり、計画を作成する時点では、必要な情報をレジストリに要求していないため、実際にいくつ見積もるかはわかりません。しかし、それが問題になる可能性があります。

    問題にならないことがわかっている述語を追加すると、実際の見積もりはさらに低下し、89.9行になります。

    select OrderDate, SalesOrderID
      from Sales.SalesOrderHeader
      where OrderDate >= convert(datetime,'20110601 00:00') at time zone 'US Eastern Standard Time' 
      and OrderDate   <  convert(datetime,'20110701 00:00') at time zone 'US Eastern Standard Time'
      and OrderDate   >= convert(datetimeoffset,'20110601 00:00 +14:00')
      and OrderDate   <  convert(datetimeoffset,'20110701 00:00 -12:00');

    行数が多すぎると割り当てられるメモリが多すぎることを意味しますが、推定する行数が少なすぎるとメモリが少なすぎる可能性があり、問題を修正するために流出が必要になる可能性があります(パフォーマンスの観点からは悲惨なことがよくあります)。見積もりがいかに悪いかについての詳細は、アーロンの投稿を読んでください。

    以前からそれらの人々の値の表示を処理する方法を検討するとき、私は次のようなクエリを使用できます:

    WITH PeopleAndTZs AS
    (
      SELECT * FROM (VALUES 
        ('Rob',   'Cen. Australia Standard Time'),
        ('Paul',  'New Zealand Standard Time'),
        ('Aaron', 'US Eastern Standard Time')
      ) t (person, tz)
    )
    SELECT tz.person, o.SalesOrderID, o.OrderDate AT TIME ZONE 'UTC' AT TIME ZONE tz.tz
    FROM PeopleAndTZs tz
    CROSS JOIN Sales.SalesOrderHeader o
    WHERE o.SalesOrderID BETWEEN 44001 AND 44010;

    そして、この計画を入手してください:

    …そのような懸念はありません。右端のComputeScalarは日時OrderDateをUTCのdatetimeoffsetに変換し、左端のComputeScalarはそれを個人の適切なタイムゾーンに変換しています。警告は、私がCROSS JOINを実行しているためであり、それは完全に意図的なものでした。

    他の時間変換方法の長所と短所

    AT TIME ZONEの前は、私のお気に入りの1つでしたが、多くの場合評価されていませんでしたが、SQL2008の機能はデータ型datetimeoffsetでした。これにより、今年アデレードで新年を祝った「20160101 00:00 +10:30」などのタイムゾーンで日付/時刻データを保存することもできます。それが米国東部でいつだったかを確認するには、関数SWITCHOFFSETを使用できます。

    SELECT SWITCHOFFSET('20160101 00:00 +10:30', '-05:00');
     
    -- 2015-12-31 08:30:00.0000000 -05:00

    これは同じ瞬間ですが、世界の別の場所にあります。ノースカロライナ州やニューヨーク州の誰かに電話をかけていて、アデレードで真夜中過ぎだったので、彼らに明けましておめでとうと言ったら、彼らは「どういう意味ですか?ここはまだ大晦日の朝食の時間です!」

    問題は、これを行うには、1月のアデレードが+10:30、米国東部が–5:00であることを知る必要があるということです。そして、それはしばしば苦痛です。特に、3月下旬、4月上旬、10月、11月上旬について質問すると、夏時間のために1時間ずつ変わるため、他の国の人々がどのタイムゾーンにいるのかわからない時期です。すべて異なる規則に従ってそうします。私のコンピューターは、人々が現在どのタイムゾーンにいるのかを教えてくれますが、他の時期にどのタイムゾーンにいるのかを知るのははるかに困難です。

    タイムゾーン間の変換に関するその他の情報、およびAaronのような人々が夏時間にどのように対処したかについては、次のリンクを参照してください。

    • AT TIME ZONEを使用して古いレポートを修正する(それは私です!)
    • SQL Serverのタイムゾーン間の変換を処理する–パート1
    • SQL Serverのタイムゾーン間の変換を処理する–パート2
    • SQL Serverのタイムゾーン間の変換を処理する–パート3

    そして、いくつかの公式のMicrosoftドキュメント:

    • AT TIME ZONE(Transact-SQL)
    • sys.time_zone_info(Transact-SQL)
    • 夏時間のヘルプとサポート

    AT TIME ZONEを使用する必要がありますか?

    タイムゾーンは完璧ではありません。しかし、それは本当に便利です–信じられないほどそうです。列と変数を入力として受け入れるのに十分な柔軟性があり、その可能性は非常に大きいと思います。しかし、それによって私の見積もりが出てしまう場合は、注意する必要があります。表示の目的では、これはまったく問題ではないはずですが、それが最も役立つことがわかります。オフセットやDSTについて頭を悩ませることなく、UTCとの間で(または現地時間との間で)表示値を簡単に変換できるようにすることは大きなメリットです。

    これは本当にSQLServer2016の私のお気に入りの機能の1つです。私は非常に長い間このようなものを求めてきました。

    ああ、そして30分タイムゾーンのそれらの10億人のほとんどはインドにいます。しかし、あなたはおそらくすでにそれを知っていたでしょう…


    1. JDBC PreparedStatementでLIKEクエリを使用できませんか?

    2. Oracle SQLを使用して、1つの出力日数と曜日をどのように出力しますか?

    3. 一括更新および削除操作を実行する際のPostgreSQLデッドロックの回避

    4. SQL Server:PARTITIONBYとGROUPBYの違い