Shoeisha Technology Media

CodeZine(コードジン)

記事種別から探す

N+1問題を回避せよ! LINQから出力されるSQLを見てみよう&遅延ローディングの光と闇

Visual StudioでDB連携も簡単プログラミング ~知っておきたいLINQメソッド式&ラムダ式 第3回

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2015/02/03 14:00

 本連載では、データベースプログラミングにおいてLINQをどのように活用できるのか、解説していきます。今回は、LINQが実際にはどんなSQLに変換されているのかを確認し、遅延ローディングの有無による処理の変化について解説します。

目次

前回のおさらい

 前回の記事では、データベースに対してLINQメソッド式を使ってクエリを実行する方法について解説しました。多数の条件が指定された場合でも、メソッドチェーン式にメソッドを積み重ねることで簡単に実装できることを確認できました。

 今回は、LINQが実際にはどんなSQLに変換されているのかを確認します。また、遅延ローディングの有無によってどのように処理が変わるかを意識し、パフォーマンス問題を解決する方法について解説します。なお、前回作成したサンプルを今回も引き続き使用します。

出力されるSQLを確認してみよう

 前回の解説通り、Entity Frameworkは、LINQのクエリ式やメソッド式を、最終的にSQLへと変換してデータベース上で実行しています。この変換は透過的に行われるため、プログラマはLINQを記述する際にSQLをそれほど意識する必要がありません。しかし、データベースプログラミングを行っている以上、実際にどんなSQLが実行されているかを理解するのは重要です。自分が書いたLINQがどのようなSQLとして実行されるかが理解できていないと、後ほど解説するようなパフォーマンス上の問題にぶつかることになります。データベースプログラミングにおいては「ただクエリ結果が正しければいい」だけでなく「データベースに不要な負荷を掛けないようにクエリを投げる」のも重要です。

 Entity Frameworkから実際に出力されるSQLを確認するには、リスト1の①のように、コンテキストクラスのDatabaseプロパティで取得できるSystem.Data.Entity.DatabaseオブジェクトのLogプロパティに、ログを出力するためのメソッドを指定します。なお、この機能はEntity Framework 6から追加されたものです。これだけで、Entity FrameworkからSQLを実行する際、SQLの内容やパラメータ、実行時間などが指定したメソッドの引数として渡されます。

リスト1 ログ出力の方法
using (var context = new CodeZineSampleContext())
{
  //①コンテキストクラスのDatabase.Logプロパティを使って
  //実際に発行されるSQLをコンソールに出力する
  context.Database.Log = Console.WriteLine;
  //②全件出力してみる
  IQueryable<Product> products = context.Products;
  //③結果をループして出力する
  foreach (var product in products)
  {
    //商品名だけ出力
    Console.WriteLine(product.Name);
  }
}

 出力結果はリスト2のようになります。

リスト2 Entity Frameworkが実際に発行しているSQL
2014/10/26 0:33:35 +09:00
 で接続を開きました
SELECT
    [Extent1].[Id] AS [Id],
    [Extent1].[Name] AS [Name],
    [Extent1].[Price] AS [Price],
    [Extent1].[Employee_Id] AS [Employee_Id],
    [Extent1].[Maker_Id] AS [Maker_Id]
    FROM [dbo].[Products] AS [Extent1]
-- 2014/10/26 0:33:35 +09:00
 で実行しています
-- 6 ミリ秒で完了しました。結果: SqlDataReader
注釈

 実際には、上記のSQLProductsテーブルへアクセスする前に、[INFORMATION_SCHEMA.TABLES]や[__MigrationHistory]テーブルにアクセスするSQLが発行されます。これはEntity Frameworkがソースコードのエンティティとデータベースのテーブル定義の同期を取るために、データベース初回アクセス時に自動的に発行されるもので、LINQとは無関係です。

 SQLの内容としては、ProductsテーブルのすべてのフィールドをSELECTするだけのシンプルなものです。特に条件を指定しない全件出力ですので、想定通りのSQLと言っていいでしょう。

 さて、ログが出力されたタイミングにも注目しましょう。ブレークポイントを②の箇所に仕掛けてデバッグ実行し、一行ずつステップ実行してみてください。②の全件出力の時点ではSQLは発行されず、③のforeach文を実行するタイミングでログが出力されることが確認できるでしょう。前回も解説しましたが、Entity Frameworkは、実際にデータを使うタイミングになって初めてSQLの発行を行います。

Whereメソッドで条件を指定した場合のSQL

 続いて、LINQのWhereメソッドを使って条件を指定した場合のSQLも確認してみましょう(リスト3)。

リスト3 Whereメソッドで絞り込み
//Priceが300を超えるものだけ出力する
IQueryable<Product> products = context.Products.Where(x => x.Price > 300);
↓
SELECT
    [Extent1].[Id] AS [Id],
    [Extent1].[Name] AS [Name],
    [Extent1].[Price] AS [Price],
    [Extent1].[Employee_Id] AS [Employee_Id],
    [Extent1].[Maker_Id] AS [Maker_Id]
    FROM [dbo].[Products] AS [Extent1]
    WHERE [Extent1].[Price] > 300

 「x => x.Price > 300」というラムダ式は「WHERE [Extent1].[Price] > 300」というSQLに変換されています。こちらも概ね想定通りですね。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • WINGSプロジェクト 土井 毅(ドイ ツヨシ)

    <WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2017年5月時点での登録メンバは52名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂き...

  • 山田 祥寛(ヤマダ ヨシヒロ)

    静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for ASP/ASP.NET。執筆コミュニティ「WINGSプロジェクト」代表。 主な著書に「入門シリーズ(サーバサイドAjax/XMLD...

バックナンバー

連載:Visual StudioでDB連携も簡単プログラミング ~知っておきたいLINQメソッド式&ラムダ式
All contents copyright © 2005-2017 Shoeisha Co., Ltd. All rights reserved. ver.1.5