Skip to content

第17章 C#からデータベースを操作する

この章では、C# のコンソールアプリから データベース(DB) を操作する基本を学習します。

ここまでの章で、C# の基本文法・クラス・オブジェクト指向・例外処理を学びました。 ここからは、プログラムからデータベースを操作する ことに踏み込みます。

研修のメインで使うデータベースは SQLServer 2022 です。 ただし、SQL 研修で扱った Oracle Database と接続のしくみは共通しているため、まず Oracle 接続の例を「参考」として軽く見ておき、その後 SQLServer に本格的に接続します。

SQL 研修で学んだ SQL
コンソールアプリで DB 接続を確認 ← この章
Windowsフォームで DB 利用(第23〜25章)
Webアプリ (ASP.NET Core MVC) で DB 利用(第27〜30章)

いきなり Windows フォームや Web アプリで DB 接続を扱うと、画面まわりと DB 接続まわりが混ざって考えることが増えます。 そのため、まずはコンソールアプリで DB 接続の基本を確認します。


第 17 章からは、データベースを使った 実践フェーズ に入ります。ここから先(本章や、第 23〜25 章の社員管理アプリなど)は、チームで助け合いながら進める ことを基本とします。

各チームで次の 3 つの役割を割り当てます。一人が複数を兼ねても構いませんが、全員がいずれかの責任を持ちます

役割主な責任
リーダープロジェクト全体の進行管理。メンバーの進み具合を把握し、詰まっている人がいないか気を配る
技術部長技術面の相談役。エラーや分からない箇所をまずチーム内で受け止め、解決を図る(解決しなければ講師へ)
タイムキーパー時間管理。区切りや発表の時刻から逆算し、「残りあと何分」をチームに共有して時間を守る

自走のすすめ

ここから先は、本文を手順書として読みながら 自分たちのペースで 進めます。分からないことは、まずペア・チーム内で「ここどうなってる?」と声に出して確認し合ってください。 それでも解決しないときは、技術部長を通じて講師に質問します。「自分たちで調べて・確認して・それでも分からなければ聞く」という現場の動き方を、ここから練習します。

なお、この「自走」は 本文の学習やチームでの助け合いの進め方 を指します。本章の章末演習だけはタイマー方式で計測します(詳しくは「演習課題」を参照)。第 23 章以降のアプリ開発では、タイマーをやめて完成・発表まで自走で進めます。

役割は章をまたいで使います

この役割分担は、本章以降のチーム作業で共通して使います(章ごとに役割を交代しても構いません)。講師の指示でチームを組み、役割を決めてから進めましょう。


この章でできるようになること

Section titled “この章でできるようになること”

この章を終えると、次のことができるようになります。

  • ADO.NET の役割を説明できる
  • C# から SQLServer に接続する流れを説明できる
  • NuGet で Microsoft.Data.SqlClient を追加できる
  • 接続文字列(Connection String)の役割を説明できる
  • SqlConnection で接続を開ける
  • SqlCommand で SQL を実行できる
  • ExecuteScalar で 1 つの値(件数・合計など)を取得できる
  • using 宣言で接続を自動的に閉じられる
  • SqlException を含む例外処理を書ける
  • 接続エラー時に確認すべき点を列挙できる
  • Oracle と SQLServer の違いを概観できる

項目内容
開発環境Visual Studio 2022
プロジェクト種類コンソール アプリ
対象フレームワーク.NET 8
ソリューション名Chapter17
プロジェクト名Ch17_DbOperation
データベースSQLServer 2022(TrainingDB)
認証方式Windows 統合認証
使用するテーブルemployeesdepartments
NuGet パッケージMicrosoft.Data.SqlClient

csproj の Nullable は disable に変更してください

プロジェクト作成後、Ch17_DbOperation.csproj を開き、<Nullable>disable</Nullable> に変更してください。詳しい手順は、第 1 章「1-1 プロジェクトを作成する」を参照してください。


この章は 「読む」節と「実施する」節 が混在します。下の表の通りに進めてください。

内容進め方
17-1ADO.NET とは読む
17-2参考:Oracle に接続する場合読む(興味があれば実行可)
17-3SQLServer に接続する準備実施
17-4接続文字列実施
17-5接続から件数取得までの完全コード実施
17-6各部分の解説読む
17-7プログラムの値を SQL に渡す(パラメータ)実施
17-8接続エラー時の確認手順読む
17-9Oracle と SQLServer の違い読む

作業を始める前に、次の内容を確認してください。

  • 第 15 章までを Git に提出済みである
  • SQLServer 2022 が起動している(サービスとして動作)
  • SSMS(SQL Server Management Studio)で TrainingDB データベースに接続できる
  • TrainingDBemployeesdepartments テーブルが作成済みで、サンプルデータが入っている
  • TrainingDBproductsordersorder_items テーブルも作成済みである(発展課題 17-5・17-6 で使用)

環境の準備について

前章の 第 16 章「SQLServer 環境構築」 で作成した TrainingDB に対して接続します。 まだ構築していない場合は、先に第 16 章(必要に応じて 付録 G「SQLServer 2022 / SSMS インストール手順」)に取り組んでください。 第 16 章で社員系(employees / departments)と商品・注文系(products / orders / order_items)の両方を作成していれば、本章の全課題に取り組めます。


