第17章 C#からデータベースを操作する
この章の目的
Section titled “この章の目的”この章では、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 接続の基本を確認します。
ここからはチームで進める
Section titled “ここからはチームで進める”第 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 の違いを概観できる
本章で使用する環境
Section titled “本章で使用する環境”| 項目 | 内容 |
|---|---|
| 開発環境 | Visual Studio 2022 |
| プロジェクト種類 | コンソール アプリ |
| 対象フレームワーク | .NET 8 |
| ソリューション名 | Chapter17 |
| プロジェクト名 | Ch17_DbOperation |
| データベース | SQLServer 2022(TrainingDB) |
| 認証方式 | Windows 統合認証 |
| 使用するテーブル | employees、departments |
| NuGet パッケージ | Microsoft.Data.SqlClient |
csproj の Nullable は disable に変更してください
プロジェクト作成後、
Ch17_DbOperation.csprojを開き、<Nullable>disable</Nullable>に変更してください。詳しい手順は、第 1 章「1-1 プロジェクトを作成する」を参照してください。
この章の進め方
Section titled “この章の進め方”この章は 「読む」節と「実施する」節 が混在します。下の表の通りに進めてください。
| 節 | 内容 | 進め方 |
|---|---|---|
| 17-1 | ADO.NET とは | 読む |
| 17-2 | 参考:Oracle に接続する場合 | 読む(興味があれば実行可) |
| 17-3 | SQLServer に接続する準備 | 実施 |
| 17-4 | 接続文字列 | 実施 |
| 17-5 | 接続から件数取得までの完全コード | 実施 |
| 17-6 | 各部分の解説 | 読む |
| 17-7 | プログラムの値を SQL に渡す(パラメータ) | 実施 |
| 17-8 | 接続エラー時の確認手順 | 読む |
| 17-9 | Oracle と SQLServer の違い | 読む |
作業前チェック
Section titled “作業前チェック”作業を始める前に、次の内容を確認してください。
- 第 15 章までを Git に提出済みである
- SQLServer 2022 が起動している(サービスとして動作)
- SSMS(SQL Server Management Studio)で
TrainingDBデータベースに接続できる -
TrainingDBのemployees、departmentsテーブルが作成済みで、サンプルデータが入っている -
TrainingDBのproducts、orders、order_itemsテーブルも作成済みである(発展課題 17-5・17-6 で使用)
環境の準備について
前章の 第 16 章「SQLServer 環境構築」 で作成した
TrainingDBに対して接続します。 まだ構築していない場合は、先に第 16 章(必要に応じて 付録 G「SQLServer 2022 / SSMS インストール手順」)に取り組んでください。 第 16 章で社員系(employees/departments)と商品・注文系(products/orders/order_items)の両方を作成していれば、本章の全課題に取り組めます。
17-1 ADO.NET とは
Section titled “17-1 ADO.NET とは”ADO.NET(エイドー・ドット・ネット)は、.NET から各種データベースを操作するための仕組みの総称です。
ADO.NET は、データベースの違いを吸収するために、次のような 共通の役割 を定義しています。
| 役割 | 共通名 | SQLServer の場合 | Oracle の場合 |
|---|---|---|---|
| 接続 | IDbConnection | SqlConnection | OracleConnection |
| SQL 実行 | IDbCommand | SqlCommand | OracleCommand |
| 結果読み取り | IDataReader | SqlDataReader | OracleDataReader |
| 例外 | (各 DB 固有) | SqlException | OracleException |
つまり、「接続して、SQL を投げて、結果を受け取り、後始末する」 という流れはどの DB でも同じで、クラス名と接続文字列だけが DB ごとに違います。
この共通構造を意識しておくと、SQLServer を学んだ後に Oracle や PostgreSQL に切り替えても、同じパターンで書けるようになります。
17-2 参考:Oracle に接続する場合
Section titled “17-2 参考:Oracle に接続する場合”この節は「読むだけ」が基本 です。SQL 研修で Oracle を扱った経験を C# に橋渡しする位置づけで、興味があれば自分の SQL 研修環境で実行してみてください。
Oracle に接続する場合は、NuGet で Oracle.ManagedDataAccess.Core を追加し、次のようなコードを書きます。
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}"); } }}クラス名(OracleConnection、OracleCommand、OracleException)と接続文字列の書き方こそ違いますが、接続を開き、コマンドを作り、ExecuteScalar で値を 1 つ取得し、using 宣言 で後始末する という構造は、これから学ぶ SQLServer 版と完全に同じです。
次の節からは、研修のメイン環境である SQLServer に接続していきます。
17-3 SQLServer に接続する準備
Section titled “17-3 SQLServer に接続する準備”プロジェクトの作成
Section titled “プロジェクトの作成”新しいコンソールアプリ Ch17_DbOperation を作成してください(ソリューション名は Chapter17)。
作成後、Ch17_DbOperation.csproj を開き、<Nullable>disable</Nullable> に変更します。
NuGet パッケージを追加する
Section titled “NuGet パッケージを追加する”C# から SQLServer に接続するには、Microsoft.Data.SqlClient という NuGet パッケージが必要です。
Microsoft.Data.SqlClientとSystem.Data.SqlClient古い書籍やサンプルでは
System.Data.SqlClientが使われていることがあります。これは .NET Framework 時代からあるもので、現在はメンテナンスのみです。 .NET 8(現在の .NET)では、Microsoft.Data.SqlClientが推奨パッケージです。本研修もこちらを使います。
- ソリューションエクスプローラーで、プロジェクト
Ch17_DbOperationを右クリック - NuGet パッケージの管理 を選択
- 参照 タブで
Microsoft.Data.SqlClientを検索 - 検索結果から
Microsoft.Data.SqlClient(発行元 Microsoft)を選択 - 右側で インストール をクリック
- ライセンスの同意ダイアログで 同意する をクリック

