都内で働くSEの技術的なひとりごと / Technical soliloquy of System Engineer working in Tokyo

都内でサラリーマンやってます。SQL Server を中心とした (2023年からは Azure も。) マイクロソフト系(たまに、OSS系などマイクロソフト以外の技術も...)の技術的なことについて書いています。日々の仕事の中で、気になったことを技術要素関係なく気まぐれに選んでいるので記事内容は開発言語、インフラ等ばらばらです。なお、当ブログで発信、発言は私個人のものであり、所属する組織、企業、団体等とは何のかかわりもございません。ブログの内容もきちんと検証して使用してください。英語の勉強のため、英語の

実行プランの読み方をまとめてみる - その11 ( Where 句の条件の型が列の型と異なる場合の実行プランを読んでみる ) -

 実行プランの読み方は情報も少ないので、内容の理解に苦しんでいる方も多いかもしれません。過去にはいくつか投稿しているので、そちらも参考にしてください。
ryuchan.hatenablog.com
ryuchan.hatenablog.com
ryuchan.hatenablog.com
ryuchan.hatenablog.com
ryuchan.hatenablog.com
ryuchan.hatenablog.com
ryuchan.hatenablog.com
ryuchan.hatenablog.com
ryuchan.hatenablog.com
ryuchan.hatenablog.com

 さて、過去の投稿を参考にしつつ、今回投稿する記事も参考にして、実行プランをスラスラ読めるようにしましょう。早速下記のクエリを実行して見ましょう。
※データベースは、ここからダウンロードしてください。
※クエリはわざと不適切な状態にしています。

-- OrderDate 型は datetime なので型が間違っている。
DECLARE @min_date datetime2
DECLARE @max_date datetime2

SET @min_date = '2011-05-31 00:00:00.000'
SET @max_date = '2011-07-31 00:00:00.000'

SET STATISTICS PROFILE ON

SELECT 
    SalesOrderID,
    RevisionNumber,
    OrderDate
FROM 
    -- OrderDate のインデックスを作成した。
    Sales.SalesOrderHeader WITH (INDEX(IX_SalesOrderHeader_OrderDate))
WHERE 
    OrderDate > @min_date
AND OrderDate < @max_date

SET STATISTICS PROFILE OFF

 実行プランは下図の通りです。
f:id:koogucc11:20180819234905p:plain

 Computer Scalar、Concatenation, Merge Interval オペレータって何しているかはっきりわかりませんよね?それぞれのオペレーターのプロパティを見ても、定義済みの値が [Expr1008] = (Expr1003, Expr1006), [Expr1009] = (Expr1004, Expr1007), [Expr1010] = (Expr1002, Expr1005) とか、出力一覧がExpr1003, Expr1004, Expr1002とか意味不明ですね。expr といえば、整数計算とか式評価を思い浮かべますが....

  • Compute Scalar
    f:id:koogucc11:20180819235900p:plain

  • Concatenation
    f:id:koogucc11:20180819235903p:plain

  • Merge Interval
    f:id:koogucc11:20180819235906p:plain

 さて、今回のような実行プランを見ていくにはグラフィカルな実行プランでは詳細はわかりません。各オペレーターの詳細情報を参照するには、実行プランの XML を参照するか、SET STATISTICS PROFILE ON の結果を参照する必要があります。今回は、SET STATISTICS PROFILE ON の結果を見ていくことにしましょう。今回実行したクエリには、SET STATISTICS PROFILE ON を付加してあるので、下図の赤枠の情報も出力されているかと思います。赤枠部分の情報を詳しく見ていきましょう。
f:id:koogucc11:20180820001109p:plain

 まず、Compute Scalar オペレーターの内容から参照してみましょう。GetRangeWithMismatchedTypes という関数が発生しています。文字通り型指定が間違っているけど、範囲を取得するみたいな関数(あっているかはわかりません。)ですね。下記のパターンでは、Expr1003 = @min_date, Expr1004 = NULL がそれぞれ割り当てられます。
※(6)が何に使用されるのかイマイチ推測できない…

([Expr1003],[Expr1004],[Expr1002])=GetRangeWithMismatchedTypes([@min_date],NULL,(6))

 下記のパターンでは、Expr1006 = NULL , Expr1007 = @max_date がそれぞれ割り当てられます。