ADO.NET(エイドー・ドット・ネット)は、.NET から各種データベースを操作するための仕組みの総称です。

ADO.NET は、データベースの違いを吸収するために、次のような 共通の役割 を定義しています。

役割共通名SQLServer の場合Oracle の場合
接続IDbConnectionSqlConnectionOracleConnection
SQL 実行IDbCommandSqlCommandOracleCommand
結果読み取りIDataReaderSqlDataReaderOracleDataReader
例外(各 DB 固有)SqlExceptionOracleException

つまり、「接続して、SQL を投げて、結果を受け取り、後始末する」 という流れはどの DB でも同じで、クラス名と接続文字列だけが DB ごとに違います。

この共通構造を意識しておくと、SQLServer を学んだ後に Oracle や PostgreSQL に切り替えても、同じパターンで書けるようになります。


17-2 参考:Oracle に接続する場合

Section titled “17-2 参考:Oracle に接続する場合”

この節は「読むだけ」が基本 です。SQL 研修で Oracle を扱った経験を C# に橋渡しする位置づけで、興味があれば自分の SQL 研修環境で実行してみてください。

Oracle に接続する場合は、NuGet で Oracle.ManagedDataAccess.Core を追加し、次のようなコードを書きます。

Program.cs
using Oracle.ManagedDataAccess.Client;
namespace Ch17_DbOperation;
internal class Program
{
static void Main(string[] args)
{
const string connectionString =
"User Id=pingt;Password=oracle;Data Source=localhost:1521/XEPDB1;";
try
{
using OracleConnection connection = new OracleConnection(connectionString);
connection.Open();
string sql = "SELECT COUNT(*) FROM employees";
using OracleCommand command = new OracleCommand(sql, connection);
int count = Convert.ToInt32(command.ExecuteScalar());
Console.WriteLine($"employees 表の件数:{count}");
}
catch (OracleException ex)
{
Console.WriteLine($"Oracle エラー({ex.Number}):{ex.Message}");
}
}
}

クラス名(OracleConnectionOracleCommandOracleException)と接続文字列の書き方こそ違いますが、接続を開き、コマンドを作り、ExecuteScalar で値を 1 つ取得し、using 宣言 で後始末する という構造は、これから学ぶ SQLServer 版と完全に同じです。

次の節からは、研修のメイン環境である SQLServer に接続していきます。


新しいコンソールアプリ Ch17_DbOperation を作成してください(ソリューション名は Chapter17)。 作成後、Ch17_DbOperation.csproj を開き、<Nullable>disable</Nullable> に変更します。

C# から SQLServer に接続するには、Microsoft.Data.SqlClient という NuGet パッケージが必要です。

Microsoft.Data.SqlClientSystem.Data.SqlClient

古い書籍やサンプルでは System.Data.SqlClient が使われていることがあります。これは .NET Framework 時代からあるもので、現在はメンテナンスのみです。 .NET 8(現在の .NET)では、Microsoft.Data.SqlClient が推奨パッケージです。本研修もこちらを使います。

  1. ソリューションエクスプローラーで、プロジェクト Ch17_DbOperation を右クリック
  2. NuGet パッケージの管理 を選択
  3. 参照 タブで Microsoft.Data.SqlClient を検索
  4. 検索結果から Microsoft.Data.SqlClient(発行元 Microsoft)を選択
  5. 右側で インストール をクリック
  6. ライセンスの同意ダイアログで 同意する をクリック

図17-1 NuGet パッケージマネージャーで Microsoft.Data.SqlClient を追加する画面

インストールが終わると、Ch17_DbOperation.csproj に次のような行が追加されます。

<ItemGroup>
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.x.x" />
</ItemGroup>

これで、SqlConnectionSqlCommand などのクラスがコードから使えるようになりました。


接続文字列 は、どの SQLServer の、どのデータベースに、どの認証方式で接続するかを示す文字列です。

本研修で使う接続文字列は次のとおりです。

Server=localhost;Database=TrainingDB;Integrated Security=true;TrustServerCertificate=true;

各項目の意味は次のとおりです。

項目意味本研修での値
Server接続先のサーバー名(ホスト名・インスタンス名)localhost
Database接続先のデータベース名TrainingDB
Integrated SecurityWindows 統合認証を使うかどうかtrue
TrustServerCertificateサーバー証明書の検証を信頼するかtrue

Windows 統合認証とは

Windows にログインしているユーザーで、そのまま SQLServer に接続する仕組みです。 ユーザー名やパスワードを接続文字列に書かなくて済むため、ローカル開発・社内ネットワークで広く使われています。

一方、Web アプリ(第 27〜30 章)では、Windows ユーザーではなく「アプリ専用の SQL ユーザー」で接続する SQL 認証 を使います。

TrustServerCertificate=true の理由

SQLServer 2022 既定の通信は暗号化されており、サーバー証明書の検証を行います。 ローカル環境では自己署名の証明書のため、TrustServerCertificate=true を付けて検証をスキップします。本番環境では正規の証明書を使い、この設定は外します。

名前付きインスタンスの場合(Server の値の読み替え)

