SQL Server 2012 の T-SQL には,ANSI の FIRST_VALUE と LAST_VALUE のサポート,FETCH と OFFSET による宣言的データページング,.NET の構文解析および書式設定機能など,多数の改善が行われている。
フェッチとオフセット
SQL Server でサーバサイドページングを実装しようとする場合,現状では命令的なテクニックを使用することが多い。結果セットを一時テーブルに読み込んで行番号を付け加えて,そこから必要な範囲を選び出す,というような方法だ。その他 ROW_NUMBER と OVER パターンを使用したもう少し現代的な方法や,カーソル処理に固執したものも一部にはある。これらのテクニックは難しくはないが,いずれも処理時間を要したり,エラーが発生しやすかったりする。さらに開発者それぞれに好みの実装があって,統一性に欠けているのが現状だ。
SQL Server 2012 では完全に宣言的なデータページングのサポートを追加することによって,この問題に対処している。この目的のため,OFFSET と FETCH NEXT オプションを T-SQL の ORDER BY 句に追加する機能が提供された。 現時点では,これらはパフォーマンス上の最適化ではない。SQL Server が行うのは,開発者が手動で行っているのと同じようなことだ。しかし Greg Low 博士 がプレゼンテーションで述べているように,クエリオプティマイザの開発者はこの状況を改善可能な立場にある。オプションを追加することによって,クエリが何を意図して記述されているかを彼らが知ることができるからだ。
Over 句のウィンドウ操作
現在行と直前の行とのタイムスタンプの差分から経過時間を取得したい場合などのように,行間の差に基づいたクエリの記述が必要なことがある。カーソルを使えば実装は簡単だが,コードスタイルとパフォーマンスは最悪になる。行単位でサブクエリを実行する方法では,得られる結果に比較して処理が非常に高価なものになる。クライアント側で問題を処理するという手段もあるが,クライアント側がプログラム言語で記述されていることが条件になる。レポートツールなどの場合はそうはいかない。
今回のリリースでは,LAG 関数を使って直前の行を直接アクセス することが可能になった。直前行の参照が明示的に宣言されているため,クエリアナライザはそれをメモリに保持しておくことができる。これによってサブクエリが不要となり,結果的にパフォーマンスの大幅な向上が実現される。デフォルトでは直前の行が対象だが,オフセットを指定することで,もっと前の行を参照することも可能だ。
LAG とその対である LEAD は共に ANSI 規格の一部である。Microsoft が SQL Server 2005 で OVER 句を部分的に導入して以来,開発者から望まれていた 機能だ。
FIRST_VALUE と LAST_VALUE も今回のリリースでサポートされている。
リフレクション
クエリあるいはストアドプロシージャが何を返すか判断する必要がある場合,これまでは SET FMTONLY コマンドが使用されていた。これによって実際にクエリを行わずに,返される列をプレビュー可能だが,残念なことに取得できる情報は,クエリ実行時に取得できる列の定義情報に限られていた。
新たに用意された sp_describe_first_result_set プロシージャを使用すれば,クエリあるいはストアドプロシージャが返す最初のリザルトセットのデータ型とスケール,ソースとなるテーブル/列,さらには更新可能な列かあるいは計算列か,といった多くの情報を取得することができる。sys.dm_exec_describe_first_result または sys.dm_exec_describe_first_result_set_for_object という動的管理ビューで情報を参照することも可能だ。
防御的コーディング
ストアドプロシージャを呼び出す側の開発者は,これまではプロシージャを作成した同僚に振り回される立場だった。プロシージャが何を返すのかがコンパイル時点ではまったく保証されないため,たまたま行われた互換性のない変更から大きな問題を被る場合があったのだ。T-SQL でもこのようなミスを防ぐ手段は提供されていないが,RESULT SETS オプションを使うことで最小限に抑制することができる。
RESULT SETS オプション を指定したストアドプロシージャは,そこで指定されたデータ構造を返さなければならない。リザルトセットが要求されたものと違っている場合,そのバッチはエラーで終了する。ただしこれは実行時エラーなので,この機能を利用する場合は完全なユニットテストも作成して,エラーがトリガされることを製品出荷前に確認しておくべきだろう。
エラー処理
T-SQL には 2005 から TRY-CATCH が追加されているが,不思議なことにこれまで THROW は存在していなかった。引数を省略した場合の THROW の動作は C# あるいは VB の catch ブロック内と同じで,その時点でキャッチした情報を一切失うことなく例外を再度スローする。ログを取得した後に項目を再度 try キューに追加して,問題の発生をアプリケーションに通知するような場合には便利な動作だ。
引数を指定した場合の THROW は RAISERROR と同じような動作をするが,sys.messages にないエラー番号をサポートすることと,severity が常に 16 であることが異なる。キャッチされない THROW エラーが常にバッチを終了させる点も RAISERROR とは違う。
解析と変換
カルチャ指定オプションを備えた PARSE 関数がサポートされるようになった。カルチャは .NET フレームワークでサポートされているものでなければならない。指定されたカルチャからは実装方法と合わせて,TRY_PARSE バージョンで利用可能かどうかが参照される。
TRY_CONVERT 関数も追加された。これと TRY_PARSE 関数とは,どちらも変換に失敗した場合 null を返す。
また,FORMAT 関数で .NET の書式文字列を使用するようになった。 STR のようなネイティブ関数と比べると実行速度では劣るが,柔軟性の点で優れている。
日付と時刻関数
不十分な内容であることには変わりないが,日付/時刻処理がいくらか改善された。月の最後の日を返す EOMONTH 関数はレポート作成に便利な機能だ。単一文字列ではなく,パラメータセットを指定して日付あるいは時刻を構築する xxxFROMPARTS という一連の関数が追加された。さらに日付を扱う型として Date,DateTime,DateTime2,DateTimeOffset,SmallDate,Time などが提供されている。
その他の機能
Choose 関数は Access と Visual basic から T-SQL に転用されたものだ。ある種の状況下では CASE の冗長性の少ないバージョンとして利用することができる。これらの言語から同じく拝借した関数に IIF がある。
CONCAT で文字列を連結することができる。他のデータベース言語からのコード移植を容易にすることに加えて,+演算子とは null の処理が異なっている。Itzik Ben-Gan 氏の説明によると,
> +連結演算子は,NULL 入力に対して NULL を生成します。CONCAT 関数では,連結する前に NULL を空文字列に変換します。COLAESCE 関数を使用して NULL 入力を空文字列に変換すれば同じことができますが,乱雑なコードになります。