([Expr1006],[Expr1007],[Expr1005])=GetRangeWithMismatchedTypes(NULL,[@max_date],(10))

 上記の結果をクエリに当てはめると下記のようになります。

SELECT
    SalesOrderID,
    RevisionNumber,
    OrderDate
FROM
    Sales.SalesOrderHeader WITH(INDEX(IX_SalesOrderHeader_OrderDate))
WHERE
    (
        OrderDate > Expr1003
    AND OrderDate < Expr1004
    )
AND (
        OrderDate < Expr1007
    AND OrderDate > Expr1006
    )

 次に、Concatenation オペレーターをみていきましょう。 OrderDate > Expr1003 と OrderDate > Expr1006、OrderDate < Expr1004 と OrderDate < Expr1007 が結合されます。

[Expr1008] = ([Expr1003], [Expr1006]), [Expr1009] = ([Expr1004], [Expr1007]), [Expr1010] = ([Expr1002], [Expr1005])

 次に、Merge Interval オペレーターでダブった範囲が消去されます。OrderDate > Expr1003 と OrderDate > Expr1006 から、Expr1008 = @min_date となります。OrderDate < Expr1004 と OrderDate < Expr1007 から、Expr1009 = @max_date となります。
 
 最後に Expr1008 と Expr1009 でシークを行ないます。

OBJECT:([AdventureWorks].[Sales].[SalesOrderHeader].[IX_SalesOrderHeader_OrderDate]), SEEK:([AdventureWorks].[Sales].[SalesOrderHeader].[OrderDate] > [Expr1008] AND [AdventureWorks].[Sales].[SalesOrderHeader].[OrderDate] < [Expr1009]) ORDERED FORWARD, FORCEDINDEX

 これは一例ですが、型の指定が間違っているだけで、SQL Server はこのような複雑な処理を行ないます。少しでも負荷を減らすためにも型を正しく指定しましょう。下記の型が正しいクエリを発行してみましょう。

-- datetime2 ではなく、datetime を指定する。
DECLARE @min_date datetime
DECLARE @max_date datetime

SET @min_date = '2011-05-31 00:00:00.000'
SET @max_date = '2011-07-31 00:00:00.000'

SET STATISTICS PROFILE ON

SELECT 
    SalesOrderID,
    RevisionNumber,
    OrderDate
FROM 
    Sales.SalesOrderHeader WITH (INDEX(IX_SalesOrderHeader_OrderDate))
WHERE 
    OrderDate > @min_date
AND OrderDate < @max_date

SET STATISTICS PROFILE OFF

 
 実行プランを参照すると、Computer Scalar、Concatenation, Merge Interval オペレーターが全て無くなっています。かなりスッキリしましたね。
f:id:koogucc11:20180820021533p:plain

 実行プランが単純化されることで、クエリの解析は高速化されますし、キャッシュプランのサイズも小さくなります。その結果、データベースサーバーへの負荷が軽減されます。条件に指定する型は正確に指定するように意識しましょう。

 SQL Server 2017 でのパフォーマンスチューニングはどう変わるんだろう。2012 で止まってる自分は未知の世界。

SQL Server 2017 Query Performance Tuning: Troubleshoot and Optimize Query Performance

SQL Server 2017 Query Performance Tuning: Troubleshoot and Optimize Query Performance

SQL Server Management Studio を触っていて、GO コマンドをみてふと Go のことを思い出したので、ちょっと勉強をしてみる

 今年のお盆休みは初の10連休でした。これだけ休みが長いとふと何かを勉強したくなるもんですね。最近はデータベースばかりでプログラミングに触れていませんでした。SQL Server を触っていて、GO コマンドを見たとき下記の記事を思いだしました。
codezine.jp

 求人数的には、RubyPython には及びませんが、年収では堂々一位です。今後人気が出そうですね。ミーハーなこともあり、早速 GO のお勉強を開始したいと思います(笑) ちょっとググってみるとわかりやすい記事がいくつも見つかります。下記のサイトが一番わかりやすそうです。
なぜGo言語 (golang) はよい言語なのか・Goでプログラムを書くべき理由

 悪い点がいくつか挙げられていました。