インストールが終わると、Ch17_DbOperation.csproj に次のような行が追加されます。
<ItemGroup> <PackageReference Include="Microsoft.Data.SqlClient" Version="5.x.x" /></ItemGroup>これで、SqlConnection や SqlCommand などのクラスがコードから使えるようになりました。
17-4 接続文字列(Connection String)
Section titled “17-4 接続文字列(Connection String)”接続文字列 は、どの SQLServer の、どのデータベースに、どの認証方式で接続するかを示す文字列です。
本研修で使う接続文字列は次のとおりです。
Server=localhost;Database=TrainingDB;Integrated Security=true;TrustServerCertificate=true;各項目の意味は次のとおりです。
| 項目 | 意味 | 本研修での値 |
|---|---|---|
Server | 接続先のサーバー名(ホスト名・インスタンス名) | localhost |
Database | 接続先のデータベース名 | TrainingDB |
Integrated Security | Windows 統合認証を使うかどうか | 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 を次の内容にしてください。
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 を扱う 最も基本的なパターン です。
17-6 各部分の解説
Section titled “17-6 各部分の解説”上のコードを 1 つずつ見ていきます。
using Microsoft.Data.SqlClient;
Section titled “using Microsoft.Data.SqlClient;”using Microsoft.Data.SqlClient;これがないと、SqlConnection や SqlCommand などのクラスがコード上で見つかりません。
NuGet パッケージを追加していないと、この行で赤波線エラーになります。
第 7 章までの章では using System; などを書いていませんでしたが、これは .NET 8 の 暗黙的 using(ImplicitUsings)で System 名前空間が自動的に取り込まれているためです。
Microsoft.Data.SqlClient は暗黙的 using の対象外なので、自分で書く必要があります。
SqlConnection:接続を表すクラス
Section titled “SqlConnection:接続を表すクラス”using SqlConnection connection = new SqlConnection(connectionString);connection.Open();SqlConnection のオブジェクトを new した時点では、まだ接続は開かれていません。
Open() を呼ぶことで、実際に SQLServer への接続が確立されます。
接続文字列が間違っていたり、SQLServer サービスが起動していなかったりすると、Open() の時点で例外が発生します。
using 宣言:後始末を自動化する
Section titled “using 宣言:後始末を自動化する”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になっただけです。
SqlCommand:SQL 実行の命令
Section titled “SqlCommand:SQL 実行の命令”string sql = "SELECT COUNT(*) FROM employees";using SqlCommand command = new SqlCommand(sql, connection);SqlCommand は、「この接続で、この SQL を実行する」という命令を表すクラスです。
コンストラクタには、実行する SQL 文字列と、使う接続オブジェクトを渡します。
ExecuteScalar:1 つの値を取得する
Section titled “ExecuteScalar:1 つの値を取得する”object result = command.ExecuteScalar();int count = Convert.ToInt32(result);ExecuteScalar は、結果の 1 行目 1 列目の値だけ を取得するメソッドです。
COUNT(*)、SUM、AVG、MAX など、結果が 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 章で扱います。
SqlException:SQLServer 関連の例外
Section titled “SqlException:SQLServer 関連の例外”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 の中に @名前 という プレースホルダー(場所取り)を書いておき、実際の値は後から パラメータ として渡します。
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の値を2や3に変えると、結果も変わります。
仕組みのまとめ
Section titled “仕組みのまとめ”| 手順 | 書き方 |
|---|---|
| ① 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 に渡すときは、文字列連結ではなくパラメータ」 と覚えておいてください。
17-8 接続エラー時の確認手順
Section titled “17-8 接続エラー時の確認手順”Open() の時点で例外が出るときは、次の手順で確認します。
- SQLServer サービスが起動しているか
- Windows の「サービス」アプリを開き、
SQL Server (MSSQLSERVER)が「実行中」であることを確認します。
- Windows の「サービス」アプリを開き、
- SSMS で同じ接続文字列の条件で接続できるか
- SSMS で
Server=localhost、認証=Windows 認証 で接続できれば、サーバー側は問題なし。
- SSMS で
- データベース名が正しいか
- 接続文字列の
Database=TrainingDBの綴り、SSMS のデータベース一覧と一致しているか。
- 接続文字列の
- 認証方式が正しいか
- SQL 認証で接続したい場合は
Integrated Security=true;ではなく、User Id=...;Password=...;を指定します。
- SQL 認証で接続したい場合は
- エラー番号で具体例を確認する
SqlException.Numberを見て、上の表と照らし合わせます。
接続できないときの最初の一手
エラーメッセージを そのままコピーして検索 するのが、最も早い解決方法です。エラー番号は世界中の人が踏んでおり、たいてい同じ症状の解決例が見つかります。
17-9 Oracle と SQLServer の違い
Section titled “17-9 Oracle と SQLServer の違い”接続のしくみは共通でも、表記や挙動には違いがあります。代表的なものを表で示します。
| 観点 | Oracle | SQLServer |
|---|---|---|
| 接続クラス | OracleConnection | SqlConnection |
| コマンドクラス | OracleCommand | SqlCommand |
| 例外クラス | OracleException | SqlException |
| 接続文字列の例 | User Id=pingt;Password=oracle;Data Source=localhost:1521/XEPDB1; | Server=localhost;Database=TrainingDB;Integrated Security=true;TrustServerCertificate=true; |
| 文字列リテラルの引用符 | シングルクォート '山田' | シングルクォート '山田' |
| 識別子の引用符 | ダブルクォート "EMP" | 角かっこ [EMP] または "EMP" |
| 文字列の型 | VARCHAR2、NVARCHAR2 | VARCHAR、NVARCHAR |
| 数値の自動採番 | シーケンス + トリガー | IDENTITY |
| 接続先の単位 | サービス名 / SID | データベース名 |
SQL の文法は大筋共通ですが、識別子の囲み方・データ型名・自動採番のしくみ は差異が大きい部分です。 SQL 研修と本章の SQLServer を見比べると、SQL の方言の感覚がつかめます。
SQL 文の書き方の違い(方言)
Section titled “SQL 文の書き方の違い(方言)”SQL 研修(Oracle)で身に付けた SQL は、SQLServer でもほとんどそのまま通じますが、いくつかの「方言」 があります。本章や、これ以降の演習でつまずきやすいものを挙げます。
| やりたいこと | Oracle(SQL 研修) | SQLServer(本研修) |
|---|---|---|
| 文字列を連結する | last_name || first_name | last_name + first_name |
| NULL を別の値に置き換える | NVL(salary, 0) | ISNULL(salary, 0) または COALESCE(salary, 0) |
| 現在日時を取得する | SYSDATE | GETDATE() |
| 先頭 N 件だけ取得する | FETCH FIRST 5 ROWS ONLY / ROWNUM | SELECT TOP 5 ... |
| ダミー表から SELECT する | SELECT 1 FROM DUAL | SELECT 1(DUAL は不要) |
| 文字列の一部を取り出す | SUBSTR(...) | SUBSTRING(...) |
まずはこれだけ覚えておく
演習で最初に出会うのは 文字列連結(
||→+) です。SQL 研修の感覚で||と書くと SQLServer ではエラーになるので注意してください。 なお、COALESCEは Oracle・SQLServer 共通で使えるので、NULL 置換は迷ったらCOALESCEが安全です。COUNT/SUM/AVG/JOIN/WHERE/GROUP BY/ORDER BYなどの基本構文は両者で共通です。
よくあるつまずき
Section titled “よくあるつまずき”| 症状 | 原因 | 対処 |
|---|---|---|
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 に直接代入できない | 戻り値は object | Convert.ToInt32(...) で変換 |
SqlException を catch できない(using ディレクティブ 未記述) | using Microsoft.Data.SqlClient; がない | ファイル冒頭に追加 |
TrustServerCertificate を付けないと接続できない | サーバー証明書の検証エラー | ローカル開発では TrustServerCertificate=true を付ける |
学んだことチェック
Section titled “学んだことチェック”この章で学んだ内容をチェックしてください。
- ADO.NET の役割を説明できる
-
Microsoft.Data.SqlClientを NuGet で追加できる - 接続文字列の各項目(
Server、Database、Integrated Security、TrustServerCertificate)の意味を説明できる -
SqlConnection.Open()で接続が確立されることを説明できる -
SqlCommandとExecuteScalarで 1 つの値を取得できる -
using 宣言で接続を自動的に閉じられる - プログラムの変数の値を、パラメータ(
@名前+AddWithValue)で SQL に渡せる - 値を文字列連結で SQL に埋め込んではいけない理由を説明できる
-
SqlExceptionをcatchしてエラー番号を表示できる - 接続エラー時の確認手順を 3 つ以上挙げられる
- Oracle と SQLServer の違いを 3 つ以上挙げられる
ペアで以下の質問に答えてみてください。
- ADO.NET とは何ですか。
Microsoft.Data.SqlClientは何のために必要ですか。- 接続文字列の
Integrated Security=trueはどんな意味ですか。 SqlConnectionをnewしただけでは何が起きないと言えますか。using 宣言を使うと何が自動で行われますか。ExecuteScalarの戻り値がobject型なのはなぜですか。- SQL に変数の値を渡すとき、文字列連結ではなくパラメータ(
@名前)を使うのはなぜですか。 command.Parameters.AddWithValue("@departmentId", departmentId)は何をしていますか。SqlException.Numberは何を表しますか。- Oracle と SQLServer の接続クラスは、それぞれ何という名前ですか。
説明するときは、完全な答えでなくても構いません。 自分の言葉で説明しようとすることが大切です。
この章の演習課題に取り組みます。DB 接続・パラメータを実際に書く、歯ごたえのある演習です。
本章の進め方
Section titled “本章の進め方”| 段階 | 目安時間 | 内容 |
|---|---|---|
| ① 準備 | 10 分 | ペア確認 + 課題確認(評価対象外) |
| ② ソロ作業 | 35 分 | タイマーで計測。タイマー時点の commit が唯一の評価対象(NuGet 追加・接続文字列の準備があるため長めに設定) |
| ③ チーム時間 | 講師指定の発表開始時刻まで | レビュー + 発表者選出 + 実装続行(任意)。発表開始時刻は厳守 |
提出ルール(タイマー方式)
タイマー時点の commit が唯一の評価対象です。タイマー後の書き足しは評価されません。 コミットメッセージ形式:
Chapter17 タイマー提出: <どこまで完成> / <詰まったポイント>(なければ「特になし」) 例:Chapter17 タイマー提出: 必須17-1〜17-3完成、17-4途中 / パラメータの渡し方で詰まった
提出方法:Git が使えないときはサーバへコピー
講師の指示があったときは、
pushの代わりにKadai17フォルダを提出先サーバへコピーし、コピー先に提出メモ.txt(「どこまで完成」「詰まったポイント」を記載)を作成してください。
タイマー後のチーム時間の使い方
レビュー・発表者選出・実装続行(任意)を自由配分してください。発表開始時刻は厳守です。
この章の演習の進め方
Section titled “この章の演習の進め方”課題はソリューション Kadai17 の中に作成してください。
課題ごとに別のプロジェクトを作成 し、指定されたプロジェクト名を使います。各プロジェクトで Microsoft.Data.SqlClient を NuGet で追加し、Nullable を disable にしてから始めてください。
| 課題 | 必須/発展 | プロジェクト名 | 作成する主なファイル |
|---|---|---|---|
| 課題 17-1 | 必須 | Kd17_01_HelloSqlServer | Program.cs |
| 課題 17-2 | 必須 | Kd17_02_BothCount | Program.cs |
| 課題 17-3 | 必須 | Kd17_03_DeptEmployeeCount | Program.cs |
| 課題 17-4 | 発展 | Kd17_04_AboveAverageCount | Program.cs |
| 課題 17-5 | 発展 | Kd17_05_CategoryStockValue | Program.cs |
| 課題 17-6 | 発展 | Kd17_06_OrdersOverAmount | Program.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 プロジェクトに入力して、実際に動かしてください。
正しく接続でき、件数が表示されることを確認したら、接続文字列の一部を意図的に間違えて、エラーの動きも観察してください。
確認事項
- 正しい接続文字列で実行 →
employees テーブルの件数:10件(サンプルデータの場合) Database=TrainingDBをDatabase=NoSuchDbに変更 →SqlExceptionが発生、Number=4060系のメッセージ- テーブル名を
employees→employeessに変更 →Number=208が発生 - 確認後、接続文字列・SQL を 必ず正しい状態に戻す
実行結果例(正常):
SQLServer への接続に成功しました。employees テーブルの件数:10件条件:
using Microsoft.Data.SqlClient;を書くSqlConnection+SqlCommand+ExecuteScalarを使うSqlExceptionとExceptionの両方をcatchするusing 宣言で接続を自動クローズ
課題 17-2 employees と departments の両方の件数を取得
Section titled “課題 17-2 employees と departments の両方の件数を取得”新しいプロジェクト Kd17_02_BothCount を作成し、employees と departments の両方の件数を取得して表示してください。
実行結果例:
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の値を2、3に変えても正しく動くことを確認する- 例外処理は課題 17-1 と同じ構造
実行結果例(departmentId = 1 の場合):
SQLServer への接続に成功しました。部署 1 の社員数:4人部署 1=4 人、部署 2=2 人、部署 3=2 人(サンプルデータの場合)。
条件:
- 文字列連結ではなく、必ずパラメータ(
@departmentId)で値を渡す ExecuteScalarの戻り値をConvert.ToInt32で変換- 例外時は
SqlExceptionのNumberとMessageを表示
必須課題が終わった人は、発展課題に取り組んでください。 発展課題からは、仕様だけが提示されます。実装方法は自分で考えてください。
発展課題は「プログラムから 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 章のサンプルデータの場合)。
発展のひとひねり:該当する商品が無いカテゴリを渡すと
SUMはNULLを返し、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 円以上の注文:〇件」と表示する
thresholdを1000、2400に変えても動くことを確認する
実行結果例(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# からそのまま動くことを体感してください。
提出前チェックリスト
Section titled “提出前チェックリスト”提出前に、次の点を確認してください。
- 全プロジェクトが
Kadai17ソリューションの中にある - 各プロジェクトで
Microsoft.Data.SqlClientを NuGet で追加した - 各プロジェクトで Nullable を disable にしている
- 接続文字列に
TrustServerCertificate=trueが含まれている -
using 宣言で接続・コマンドを書いている - パラメータが必要な課題(17-3・17-5・17-6)で
AddWithValueを使っている(文字列連結で値を埋め込んでいない) -
SqlExceptionをcatchしている - ビルドエラーが出ていない
- タイマー時点で commit 済み(または
提出メモ.txtを書いた) - 各課題のプログラムが期待した出力を返す
Git への提出
Section titled “Git への提出”タイマーが鳴ったら、その時点の状態を Git に提出します。
git statusgit add .git commit -m "Chapter17 タイマー提出: 必須17-1〜17-3完成、17-4途中 / 特になし"git push origin mainGit が使えないときは、上記コミットの代わりに Kadai17 フォルダを提出先サーバへコピーし、コピー先に 提出メモ.txt を作成してください(演習課題の「提出方法:Git が使えないときはサーバへコピー」参照)。
Git の詳しい操作は、付録 C「Git のインストールと提出ルール」 を参照してください。
この章のまとめ
Section titled “この章のまとめ”この章では、C# のコンソールアプリから SQLServer に接続する基本を学習しました。
主な内容は次のとおりです。
- ADO.NET は .NET から DB を操作する共通の枠組みで、DB ごとに専用クラスが提供されている
- SQLServer に接続するには
Microsoft.Data.SqlClientを NuGet で追加する - 接続文字列は
Server、Database、Integrated Security、TrustServerCertificateなどを並べる SqlConnection.Open()で接続を開き、SqlCommandで SQL を組み立て、ExecuteScalarで 1 つの値を取得するusing 宣言を使うと、スコープを抜けるときに接続が自動で閉じられる- プログラムの変数の値を SQL に渡すときは、文字列連結ではなくパラメータ(
@名前+AddWithValue)を使う(壊れにくく、SQL インジェクションも防げる) - パラメータを使えば、SQL 研修で学んだ
WHERE/JOIN/ 集計を、C# プログラムから検索条件付きで実行できる SqlExceptionのNumberプロパティで原因を特定できる- Oracle と SQLServer は接続クラス名や接続文字列が違うが、構造は共通
ここまでで、コンソールアプリから SQLServer に接続し、SQL を実行して値を取得する基本パターンが身に付きました。
次章からは、いよいよ Windows フォームアプリ に入ります。 まずは画面の作り方とイベント駆動の考え方を学び(第 18〜21 章)、CSV 読み書き(第 22 章)を経て、SQLServer と連携する社員管理アプリ(第 23〜25 章)に進みます。
ここで学んだ SqlConnection / SqlCommand / ExecuteScalar の組み合わせは、Windows フォーム章でもそのまま使います。