Section titled “名前付きインスタンスの場合(Server の値の読み替え)”

SQLServer のインストール方法によっては、サーバー名が localhost だけでなく localhost\インスタンス名 になります。 SSMS で接続できているサーバー名(接続ダイアログの「サーバー名」)が、たとえば localhost\SQLEXPRESS01 のように \(円マーク区切り) を含む場合は、Server の値もそれに合わせます。

SSMS でのサーバー名C# の Server に書く値(本来の表記)
localhost(既定インスタンス)Server=localhost
localhost\SQLEXPRESS01(名前付きインスタンス)Server=localhost\SQLEXPRESS01

ここで C# のコードに書くときだけ注意 が必要です。

C# の文字列では \\\ と二重に書く

C# の通常の文字列リテラル("...")では、\特殊な意味を持つ記号(エスケープ文字)です。そのため、サーバー名に含まれる \ 1 つを表すには、コード上で \\ と 2 つ書く 必要があります。

// サーバー名そのものは localhost\SQLEXPRESS01(\ は 1 つ)
// C# のコードでは \ を \\ と書く
const string connectionString =
"Server=localhost\\SQLEXPRESS01;Database=TrainingDB;Integrated Security=true;TrustServerCertificate=true;";

二重に見えますが、実行時には \ 1 つの localhost\SQLEXPRESS01 として扱われます。本来のサーバー名の \ は 1 つで、\\ は「C# のコードでの書き方」だと理解してください。

なお、日本語環境のエディタやフォントでは、\(バックスラッシュ)が ¥(円マーク) で表示されることがあります。¥¥\\ のように見えても、意味は同じ「区切りの記号 1 つ」です。

\ を 1 つしか書かないと、\S などが不正なエスケープと見なされてビルドエラーになります。localhost の既定インスタンスを使う場合は \ 自体が出てこないので、この心配はありません。


17-5 接続から件数取得までの完全コード

Section titled “17-5 接続から件数取得までの完全コード”

ここまでの内容を組み合わせて、employees テーブルの件数を取得するプログラムを書きます。

Program.cs を次の内容にしてください。

Program.cs
using Microsoft.Data.SqlClient;
namespace Ch17_DbOperation;
internal class Program
{
static void Main(string[] args)
{
const string connectionString =
"Server=localhost;Database=TrainingDB;Integrated Security=true;TrustServerCertificate=true;";
try
{
using SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
Console.WriteLine("SQLServer への接続に成功しました。");
string sql = "SELECT COUNT(*) FROM employees";
using SqlCommand command = new SqlCommand(sql, connection);
object result = command.ExecuteScalar();
int count = Convert.ToInt32(result);
Console.WriteLine($"employees テーブルの件数:{count}");
}
catch (SqlException ex)
{
Console.WriteLine("SQLServer 関連のエラーが発生しました。");
Console.WriteLine($"エラー番号:{ex.Number}");
Console.WriteLine($"メッセージ:{ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine("予期しないエラーが発生しました。");
Console.WriteLine($"内容:{ex.Message}");
}
}
}

実行結果:

SQLServer への接続に成功しました。
employees テーブルの件数:10件

(件数は TrainingDB の標準サンプルデータの場合の例です。)

これが、C# から SQLServer を扱う 最も基本的なパターン です。


上のコードを 1 つずつ見ていきます。

using Microsoft.Data.SqlClient;

これがないと、SqlConnectionSqlCommand などのクラスがコード上で見つかりません。 NuGet パッケージを追加していないと、この行で赤波線エラーになります。

第 7 章までの章では using System; などを書いていませんでしたが、これは .NET 8 の 暗黙的 using(ImplicitUsings)で System 名前空間が自動的に取り込まれているためです。 Microsoft.Data.SqlClient は暗黙的 using の対象外なので、自分で書く必要があります。


using SqlConnection connection = new SqlConnection(connectionString);
connection.Open();

SqlConnection のオブジェクトを new した時点では、まだ接続は開かれていませんOpen() を呼ぶことで、実際に SQLServer への接続が確立されます。

接続文字列が間違っていたり、SQLServer サービスが起動していなかったりすると、Open() の時点で例外が発生します。


using SqlConnection connection = new SqlConnection(connectionString);

行頭の using に注目してください。 これは、ファイル先頭の using Microsoft.Data.SqlClient;(using ディレクティブ)とは別の機能で、using 宣言 と呼ばれます。

種類書き方役割
using ディレクティブファイル先頭の using Microsoft.Data.SqlClient;名前空間を取り込んで、SqlConnection などの短い名前で書けるようにする
using 宣言変数宣言の前に付ける using SqlConnection conn = ...;スコープを抜けるときに自動的に後始末(Dispose)を呼ぶ

データベース接続は OS の限られたリソースを使うため、使い終わったら必ず閉じる 必要があります。 using 宣言 を使うと、例外が出ても確実に閉じられるので安全です。

第 9 章で学んだ using との関係

第 9 章で出てきた using は、変数宣言の前に付ける using 宣言 の形でした。今回も同じ仕組みで、対象が SqlConnection / SqlCommand になっただけです。


string sql = "SELECT COUNT(*) FROM employees";
using SqlCommand command = new SqlCommand(sql, connection);

SqlCommand は、「この接続で、この SQL を実行する」という命令を表すクラスです。 コンストラクタには、実行する SQL 文字列と、使う接続オブジェクトを渡します。


object result = command.ExecuteScalar();
int count = Convert.ToInt32(result);

ExecuteScalar は、結果の 1 行目 1 列目の値だけ を取得するメソッドです。 COUNT(*)SUMAVGMAX など、結果が 1 つの値になる SQL に向いています。

戻り値は object 型なので、必要な型に変換します。COUNT(*) の結果なら int です。

SQL の例戻り値の型(変換先)用途
SELECT COUNT(*) FROM ...int件数
SELECT SUM(salary) FROM ...decimal合計(salary が DECIMAL のため)
SELECT AVG(salary) FROM ...decimal平均
SELECT MAX(hire_date) FROM ...DateTime最大日付

複数行を取得したい場合は、SqlDataReader という別のクラスを使います。それは第 23 章で扱います。


catch (SqlException ex)
{
Console.WriteLine($"エラー番号:{ex.Number}");
Console.WriteLine($"メッセージ:{ex.Message}");
}

SqlException は、SQLServer 関連のエラーが起きたときに投げられる例外です。 Number プロパティで SQLServer 固有のエラー番号 を取得でき、原因の特定に役立ちます。

代表的なエラー番号は次のとおりです。

エラー番号意味
4060指定したデータベースに接続できない(データベース名の typo など)
18456ログインに失敗(認証エラー)
208指定したオブジェクト(テーブル名)が見つからない
53サーバーへの接続が確立できない(サービス停止・サーバー名 typo)

catch ブロックは 特定の例外を先に、より一般的な例外を後に 書くのが原則です。 このコードでは、SqlException を先に、Exception を後に書いています。


17-7 プログラムの値を SQL に渡す(パラメータ)

Section titled “17-7 プログラムの値を SQL に渡す(パラメータ)”

ここまでの SQL は、SELECT COUNT(*) FROM employees のように 固定 でした。 しかし実際のプログラムでは、画面の入力値や変数の値を条件に使って SQL を実行したい場面がほとんどです。

SQL 研修では SQL を「直接書いて実行」してきましたが、プログラムからは「変数の値を SQL に流し込んで実行」します。ここが、プログラムから DB を扱うときの肝です。

たとえば「指定した部署 ID の社員数を数える」を考えます。 部署 ID は、プログラムの変数(departmentId)で決まるとします。

やってはいけない書き方:文字列で連結する

Section titled “やってはいけない書き方:文字列で連結する”

まず、やってはいけない例 を見ます。変数を文字列連結で SQL に埋め込む書き方です。

int departmentId = 1;
// ❌ 悪い例:変数を文字列でつなぐ
string sql = "SELECT COUNT(*) FROM employees WHERE department_id = " + departmentId;

これは動いてしまいますが、2 つの問題 があります。

