WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

SQL Server の数値型を LIKE 句を使ってあいまい検索

by WebSurfer 19. June 2021 12:55

SQL Server の数値型は、文字列型と同様に、そのまま直接 LIKE 句を使ってあいまい検索ができるようです。下の画像を見てください。UnitPrice 列は money 型ですが、[UnitPrice] LIKE '%2%' という条件が有効になっています。

SSMS での検索結果

知ってました? 実は何を隠そう自分は最近まで知らなかったです。(汗) 数字型はまず文字列型に変換してから、それに LIKE 句を使うものだと思ってました。

調べてみると、Microsoft のドキュメント「LIKE (Transact-SQL)」に、

"引数が文字列データ型でない場合、SQL Server データベース エンジン は可能であれば引数を文字列データ型に変換します。 If any one of the arguments isn't of character string data type, the SQL Server Database Engine converts it to character string data type, if it's possible."

・・・と書いてあります。実際に試してみると、上の画像のように money 型の UnitPrice 列も LIKE 句を使ってあいまい検索ができました。

Microsoft のドキュメントが言う「可能であれば」がどこまでの範囲か調べ切れていませんが、自分が SQL Server 2012 で試した限りでは int 型と money 型は可能な範囲に入るようです。

ADO.NET + SqlClient を使った .NET Framework のアプリケーションでも同じことができます。パラメータ化も可能です。ただし、パラメータ化する場合は、パラメータは文字列型として扱う必要がありますが。

上の画像と同様な LIKE 句を使って検索を行う .NET Framework コンソールアプリのサンプルコードを以下に載せておきます。ADO.NET + SqlClient を使い、SQL 文はパラメータ化しています。

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;

namespace ConsoleAppLIKE
{
    public class Product
    {
        public int ProductID { get; set; }

        public string ProductName { set; get; }

        // UnitPrice 列は NULL 可なので Nullable とした
        public decimal? UnitPrice { set; get; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            string connString = "接続文字列";
            string selectQuery =
                "SELECT [ProductID] ,[ProductName] ,[UnitPrice] FROM [Products] " +
                "WHERE [ProductName] LIKE N'%' + @ProductName + N'%' AND " +
                "[UnitPrice] LIKE N'%' + @UnitPrice + N'%'";

            string productName = "ch";
            string unitPrice = "2";            
            List<Product> productList = new List<Product>();

            using (var connection = new SqlConnection(connString))
            {
                using (var command = new SqlCommand(selectQuery, connection))
                {
                    var p1 = new SqlParameter("@ProductName", SqlDbType.NVarChar);
                    p1.Value = productName;

                    // UnitPrice 列は money 型だが LIKE 句を使ってあいまい検索
                    // する場合はパラメータの型は文字列とする
                    var p2 = new SqlParameter("@UnitPrice", SqlDbType.NVarChar);
                    p2.Value = unitPrice;

                    command.Parameters.Add(p1);
                    command.Parameters.Add(p2);

                    connection.Open();
                    using (var reader = command.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            var product = new Product
                            {
                                ProductID = reader.GetInt32(0),
                                ProductName = reader.GetString(1),

                                // UnitPrice 列は NULL 可なのでその対応
                                UnitPrice = reader.IsDBNull(2) ?
                                            null : (decimal?)reader.GetDecimal(2)
                            };

                            productList.Add(product);
                        }
                    }
                }
            }

            foreach (Product p in productList)
            {
                Console.WriteLine($"PriductID: {p.ProductID}, " +
                    $"ProductName: {p.ProductName}, UnitPrice: {p.UnitPrice}");
            }
        }
    }
}

サンプルコード中のコメントにも書きましたが、UnitPrice 列は money 型ですが LIKE 句を使ってあいまい検索する場合はパラメータの型は文字列とする必要がありますので注意してください (例えば、SqlDbType.NVarChar を SqlDbType.Deciaml にするとエラーになります)。

上のコードの実行結果は以下の通りで、上の画像の SSMS での実行結果と同じになっています。

ADO.NET での検索結果

Tags: , , ,

SQL Server

Employees の Photo

by WebSurfer 4. June 2021 14:51

Microsoft のサンプル SQL Server データベース Northwind の Employees テーブルの Photo フィールドの画像データを取得して表示する方法を書きます。

Employees の Photo

アプリから Photo フィールドのバイト列データを取得するのは ADO.NET + SqlClient を使って可能です。問題はそのバイト列を加工して jpg とか png 形式にすることで、それをどうしたらよいかという話です。

Photo フィールドの画像データは Bitmap 形式のバイト列なのですが、ネットの記事 Displaying images in SAPUI5 received from the Northwind OData service によると "The 78 superfluous bytes are a proprietary OLE header that Access creates when saving bitmaps." という余計なものが付いています。(Microsoft のドキュメントもそのような情報があったのですが行方不明)

