アーカイブ

Archive for 2007年9月12日

LINQ to SQL:検索条件を指定する

2007年9月12日 コメントを残す

※この投稿はMicrosoft Visual Studio 2008 Beta2で動作を確認しています。

データの検索時にその場でいくつかの条件を指定したい、という場合があります。
あるときは成年の男性を取り出したい、また別のときは未成年で名前に「コ」の付く人を取り出したい、こんな場合にどのようにLINQを記述すればよいか考えてみましょう。

条件を適宜入力できるようにするのが良いのでしょうが、ちょっとサンプルが見づらくなるので、ここでは変数に条件を設定する形で条件の指定を行います。

まずは年齢について考えてみます。
条件として数字が与えられていたらその数字以上の年齢の人を抽出し、条件が与えられていない場合はすべての人を抽出する、というプログラムを考えてみました。

using System;
using System.Linq;

namespace LINQ4
{
    class Program
    {
        static void Main(string[] args)
        {
            LINQTESTDataContext dtc = new LINQTESTDataContext();
            dtc.Log = Console.Out;

            int? ageCheck = null;

            var query = from p in dtc.People
                        where p.Age >= (ageCheck ?? 0)
                        select p;

            foreach (var item in query)
            {
                Console.WriteLine("名前={0}, 年齢={1}", item.Name, item.Age);
            }

            Console.Read();
        }
    }
}

年齢の条件を設定する変数としてnullableなint型の変数ageCheckを利用しています。
上記のプログラムでは、ageCheckに値が設定されていません(nullを設定)。この場合 ?? 演算子の働きによって0がp.Ageとの比較対象になります。
この結果、すべての人が抽出されます。

 image

上記のプログラムを修正し、"int? ageCheck = 30;" とすると、30歳以上の人が抽出されます。

 image_3

年齢の場合、条件の指定がなければ0歳という特定の値と比較させることができますが、性別の場合はどうするのが良いのでしょうか?
この場合、条件の指定がなければ、比較しようとしているデータの値そのものと比較を行うようにプログラムを記述します。このように自分自身の値と比較するという方法はストアドプロシージャー内でよく使われたりするようです。

using System;
using System.Linq;

namespace LINQ4
{
    class Program
    {
        static void Main(string[] args)
        {
            LINQTESTDataContext dtc = new LINQTESTDataContext();
            dtc.Log = Console.Out;

            int? ageCheck = null;
            int? genderCheck = 1;

            var query = from p in dtc.People
                        where p.Age >= (ageCheck ?? 0) && p.Gender == (genderCheck ?? p.Gender)
                        select p;

            foreach (var item in query)
            {
                Console.WriteLine("名前={0}, 年齢={1}", item.Name, item.Age);
            }

            Console.Read();
        }
    }
}

抽出条件をつなぐ場合、"AND" ではなく && 演算子を利用します。
ここではまずは男性(genderCheck=1)を抽出してみました。

 image_4

ここで ?? 演算子はCOALESCEというSQL文に変換されています。同じ ?? 演算子でも状況によって異なるSQL文が生成されるというのは興味深いところです。
genderCheckにnullを設定すると、ちゃんと全員が抽出されることを確認しておきましょう。

 image_5

SQL文に渡されている@p1の値が空になり、全員が抽出されていることが確認できます。

最後に名前の部分一致検索の方法を考えてみます。
まずはString.Containsを使って次のように記述してみたのですが、、、

    int? ageCheck = null;
    int? genderCheck = null;
    string namePart = null;

    var query = from p in dtc.People
                where p.Age >= (ageCheck ?? 0)
                      && p.Gender == (genderCheck ?? p.Gender)
                      && p.Name.Contains(namePart ?? p.Name)
                select p;

これではqueryを評価する時点で「String.Contains メソッドに対しては、クライアント上で評価できる引数だけがサポートされます。」というエラーになってしまいます。
そこで、SqlMethods.Likeを使ってみました。

using System;
using System.Linq;
using System.Data.Linq.SqlClient;

namespace LINQ4
{
    class Program
    {
        static void Main(string[] args)
        {
            LINQTESTDataContext dtc = new LINQTESTDataContext();
            dtc.Log = Console.Out;

            int? ageCheck = null;
            int? genderCheck = null;
            string namePart = null;

            var query = from p in dtc.People
                        where p.Age >= (ageCheck ?? 0)
                              && p.Gender == (genderCheck ?? p.Gender)
                              && SqlMethods.Like(p.Name, "%" + (namePart ?? p.Name) + "%")
                        select p;

            foreach (var item in query)
            {
                Console.WriteLine("名前={0}, 年齢={1}", item.Name, item.Age);
            }

            Console.Read();
        }
    }
}

string型に対しても ?? 演算子は有効です。
上記のプログラムでは結果として全員が抽出されます。

 image_6

namePart変数に"タカ"という文字列を設定すると、名前の一部に"タカ"を含む人だけが抽出されます。

 image_7

これで年齢、性別、名前に対して指定した条件を組み合わせてデータを抽出するLINQを完成することができました。
実際にいろいろな条件を設定して動作を試してみてください。

#ある意味、パラメータを利用したSQLクエリの書き方の勉強にもなっているような気がする。。。

カテゴリー:.NET, LINQを楽しむ