  • 値が文字列(名前など)の場合、クォートのエスケープが大変で、壊れやすい
  • 悪意ある入力で SQL を改ざんされる SQL インジェクション の危険がある(詳しくは第 29 章で扱います)

正しい書き方:パラメータ(プレースホルダー)を使う

Section titled “正しい書き方:パラメータ(プレースホルダー)を使う”

正しくは、SQL の中に @名前 という プレースホルダー(場所取り)を書いておき、実際の値は後から パラメータ として渡します。

Program.cs
using Microsoft.Data.SqlClient;
namespace Ch17_DbOperation;
internal class Program
{
static void Main(string[] args)
{
const string connectionString =
"Server=localhost;Database=TrainingDB;Integrated Security=true;TrustServerCertificate=true;";
int departmentId = 1; // プログラムで決まる値(画面入力などを想定)
try
{
using SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
// SQL の中は @departmentId という「場所取り」にしておく
string sql = "SELECT COUNT(*) FROM employees WHERE department_id = @departmentId";
using SqlCommand command = new SqlCommand(sql, connection);
// 場所取りに、実際の値を割り当てる
command.Parameters.AddWithValue("@departmentId", departmentId);
int count = Convert.ToInt32(command.ExecuteScalar());
Console.WriteLine($"部署 {departmentId} の社員数:{count}");
}
catch (SqlException ex)
{
Console.WriteLine($"エラー番号:{ex.Number} / {ex.Message}");
}
}
}

実行結果(departmentId = 1 の場合):

部署 1 の社員数:4人

サンプルデータでは、部署 1(総務)に山田二郎・佐々木明子・菊池浩二・中山大輔の 4 人が所属しています。departmentId の値を 23 に変えると、結果も変わります。

手順書き方
① SQL に場所取りを書く... WHERE department_id = @departmentId
② 場所取りに値を割り当てるcommand.Parameters.AddWithValue("@departmentId", departmentId);
③ 実行するcommand.ExecuteScalar()(やることは今までと同じ)

ポイントは、SQL 本体と値を分けて渡す ことです。 こうすると、値がどんな文字列でも安全に扱われ(SQL として解釈されない)、SQL インジェクションも防げます。

@departmentId は SQL 文の中ではただの目印

@departmentId は C# の変数ではありません。SQL 文の中の「ここに値が入る」という目印で、AddWithValue の第 1 引数の名前("@departmentId")と一致させることで結び付きます。

このパラメータの渡し方は、第 24 章(Windows フォームの検索)や第 29 章(Web の検索)でも、検索条件を SQL に渡すときにそのまま使います。 「変数の値を SQL に渡すときは、文字列連結ではなくパラメータ」 と覚えておいてください。


Open() の時点で例外が出るときは、次の手順で確認します。

