by WebSurfer
30. May 2014 16:37
SQL Server の接続文字列で Initial Catalog(データベース名)の指定は必要かという話を書きます。
MSDN ライブラリなどでは、SQL Server の既定のインスタンスまたは名前つきインスタンスに接続する場合の接続文字列(正確には SqlClient 接続文字列)の例として、Initail Catalog(もしくは Database)キーワードでデータベース名を指定するように書かれています。
具体例は下記のページを見てください。
Initial Catalog(もしくは Database)のない接続文字列の例は、自分が知る限りですが、見たことがないです。
なので、接続文字列にデータベース名の指定は必須かと思い込んでいましたが、ある機会に試してみたところ、そんなことはなかったです。
以下の条件が満足されれば、任意のデータベースからデータを取得できます。
-
SQL Server の既定のインスタンスまたは名前つきインスタンスが対象。(ユーザーインスタンスとか LacalDB など、.mdf ファイルを動的にアタッチするようなケースはダメです)
-
同一インスタンス内にある、静的にアタッチ済みのデータベースを対象とする。
-
接続文字列で指定したログイン権限で SQL Server による認証が通ってログインできること。
-
接続文字列にデータベース名の指定がない場合、もしくは指定されていてもそれとは異なるデータベースを対象とする場合は、データベース名を特定できるクエリを記述する。例えば、 [データベース名].[スキーマ名].[テーブル名] というように。
-
ログインしたユーザーが、目的のデータベースに対して必要な権限を持っている(すなわち、SQL Server によるユーザー承認が通る)。具体的には、先の記事 ユーザー権限の設定 に書いたような設定がしてあることが条件です。
ADO.NET のコードを使って説明した方が分かりやすいと思いますので、以下にサンプルをアップしておきます。説明はコメントに書きましたので見てください。
using System;
using System.Data.SqlClient;
class Program
{
static void Main(string[] args)
{
// Initial Catalog=PUBS でも NORTHWIND から取得できる。
// Initial Catalog の指定なしでも OK。下はその例。
// ただしクエリに DB 名の指定が必要。下の例を参照。
// Integrated Security=True まで省くとログインできない。
string connString1 =
"Data Source=tcp:papiko-pc;Integrated Security=True";
string query1 =
"SELECT CategoryID, CategoryName " +
"FROM [NORTHWIND].[dbo].[Categories];" +
"SELECT EmployeeID, LastName " +
"FROM [NORTHWIND].[dbo].[Employees]";
RetrieveMultipleResults(new SqlConnection(connString1), query1);
// Initial Catalog を指定すればクエリで DB 名の指定は不要(下
// の例では NORTHWIND に含まれる Categories テーブル)。
// ただし、titleauthor テーブルは PUBS に含まれるものなので、
// 下の例のようにクエリに DB 名の指定が必要。
string connString2 =
"Data Source=tcp:papiko-pc;Initial Catalog=NORTHWIND;" +
"Integrated Security=True";
string query2 =
"SELECT CategoryID, CategoryName FROM Categories;" +
"SELECT royaltyper, au_id FROM [PUBS].[dbo].[titleauthor]";
RetrieveMultipleResults(new SqlConnection(connString2), query2);
}
// MSDN ライブラリ「DataReader によるデータの取得」
// http://msdn.microsoft.com/ja-jp/library/haa3afyz.aspx
// NextResult による複数の結果セットの取得
static void RetrieveMultipleResults(
SqlConnection connection, string query)
{
using (connection)
{
SqlCommand command = new SqlCommand(query, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
while (reader.HasRows)
{
Console.WriteLine("\t{0}\t{1}", reader.GetName(0),
reader.GetName(1));
while (reader.Read())
{
Console.WriteLine("\t{0}\t{1}",
reader.GetInt32(0), reader.GetString(1));
}
// バッチ Transact-SQL ステートメントの結果を読み込む
// ときに、データリーダーを次の結果に進めます。
reader.NextResult();
}
}
}
}
よく分からないのは、上記のようなことができるのに、何のためにデータベース名を接続文字列に指定するかということです。
クエリを書くときにデータベース名を省略できるぐらいのメリットしか思い浮かびません。他に、コードファーストを利用する際 Initial Catalog に指定された名前でデータベースを作ってくれるということもあるそうですが。でも、そんな理由ではなさそうです。
接続プールが複数作られることになるのも無駄なような気がします。(Initial Catalog が違えばもちろん、キーワードを異なる順序で指定しただけでも接続プールは異なるとのこと。詳しくは MSDN ライブラリ SQL Server の接続プール (ADO.NET) を参照)
上のサンプルの結果からわかるように、接続プールは接続文字列で指定されるインスタンスに接続するためにあるだけで、インスタンス中にあるどのデータベースに接続するかは関知しないです。
それなのに、わざわざ接続文字列にデータベースを指定して、複数の接続プールを作る理由が分かりません。
そもそも、同じ接続プールで複数の異なるデータベースに接続するなどということはやってはいけないことで、好ましからざる副作用があるのでしょうか?