Generics (template) がない
継承がない
例外がない。まるで1970年代に設計されたかのようである。
非知的なプログラマのためにデザインされている。

 ここで、「非知的なプログラマのためにデザインされている。」という項目がありましたが、大規模プロジェクトになると様々なレベルの開発者が関わるため、この点は非常に重要だと感じます。知識のない開発者に継承や Generics などを下手に扱わせて、メンテナンス性などを落とすくらいなら、ある程度制限がある方がソースのレベルも一定に保てていいように思います。※ただ、やっぱり継承欲しいです。

 try catch finally などの例外処理がないとありますが、とりあえず手法は存在するようです。try に対応する defer を使って panic() 関数でコールした内容を catch にあたる recover() 関数で処理するようです。また、この defer,panic, recover は内部処理的なものに使うのが指針のようで、API レベルのものには error オブジェクトを使用するのが本流のようです。
blog.amedama.jp

 それ以外には、繰り返し処理には do や while は存在せず、for 分のみです。switch 文では case に条件を複数指定することができます。※ case 1,2,3,4 みたいに。

 たまに、言語を勉強するなら C++ を学べばどんな言語でもできるようになるという方もいらっしゃいますが、C++ が挫折する人も多く、開発言語を習得するにはハードルが高いように思えます。Go であれば習得も速く、言語習得するには最適なような気がします。(個人的意見です。)

 とりあえず Go の開発環境をインストールして見た。面白そうなので、しばらく触ってみよう。
f:id:koogucc11:20180819131934p:plain

みんなのGo言語【現場で使える実践テクニック】

みんなのGo言語【現場で使える実践テクニック】

SQL Server のチューニングについてまとめてみる - その 25 - ( パーティションを有効に使ってみる )

 久しぶりの投稿です。梅雨前にもかかわらず関東はいい天気に恵まれ、来週の月曜日までこの天気が続くようです。
f:id:koogucc11:20180602164823j:plain


 長らくチューニングに関する投稿をしていませんでした。前回 ( その24 ) は7か月前ですね。
ryuchan.hatenablog.com

 MSDN では、パーティションについて以下のように記載されています。

パーティション テーブルとパーティション インデックスのデータは、データベース内の複数のファイル グループに分散できるように、行方向に複数の単位に分割されています。パーティション分割により、大きいテーブルとインデックスの管理可能性と拡張性が向上します。パーティション テーブルとパーティション インデックスは、SQL Server の Enterprise Edition、Developer Edition、および Evaluation Edition でのみ使用できます。

 管理と拡張性だけではなく、WHERE 句の条件として指定することによりパフォーマンスが飛躍的に向上します。それでは早速実験してみましょう。パーティション分割されたテーブル(パーティションID 1から20 まで存在するテーブル)にクエリを投げてみます。パーティションが 20 存在し、アクセスしたパーティションが 1..20 ですべてのパーティションにアクセスしたと判断できます。
f:id:koogucc11:20180602173656p:plain

 WHERE 句の条件に、$PARTITION.パーティション関数 ( パーティション列 ) を付加します。今回のケースで 1 から 20 のパーティションの中から、13 から 14 のみにアクセスしたい場合は、

$PARTITION.パーティション関数 ( パーティション列 ) BETWEEN 13 AND 14

と指定します。パーティション条件を指定した状態でクエリを実行すると、パーティションID の 13 から 14 のみにアクセスしていることが判断できます。CPU の推定コストも減少していますね。
f:id:koogucc11:20180602190121p:plain

 チューニングの記事もコツコツ投稿していないといけないですね。気力が続く限り、がんばります。

 iPad 2018 の付属品も色々買いました。

クラスタ化インデックスと非クラスタ化インデックスでこんなことやったらどうなんだろうと思ったことを実験してみる

 搭乗回数が若干減っています。#ダイヤモンド修行 がなかなか思い通りに進みません。それはさておき、最近、ふと思ったことがあり、記事を書く回数も昨年にも増して少なくなっているため、若干記事にする意味あるかなと思いながらも…記事にしてみます。

 AdventureWorks の SalesOrderDetail テーブルに対して、クラスタ化インデックス、非クラスタ化インデックスを3種類作成してみます。

CREATE CLUSTERED INDEX ClusteredIndex ON Sales.SalesOrderDetail_
(
    ModifiedDate ASC
)

CREATE NONCLUSTERED INDEX NonClusteredIndex-1 ON Sales.SalesOrderDetail_
(
    ProductID ASC,
    CarrierTrackingNumber ASC
)