  1. SQLServer サービスが起動しているか
    • Windows の「サービス」アプリを開き、SQL Server (MSSQLSERVER) が「実行中」であることを確認します。
  2. SSMS で同じ接続文字列の条件で接続できるか
    • SSMS で Server=localhost、認証=Windows 認証 で接続できれば、サーバー側は問題なし。
  3. データベース名が正しいか
    • 接続文字列の Database=TrainingDB の綴り、SSMS のデータベース一覧と一致しているか。
  4. 認証方式が正しいか
    • SQL 認証で接続したい場合は Integrated Security=true; ではなく、User Id=...;Password=...; を指定します。
  5. エラー番号で具体例を確認する
    • SqlException.Number を見て、上の表と照らし合わせます。

接続できないときの最初の一手

エラーメッセージを そのままコピーして検索 するのが、最も早い解決方法です。エラー番号は世界中の人が踏んでおり、たいてい同じ症状の解決例が見つかります。


接続のしくみは共通でも、表記や挙動には違いがあります。代表的なものを表で示します。

観点OracleSQLServer
接続クラスOracleConnectionSqlConnection
コマンドクラスOracleCommandSqlCommand
例外クラスOracleExceptionSqlException
接続文字列の例User Id=pingt;Password=oracle;Data Source=localhost:1521/XEPDB1;Server=localhost;Database=TrainingDB;Integrated Security=true;TrustServerCertificate=true;
文字列リテラルの引用符シングルクォート '山田'シングルクォート '山田'
識別子の引用符ダブルクォート "EMP"角かっこ [EMP] または "EMP"
文字列の型VARCHAR2NVARCHAR2VARCHARNVARCHAR
数値の自動採番シーケンス + トリガーIDENTITY
接続先の単位サービス名 / SIDデータベース名

SQL の文法は大筋共通ですが、識別子の囲み方・データ型名・自動採番のしくみ は差異が大きい部分です。 SQL 研修と本章の SQLServer を見比べると、SQL の方言の感覚がつかめます。

SQL 研修(Oracle)で身に付けた SQL は、SQLServer でもほとんどそのまま通じますが、いくつかの「方言」 があります。本章や、これ以降の演習でつまずきやすいものを挙げます。

やりたいことOracle(SQL 研修)SQLServer(本研修)
文字列を連結するlast_name || first_namelast_name + first_name
NULL を別の値に置き換えるNVL(salary, 0)ISNULL(salary, 0) または COALESCE(salary, 0)
現在日時を取得するSYSDATEGETDATE()
先頭 N 件だけ取得するFETCH FIRST 5 ROWS ONLY / ROWNUMSELECT TOP 5 ...
ダミー表から SELECT するSELECT 1 FROM DUALSELECT 1(DUAL は不要)
文字列の一部を取り出すSUBSTR(...)SUBSTRING(...)

まずはこれだけ覚えておく

演習で最初に出会うのは 文字列連結(||+) です。SQL 研修の感覚で || と書くと SQLServer ではエラーになるので注意してください。 なお、COALESCE は Oracle・SQLServer 共通で使えるので、NULL 置換は迷ったら COALESCE が安全です。COUNT / SUM / AVG / JOIN / WHERE / GROUP BY / ORDER BY などの基本構文は両者で共通です。


症状原因対処
SqlConnection が赤波線になるNuGet パッケージ未追加Microsoft.Data.SqlClient を NuGet で追加
Open()SqlException (Number=53)SQLServer サービス停止、サーバー名の typoサービスの「実行中」を確認、Server=localhost の綴り確認
Open()SqlException (Number=4060)データベース名が間違っているDatabase=TrainingDB の綴り確認、SSMS のデータベース一覧と照合
Open()SqlException (Number=18456)認証エラーWindows 統合認証 / SQL 認証の指定を見直す、ユーザー名・パスワードを確認
ExecuteScalar の戻り値を int に直接代入できない戻り値は objectConvert.ToInt32(...) で変換
SqlExceptioncatch できない(using ディレクティブ 未記述)using Microsoft.Data.SqlClient; がないファイル冒頭に追加
TrustServerCertificate を付けないと接続できないサーバー証明書の検証エラーローカル開発では TrustServerCertificate=true を付ける

この章で学んだ内容をチェックしてください。

  • ADO.NET の役割を説明できる
  • Microsoft.Data.SqlClient を NuGet で追加できる
  • 接続文字列の各項目(ServerDatabaseIntegrated SecurityTrustServerCertificate)の意味を説明できる
  • SqlConnection.Open() で接続が確立されることを説明できる
  • SqlCommandExecuteScalar で 1 つの値を取得できる
  • using 宣言 で接続を自動的に閉じられる
  • プログラムの変数の値を、パラメータ(@名前 + AddWithValue)で SQL に渡せる
  • 値を文字列連結で SQL に埋め込んではいけない理由を説明できる
  • SqlExceptioncatch してエラー番号を表示できる
  • 接続エラー時の確認手順を 3 つ以上挙げられる
  • Oracle と SQLServer の違いを 3 つ以上挙げられる

ペアで以下の質問に答えてみてください。