実際、Photo フィールドのバイト列を取得してそれをそのまま stream に変換し、new Bitmap(stream) で Bitmap オブジェクトを取得しようとすると ArgumentException がスローされます。下のサンプルコードでコメントアウトした部分を見てください。

ということで、Photo フィールドのバイト列を stream に変換するとき最初の 78 バイトをスキップしてやる必要があります。それは MemoryStream(Byte[], Int32, Int32) コンストラクタを使って可能です。

以下にそのサンプルコードを載せておきます。上の画像の Photo を表示するのに使った HTTP ジェネリックハンドラで、Employees テーブルの Photo データを取得して、最初の 78 バイトをスキップして MemoryStream を作り、new Bitmap(stream) で Bitmap オブジェクトを取得し、Bitmap.Save メソッドで jpg 形式にして HTTP 応答ストリームに書き込んでいます。

using System.Web;
using System.Web.Configuration;
using System.Data.SqlClient;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;

namespace WebApplication1
{
    public class EmployeePhotoHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            string id = context.Request.QueryString["id"];
            context.Response.ContentType = "image/jpeg";
            byte[] phote;
            int employeeId;
            if (!string.IsNullOrEmpty(id) && int.TryParse(id, out employeeId))
            {
                var connString = WebConfigurationManager
                    .ConnectionStrings["NORTHWINDConnectionString"]
                    .ConnectionString;
                var query = "SELECT photo FROM Employees WHERE EmployeeID = @ID";
                using (var connection = new SqlConnection(connString))
                {
                    using (var command = new SqlCommand(query, connection))
                    {
                        command.Parameters.AddWithValue("@ID", employeeId);
                        connection.Open();
                        phote = (byte[])command.ExecuteScalar();
                    }
                }

                // これはダメ
                // new Bitmap(stream) で ArgumentException がスローされる
                // "使用されたパラメーターが有効ではありません。"
                //using (var stream = new MemoryStream(phote))
                //{
                //    using (var bitmap = new Bitmap(stream))
                //    {
                //        bitmap.Save(context.Response.OutputStream, ImageFormat.Jpeg);
                //    }
                //}

                // これは OK
                // The 78 superfluous bytes are a proprietary OLE header that Access
                // creates when saving bitmaps. ・・・ということで最初の 78 バイトは
                // スキップしないと有効な Bitmap 形式とはみなされないらしい
                using (var stream = new MemoryStream(phote, 78, phote.Length - 78))
                {
                    using (var bitmap = new Bitmap(stream))
                    {
                        bitmap.Save(context.Response.OutputStream, ImageFormat.Jpeg);
                    }
                }
            }
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

Tags: , ,

SQL Server

LocalDB の照合順序

by WebSurfer 24. September 2020 11:28

Microsoft LocalDB の照合順序はデフォルトで SQL_Latin1_General_CP1_CI_AS になるそうです。今更ながらですが調べてみたらそうなってました。

LocalDB の照合順序

Microsoft の Blog の記事「[SQL Azure] Unicode型列(NCHAR/NVARCHAR) に格納されるデータが “?” になる」によると、SQL Azure も同じだそうです。

なので、その記事にも書いてありますが、以下のような N プレフィックスを使わない SQL では文字化けします。

INSERT INTO [Table] ([Name]) VALUES ('あいうえお')

知ってました? 何をいまさらと言われそうですけど。(汗)

SQL 文をパラメータ化し ADO.NET + SqlClient を利用して INSERT, UPDATE 操作を行えば、内部的に N プレフィックスが付与された SQL 文に変換されて実行され、文字化けの問題は起こらないので、気が付かないかもしれません。(詳しくは先の記事「パラメータ化の副次的な効用」参照)

Visual Studio を使って ASP.NET プロジェクトを作る際[個別のユーザーアカウント]を選ぶと、ASP.NET Identity のユーザー情報のストアはデフォルトで LocalDB を使う設定となり、EF Code First で生成されるデータベースの照合順序も同じく SQL_Latin1_General_CP1_CI_AS になります。

自分でモデルを定義してそれをベースに EF Code First でデータベースを生成する場合も、接続文字列で LocalDB に接続する設定がされていると当然 LocalDB にデータベースが生成されますが、同様に照合順序は SQL_Latin1_General_CP1_CI_AS になります。

ASP.NET MVC アプリでは Linq to Entities を使って DbContext クラスを継承したコンテキストクラスを操作してデータベースとのやり取りを行うケースが多いですが、そういう場合も内部的に N プレフィックスが付与された SQL 文に変換されて実行されるようで、文字化けの問題は起こらなかったです。

Tags: ,

SQL Server

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  July 2021  >>
MoTuWeThFrSaSu
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

View posts in large calendar