CREATE NONCLUSTERED INDEX NonClusteredIndex-2 ON Sales.SalesOrderDetail_
(
    ProductID ASC,
    CarrierTrackingNumber ASC,
    ModifiedDate ASC
)

 下記のクエリを SQL Server Management Studio で実行してみます。

SELECT
    ProductID
FROM
    Sales.SalesOrderDetail_ WITH(INDEX([NonClusteredIndex-1]))
WHERE
    ProductID = 750
AND CarrierTrackingNumber = 'E46D-4692-AC'
AND ModifiedDate BETWEEN '2011-07-07 00:00:00.000' AND '2011-12-07 00:00:00.000'
SELECT
    ProductID
FROM
    Sales.SalesOrderDetail_ WITH(INDEX([NonClusteredIndex-2]))
WHERE
    ProductID = 750
AND CarrierTrackingNumber = 'E46D-4692-AC'
AND ModifiedDate BETWEEN '2011-07-07 00:00:00.000' AND '2011-12-07 00:00:00.000'
SELECT
    ProductID
FROM
    Sales.SalesOrderDetail_
WHERE
    ProductID = 750
AND CarrierTrackingNumber = 'E46D-4692-AC'
AND ModifiedDate BETWEEN '2011-07-07 00:00:00.000' AND '2011-12-07 00:00:00.000'

 実行プランの結果は下図のとおりです。
f:id:koogucc11:20180429093812p:plain

 少しでも差が出るかと思ったんですが、コスト上変わらないですね。もう少し実験してみるか。

 iPad 9.7
www.apple.com

 Apple Pencil
www.apple.com

 Logicool Keys-to-Go Ultra Slim Keyboard with iPhone Stand
www.apple.com

 ケース

 フィルム

SQL Server Management Studio の知っておいたほうが良い機能について挙げてみる - その12 - ( 列名の前後に [ ] を付けないへの要望 )

 ちょっと早起きしたので、記事書きます。前回の記事で、17.6 で対応されたオブジェクトエクスプローラーからの各情報をドラッグ&ドロップで [] を付加しないようにする機能を紹介しました。
ryuchan.hatenablog.com

 個人的には下記のパターンも対応してほしいです。あと、カンマの位置とかも制御できるといいですね。

  • テーブルをクリックする。
    f:id:koogucc11:20180324062546p:plain
  • 右クリックで、上位 n 件をクリックする。
    f:id:koogucc11:20180324065650p:plain
  • 列名およびテーブル名に [ ] が付加された SELECT 文が生成される。
    f:id:koogucc11:20180324062559p:plain

 このケースも [ ] を制御できるといいですね。ひとりごとです。

SQL Server Management Studio の知っておいたほうが良い機能について挙げてみる - その11 - ( 列名の前後に [ ] を付けない )

 しばらく、SQL Server Management Studio の機能について記事を書いていませんでした。
ryuchan.hatenablog.com

 SQL Server Management Studio 17.6 がリリースされました。
https://blogs.technet.microsoft.com/dataplatforminsider/2018/03/20/ssms-17-6-is-now-available-managed-instance-and-many-bug-fixes/

 細かい機能ですが、便利な設定が追加されていました。

Object Explorer: Added settings to allow users not to force brackets around names when dragging and dropping from Object Explorer to Query Window.

 早速設定してみましょう。

  • SQL Server management Studio を起動し、ツール→オプションをクリックします。f:id:koogucc11:20180321195359p:plain
  • Surround Object .. を false に設定します。f:id:koogucc11:20180324060606p:plain
  • テーブルを開きます。f:id:koogucc11:20180321202327p:plain
  • 列を選択します。f:id:koogucc11:20180321202338p:plain
  • ドロップします。f:id:koogucc11:20180321202349p:plain

列名に [ ] を付けずに展開することが可能になっています。細かい機能ですが、いい機能ですね。

 肉が食べたくなってきた。

熟成肉 肉バル CARNE BAR KATETE 虎ノ門店
〒105-0001 東京都港区虎ノ門1-16-2 B1
5,000円(平均)1,000円(ランチ平均)

 夏に向けて、色々購入。

 シュノーケルマスクなるものがあるかのか。
[asin:B079FH512G:detail]