  1. ADO.NET とは何ですか。
  2. Microsoft.Data.SqlClient は何のために必要ですか。
  3. 接続文字列の Integrated Security=true はどんな意味ですか。
  4. SqlConnectionnew しただけでは何が起きないと言えますか。
  5. using 宣言 を使うと何が自動で行われますか。
  6. ExecuteScalar の戻り値が object 型なのはなぜですか。
  7. SQL に変数の値を渡すとき、文字列連結ではなくパラメータ(@名前)を使うのはなぜですか。
  8. command.Parameters.AddWithValue("@departmentId", departmentId) は何をしていますか。
  9. SqlException.Number は何を表しますか。
  10. Oracle と SQLServer の接続クラスは、それぞれ何という名前ですか。

説明するときは、完全な答えでなくても構いません。 自分の言葉で説明しようとすることが大切です。


この章の演習課題に取り組みます。DB 接続・パラメータを実際に書く、歯ごたえのある演習です。

段階目安時間内容
① 準備10 分ペア確認 + 課題確認(評価対象外)
② ソロ作業35 分タイマーで計測。タイマー時点の commit が唯一の評価対象(NuGet 追加・接続文字列の準備があるため長めに設定)
③ チーム時間講師指定の発表開始時刻までレビュー + 発表者選出 + 実装続行(任意)。発表開始時刻は厳守

提出ルール(タイマー方式)

タイマー時点の commit が唯一の評価対象です。タイマー後の書き足しは評価されません。 コミットメッセージ形式:Chapter17 タイマー提出: <どこまで完成> / <詰まったポイント>(なければ「特になし」) 例:Chapter17 タイマー提出: 必須17-1〜17-3完成、17-4途中 / パラメータの渡し方で詰まった

提出方法:Git が使えないときはサーバへコピー

講師の指示があったときは、push の代わりに Kadai17 フォルダを提出先サーバへコピーし、コピー先に 提出メモ.txt(「どこまで完成」「詰まったポイント」を記載)を作成してください。

タイマー後のチーム時間の使い方

レビュー・発表者選出・実装続行(任意)を自由配分してください。発表開始時刻は厳守です。


課題はソリューション Kadai17 の中に作成してください。 課題ごとに別のプロジェクトを作成 し、指定されたプロジェクト名を使います。各プロジェクトで Microsoft.Data.SqlClient を NuGet で追加し、Nullable を disable にしてから始めてください。

課題必須/発展プロジェクト名作成する主なファイル
課題 17-1必須Kd17_01_HelloSqlServerProgram.cs
課題 17-2必須Kd17_02_BothCountProgram.cs
課題 17-3必須Kd17_03_DeptEmployeeCountProgram.cs
課題 17-4発展Kd17_04_AboveAverageCountProgram.cs
課題 17-5発展Kd17_05_CategoryStockValueProgram.cs
課題 17-6発展Kd17_06_OrdersOverAmountProgram.cs

フォルダ構成は次のようになります。

Kadai17/ ← 課題用ソリューションフォルダ
Kadai17.sln
Kd17_01_HelloSqlServer/
Kd17_02_BothCount/
Kd17_03_DeptEmployeeCount/
Kd17_04_AboveAverageCount/
Kd17_05_CategoryStockValue/
Kd17_06_OrdersOverAmount/

課題 17-1〜17-3 は必須、課題 17-4〜17-6 は発展です。 必須 17-3、発展 17-5・17-6 で、本文 17-7 で学んだ「パラメータ(プレースホルダー)」を使います — プログラムの変数の値を SQL に渡す練習です(発展 17-4 はサブクエリが主役でパラメータは使いません)。

接続文字列について

すべての課題で、本文 17-4 の接続文字列をそのまま使えます。 環境によっては、Server の値を読み替える必要があります(名前付きインスタンスの場合は Server=localhost\\SQLEXPRESS01 のように書きます。\\\ と二重に書く理由は 本文 17-4「名前付きインスタンスの場合」 を参照)。


まずは、全員が必須課題に取り組んでください。


課題 17-1 Hello SqlServer(employees の件数を取得)

Section titled “課題 17-1 Hello SqlServer(employees の件数を取得)”

本文 17-5 のコードを Kd17_01_HelloSqlServer プロジェクトに入力して、実際に動かしてください。 正しく接続でき、件数が表示されることを確認したら、接続文字列の一部を意図的に間違えて、エラーの動きも観察してください。

確認事項

  1. 正しい接続文字列で実行 → employees テーブルの件数:10件(サンプルデータの場合)
  2. Database=TrainingDBDatabase=NoSuchDb に変更 → SqlException が発生、Number=4060 系のメッセージ
  3. テーブル名を employeesemployeess に変更 → Number=208 が発生
  4. 確認後、接続文字列・SQL を 必ず正しい状態に戻す

実行結果例(正常):

SQLServer への接続に成功しました。
employees テーブルの件数:10件

条件:

  • using Microsoft.Data.SqlClient; を書く
  • SqlConnection + SqlCommand + ExecuteScalar を使う
  • SqlExceptionException の両方を catch する
  • using 宣言 で接続を自動クローズ

課題 17-2 employees と departments の両方の件数を取得

Section titled “課題 17-2 employees と departments の両方の件数を取得”

新しいプロジェクト Kd17_02_BothCount を作成し、employeesdepartments の両方の件数を取得して表示してください。

実行結果例:

SQLServer への接続に成功しました。
employees テーブルの件数:10件
departments テーブルの件数:5件

条件:

  • SqlCommand を 2 つ作って、順番に ExecuteScalar を呼ぶ
  • どちらも using 宣言 で書く
  • 接続(SqlConnection)は 1 つで OK
  • 例外処理は課題 17-1 と同じ構造

ヒント:

using SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
using (SqlCommand command1 = new SqlCommand("SELECT COUNT(*) FROM employees", connection))
{
int count = Convert.ToInt32(command1.ExecuteScalar());
Console.WriteLine($"employees テーブルの件数:{count}");
}
using (SqlCommand command2 = new SqlCommand("SELECT COUNT(*) FROM departments", connection))
{
int count = Convert.ToInt32(command2.ExecuteScalar());
Console.WriteLine($"departments テーブルの件数:{count}");
}

using 宣言(using SqlCommand command = ...;)の代わりに、上のような using ステートメント(using (...) { ... })も使えます。 スコープを明示したい場合に便利です。


課題 17-3 部署を指定して社員数を取得(パラメータ)

Section titled “課題 17-3 部署を指定して社員数を取得(パラメータ)”

新しいプロジェクト Kd17_03_DeptEmployeeCount を作成し、指定した部署 ID の社員数 を取得して表示してください。 本文 17-7 で学んだ パラメータ(プレースホルダー) を使います。

仕様

  • プログラムの変数 int departmentId = 1; を用意する
  • SQL:SELECT COUNT(*) FROM employees WHERE department_id = @departmentId
  • command.Parameters.AddWithValue("@departmentId", departmentId); で値を渡す
  • 結果を「部署 1 の社員数:〇人」と表示する
  • departmentId の値を 23 に変えても正しく動くことを確認する
  • 例外処理は課題 17-1 と同じ構造

実行結果例(departmentId = 1 の場合):

SQLServer への接続に成功しました。
部署 1 の社員数:4人

部署 1=4 人、部署 2=2 人、部署 3=2 人(サンプルデータの場合)。

条件:

  • 文字列連結ではなく、必ずパラメータ(@departmentId)で値を渡す
  • ExecuteScalar の戻り値を Convert.ToInt32 で変換
  • 例外時は SqlExceptionNumberMessage を表示

必須課題が終わった人は、発展課題に取り組んでください。 発展課題からは、仕様だけが提示されます。実装方法は自分で考えてください。

発展課題は「プログラムから SQL を使う」練習場です

SQL 研修で身に付けた SUM / AVG / JOIN / GROUP BY / HAVING / サブクエリ を、C# プログラムから実行して 1 つの値を受け取る(ExecuteScalar)形で書いてみましょう。SQL の腕の見せどころです(結果が 1 つの値になるように書く のがポイント)。 検索条件は、本文 17-7 のパラメータ(@名前)で渡します。SQLServer の書き方で迷ったら、本章 17-9 の 方言表 を参照してください。

C# 側のコード(ExecuteScalar + Convert.ToInt32 / Convert.ToDecimal + 例外処理)は必須課題と同じ形のままです。変えるのは SQL だけ です。


課題 17-4 平均給与より高い社員は何人か(サブクエリ)

Section titled “課題 17-4 平均給与より高い社員は何人か(サブクエリ)”

以下の仕様で Kd17_04_AboveAverageCount を実装してください。

仕様:

  • 全社員の 平均給与より高い 給与をもらっている社員の 人数 を求める
  • ヒント:WHERE salary > (SELECT AVG(salary) FROM employees) のように サブクエリ を使う
  • 結果を「平均給与より高い社員:〇人」と表示する
  • 例外処理は必須課題と同じ構造

実行結果例(サンプルデータの場合):

平均給与より高い社員:2人

平均給与は 530000 円(= 5300000 ÷ 10)。これより高いのは給与 800000 円の 2 人(佐々木明子・菊池浩二)です。COUNT の戻り値は Convert.ToInt32 で受け取ります。


課題 17-5 カテゴリを指定して在庫金額の合計を求める(パラメータ + 集計式)

Section titled “課題 17-5 カテゴリを指定して在庫金額の合計を求める(パラメータ + 集計式)”

以下の仕様で Kd17_05_CategoryStockValue を実装してください。 第 16 章で作った products テーブルを使い、カテゴリ名を文字列のパラメータで渡します

仕様:

  • プログラムの変数 string category = "文房具"; を用意する
  • 指定したカテゴリの商品について、価格 × 在庫数 の合計(在庫金額)を求める
  • ヒント:SELECT SUM(price * stock) FROM products WHERE category = @category
  • AddWithValue("@category", category)文字列 を渡す
  • 結果を「カテゴリ『文房具』の在庫金額:〇〇円」と表示する
  • category"食器""衣類" に変えても動くことを確認する

実行結果例(category = "文房具" の場合):

カテゴリ『文房具』の在庫金額:29000円

文房具=29000 円、食器=29600 円、衣類=36000 円(第 16 章のサンプルデータの場合)。

発展のひとひねり:該当する商品が無いカテゴリを渡すと SUMNULL を返し、Convert.ToInt32 で例外になります。0 を返したいときは COALESCE(SUM(price * stock), 0) と書きます(COALESCE は Oracle・SQLServer 共通。17-9 の方言表も参照)。


課題 17-6 指定金額以上の注文は何件か(JOIN + GROUP BY + HAVING + パラメータ)

Section titled “課題 17-6 指定金額以上の注文は何件か(JOIN + GROUP BY + HAVING + パラメータ)”

以下の仕様で Kd17_06_OrdersOverAmount を実装してください。 第 16 章で作った orders / order_items / products を使い、合計金額が指定額以上の注文の件数 を求めます。

仕様:

  • プログラムの変数 int threshold = 2000; を用意する
  • 各注文の合計金額(価格 × 数量 の合計)が threshold 以上の注文が 何件あるか を数える
  • ヒント:
    • 注文ごとに金額を集計するには GROUP BY oi.order_id
    • 集計後の合計で絞り込むには HAVING SUM(p.price * oi.quantity) >= @threshold
    • その「注文の一覧」を サブクエリ にして、外側で COUNT(*) すれば件数になる
  • AddWithValue("@threshold", threshold) で値を渡す
  • 結果を「合計 2000 円以上の注文:〇件」と表示する
  • threshold10002400 に変えても動くことを確認する

実行結果例(threshold = 2000 の場合):

合計 2000 円以上の注文:2件

注文別の合計は 2140 / 1000 / 2400 円。2000 円以上は注文 1 と注文 3 の 2 件 です(threshold = 1000 なら 3 件、2400 なら 1 件)。

結果は 1 つの値なので、JOIN・GROUP BY・HAVING・サブクエリを使っても、これまでどおり ExecuteScalar + Convert.ToInt32 で受け取れます。SQL 研修で書いた集計クエリが、C# からそのまま動くことを体感してください。


提出前に、次の点を確認してください。

  • 全プロジェクトが Kadai17 ソリューションの中にある
  • 各プロジェクトで Microsoft.Data.SqlClient を NuGet で追加した
  • 各プロジェクトで Nullable を disable にしている
  • 接続文字列に TrustServerCertificate=true が含まれている
  • using 宣言 で接続・コマンドを書いている
  • パラメータが必要な課題(17-3・17-5・17-6)で AddWithValue を使っている(文字列連結で値を埋め込んでいない)
  • SqlExceptioncatch している
  • ビルドエラーが出ていない
  • タイマー時点で commit 済み(または 提出メモ.txt を書いた)
  • 各課題のプログラムが期待した出力を返す

タイマーが鳴ったら、その時点の状態を Git に提出します。

Terminal window
git status
git add .
git commit -m "Chapter17 タイマー提出: 必須17-1〜17-3完成、17-4途中 / 特になし"
git push origin main

Git が使えないときは、上記コミットの代わりに Kadai17 フォルダを提出先サーバへコピーし、コピー先に 提出メモ.txt を作成してください(演習課題の「提出方法:Git が使えないときはサーバへコピー」参照)。

Git の詳しい操作は、付録 C「Git のインストールと提出ルール」 を参照してください。


この章では、C# のコンソールアプリから SQLServer に接続する基本を学習しました。

主な内容は次のとおりです。

  • ADO.NET は .NET から DB を操作する共通の枠組みで、DB ごとに専用クラスが提供されている
  • SQLServer に接続するには Microsoft.Data.SqlClient を NuGet で追加する
  • 接続文字列は ServerDatabaseIntegrated SecurityTrustServerCertificate などを並べる
  • SqlConnection.Open() で接続を開き、SqlCommand で SQL を組み立て、ExecuteScalar で 1 つの値を取得する
  • using 宣言 を使うと、スコープを抜けるときに接続が自動で閉じられる
  • プログラムの変数の値を SQL に渡すときは、文字列連結ではなくパラメータ(@名前 + AddWithValue)を使う(壊れにくく、SQL インジェクションも防げる)
  • パラメータを使えば、SQL 研修で学んだ WHERE / JOIN / 集計を、C# プログラムから検索条件付きで実行できる
  • SqlExceptionNumber プロパティで原因を特定できる
  • Oracle と SQLServer は接続クラス名や接続文字列が違うが、構造は共通

ここまでで、コンソールアプリから SQLServer に接続し、SQL を実行して値を取得する基本パターンが身に付きました。

次章からは、いよいよ Windows フォームアプリ に入ります。 まずは画面の作り方とイベント駆動の考え方を学び(第 18〜21 章)、CSV 読み書き(第 22 章)を経て、SQLServer と連携する社員管理アプリ(第 23〜25 章)に進みます。

ここで学んだ SqlConnection / SqlCommand / ExecuteScalar の組み合わせは、Windows フォーム章でもそのまま使います。