Delphi Programming / Object Pascal
[掲載 2015年09月21日] [更新 2015年09月21日] Delphi 一般・その他 |
Delphi 6 Personal 版で ADO によるデータベース接続 |
動作確認等 |
Windows 8.1 Pro64 + Delphi 6 Personal(UP2)
Microsoft ActiveX Data Object 6.0 Library (Version6.0) |
Delphi 6 Personal 版で ADO によるデータベース接続 |
Delphi 6 Personal 版にはデータベースの機能が実装されていませんが,タイプライブラリの取り込みで作成した ADODB_TLB ユニットを使用すると ADO によるデータベースの接続が可能になります.データベース接続用の文字列は,当サイトの ADO 関係のサンプルと同じです.
テキストファイルも読み出し専用として扱えます.テキストファイルは,データの追加や編集は自由ですから,見かけ上,読み書き可能なデータベースとすることも可能です.
エクセルのブックもデータベースとして接続できますが,エクセルのブックやテキストファイルは,世間一般で言うところの「データベース」としての機能を持たせることはできません.データ数が少ない個人用のテータベースとしての使用に限定されるでしょう.
それでも,SQL 文でクエリを実行できるという利点はあります. |
[備考 1]
本ページにも当サイトにも,データベース,クエリ, SQL 等に関する記事,説明はありません.それらの情報は参考書やネット上にいくらでもあります. |
[備考 2]
データベースの表 (テーブル) は,よくエクセルシートのような表にたとえられますが,データベースには行番号という概念はありません.ある条件に一致するデータを取り出すのがデータベースの主な目的です.特定の行のデータを取り出という操作はありません.そのため,SQL (クエリ) に関する知識は必須となります. |
[備考 3]
当サイトの Personal 版に関係する記事は,本ページだけです.以前は Personal 版の場合の記事や文章も書いていましたが,2015年09月21日現在,それらは全て削除しています. |
今回は,Microsoft ActiveX Data Object 6.0 Library (Version6.0) からタイプライブラリの取り込みを行ってテストしています.
下図の操作手順で,コンポーネントを登録しました.
マイクロソフトが提供している COM オブジェクトの名前は,他のオブジェクトと名前が重複する場合があります.その場合は,インストールしないで (どのパッケージにも登録しないで) ユニットだけを作成します.そして,uses に ADODB_TLB を追加して,実行時に TConnection と TRecordset のインスタンスを生成して使用します.
Microsoft ActiveX Data Object XX Library は,バージョンによって微妙な違いがあります.ネット上のコードを参考にする際には注意が必要です. |
|
図1
タイプライブラリの取り込み
- [プロジェクト] メニューで [タイプライブラリの取り込み] を選択
- [コンポーネントラッパーの作成] をチェックして [インストール]
- [インストール] ダイアログの [既存のパッケージに追加] タブを表示
- dclusr.dpk に追加
|
02_Access 2007 以前 の .mdb と Access 2010 の .accdb |
本ページで扱っている .mdb ファイルは,Microsoft Access 97 ~ 2003 用の JET エンジンデータベースファイルが対象です.Microsoft Access 2007 の拡張子は同じ .mdb ですが,これは本ページのコードでは操作できません.
また,Microsoft Access 2010 の,拡張子が .accdb の JET エンジンデータベースファイルも,本ページのコードでは操作できません.Access 2007 の .mdb ファイル,.acdb ファイルを扱う方法は,当サイトの関連記事を参考にしてください.
Access 2010, Access 2007 の JET エンジンデータベースファイルは,Access 2000 用の JET エンジンデータベースファイルに変換できます.Microsoft Access のヘルプで確認してください. |
03_特定の項目 ( Field ) の値だけを取得 |
ADO で JET エンジンデータベースに接続する例です.
テストには,以下のサイトから address03.lzh をダウンロードし,その中の「住所録Data03.mdb」を使用しています.このデータベースの「ta住所録」という表にある [名前] の項目の値だけを取得して表示します.
拡張子が .mdb のファイルは,Microsoft Access で作成したデータベースのファイルです. |
|
図2
設計時画面
- [ActiveX] タブから TConnection をフォームに配置
- 結果の表示用に TListBox コンポーネントも配置
|
|
図3
実行結果
|
リスト1
指定項目のデータのみ取得する例 (項目へのアクセスの参考用) |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, OleServer, ADODB_TLB, Grids, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Connection1: TConnection;
ListBox1: TListBox;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
LRecordSet : ADODB_TLB.Recordset;
LRcdAffectd : OleVariant;
LStrConnect : WideString;
LDBFile : WideString;
LUserID : WideString;
LPassword : WideString;
LStrSQL : WideString;
LDataText : WideString;
begin
ListBox1.Items.Clear;
//データベースのファイル名
LDBFile := ExtractFilePath(Application.ExeName) + '住所録Data03.mdb';
LUserID := '';
LPassword := '';
//接続文字列を作成してデータベース接続
LStrConnect := 'Provider=Microsoft.Jet.OLEDB.4.0'
+ ';Data Source=' + LDBFile;
Connection1.Open(LStrConnect, LUserID, LPassword, Integer(adConnectUnspecified));
try
//描画の更新を停止
ListBox1.Items.BeginUpdate;
//SQL文を指定して実行
LStrSQL := 'SELECT * FROM ta住所録';
LRecordSet := Connection1.Execute(LStrSQL, LRcdAffectd, 0);
//指定の項目の値だけ取得して表示
while not LRecordSet.EOF do begin
LDataText := LRecordSet.Fields['名前'].Value;
ListBox1.Items.Add(LDataText);
LRecordSet.MoveNext;
Application.ProcessMessages;
Sleep(1);
end;
finally
//描画の更新を再開
ListBox1.Items.EndUpdate;
//レコードセットとデータベースを閉じる
LRecordSet.Close;
Connection1.Close;
end;
end;
end.
上のテストで使用したデータベースのファイルには,複数の表があります.
以下は,これを取得した結果の図と,取得に使用したコードです. |
|
図4
表のリストを取得
- JET エンジンデータベースのシステム関係の表もリストされている
|
procedure TForm1.Button1Click(Sender: TObject);
var
LRecordSet : Recordset;
LStrConnect : WideString;
LDBFile : WideString;
LUserID : WideString;
LPassword : WideString;
begin
ListBox1.Items.Clear;
//データベースのファイル名
LDBFile := ExtractFilePath(Application.ExeName) + '住所録Data03.mdb';
LUserID := '';
LPassword := '';
//接続文字列を作成してデータベース接続
LStrConnect := 'Provider=Microsoft.Jet.OLEDB.4.0'
+ ';Data Source=' + LDBFile;
Connection1.Open(LStrConnect, LUserID, LPassword, Integer(adConnectUnspecified));
try
//描画の更新を停止
ListBox1.Items.BeginUpdate;
//OpenSchemaでファイル情報のレコードセットを取得
LRecordSet := Connection1.OpenSchema(adSchemaTables);
while not LRecordSet.EOF do begin
ListBox1.Items.Add(LRecordSet.Fields['TABLE_NAME'].Value);
LRecordSet.MoveNext;
end;
finally
//描画の更新を再開
ListBox1.Items.EndUpdate;
//レコードセットとデータベースを閉じる
LRecordSet.Close;
Connection1.Close;
end;
end;
ADO で JET エンジンデータベースに接続する例です.
これは,Microsoft Access で作成した,拡張子が .mdb のファイルをデータベースとして利用することを意味しています.
上のサンプルでは,クエリの実行に TConnection.Execute メソッドを使用し,そのメソッドの戻り値であるデータセットから項目のデータを取得しています.この TConnection.Execute によるクエリの実行結果のレコードセットは,読み出し専用です.または更新クエリの場合に使用します.
本サンプルでは,クエリの実行に TRecordset コンポーネントの Open メソッドを使用しています.
TRecordset.Open では,実行結果のレコードセットに対して細かい設定ができます.以下のサンプルでは読み出し専用と,読み書き可能なレコードセットの両方をテストしています.
レコードセットからのデータ取得には,TRecordSet.GetRows メソッドを使用しています.GetRows メソッドも,取り出すレコード数等が指定できます.
テストには,以下のリンクのサンプルに添付している .mdb ファイルを使用しました.
TConnection.Open メソッドの第 1 引数の接続文字列を変更すれば,他のデータベースや,エクセルのブックにも接続できます. |
|
図5
設計時画面
- TConnection, TRecordset コンポーネントを配置 ([ActiveX] タブ)
- データ表示用に TStringGrid を配置
|
|
図6
Button1 クリック
|
|
図7
Button2 クリック
- [食品群] 項目の値の先頭が '野菜' という文字である場合,その値を '野菜類等' に変更
|
|
図8
もう 1 度 Button2 をクリック
- [食品群] 項目の値の先頭が '野菜' という文字である場合,その値を '野菜' に変更
|
リスト3
TRecordset.ooen によるクエリの実行と,TRecordSet.GetRows によるデータの取得 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, OleServer, ADODB_TLB, Grids, StdCtrls;
type
TForm1 = class(TForm)
Connection1: TConnection;
Recordset1: TRecordset;
Button1: TButton;
Button2: TButton;
StringGrid1: TStringGrid;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
FStrConnect : WideString;
FUserID : WideString;
FPassword : WideString;
procedure DisplayToStringGrid(AGrid: TStringGrid; ARecordSet: TRecordset);
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
LDBFile : WideString;
begin
//データベースのファイル名
LDBFile := ExtractFilePath(Application.ExeName) + 'ForExport.mdb';
FUserID := '';
FPassword := '';
//接続文字列を作成
FStrConnect := 'Provider=Microsoft.Jet.OLEDB.4.0'
+ ';Data Source=' + LDBFile;
end;
//=============================================================================
// フォームを閉じる時の処理
//=============================================================================
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if Connection1.State = adStateOpen then begin
if Recordset1.State = adStateOpen then begin
RecordSet1.Close;
end;
Connection1.Close;
end;
end;
//=============================================================================
// 読み出し専用クエリの実行例
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LStrSQL : OleVariant;
begin
//データベース接続
if Connection1.State = adStateClosed then begin
Connection1.Open(FStrConnect, FUserID, FPassword, Integer(adConnectUnspecified));
end else begin
//レコードセットを閉じる
if Recordset1.State = adStateOpen then begin
RecordSet1.Close;
end;
end;
//SQL文を指定してレコードセットを取得
LStrSQL := 'SELECT 番号,食品群,食品名,キロカロリ FROM 食品カロリ表';
Recordset1.Open(LStrSQL,
Connection1.DefaultInterface,
adOpenForwardOnly,
adLockReadOnly,
adCmdText);
//取得したデータの表示
DisplayToStringGrid(StringGrid1, RecordSet1);
end;
//=============================================================================
// 更新可能なクエリの実行例
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LStrSQL : OleVariant;
LTempValue : OleVariant;
begin
//データベース接続
if Connection1.State = adStateClosed then begin
Connection1.Open(FStrConnect, FUserID, FPassword, Integer(adConnectUnspecified));
end else begin
//レコードセットを閉じる
if Recordset1.State = adStateOpen then begin
RecordSet1.Close;
end;
end;
//SQL文を指定してレコードセットを取得
//項目の[食品群]の先頭に'野菜'があるデータを取り出す
LStrSQL := 'SELECT * FROM 食品カロリ表 WHERE 食品群 LIKE "野菜%"';
Recordset1.Open(LStrSQL,
Connection1.DefaultInterface,
adOpenKeyset,
adLockOptimistic,
adCmdText);
//項目のデータを変更してデータベースも更新
if Recordset1.RecordCount > 0 then begin
Recordset1.MoveFirst;
while not Recordset1.EOF do begin
LTempValue := Recordset1.Fields['食品群'].Value;
if LTempValue = '野菜類等' then begin
Recordset1.Fields['食品群'].Value := '野菜';
end else begin
Recordset1.Fields['食品群'].Value := '野菜類等';
end;
//TRecordset.MoveNextで自動的にUpdateするので実際には不要
//Recordset1.Update;
RecordSet1.MoveNext;
end;
end;
//取得したデータの表示
DisplayToStringGrid(StringGrid1, Recordset1);
end;
//=============================================================================
// 引数のARecordSetのデータをTStringGridに表示するメソッド
//=============================================================================
procedure TForm1.DisplayToStringGrid(AGrid: TStringGrid; ARecordSet: TRecordset);
var
LDataArray : OleVariant;
LColsLow : Integer;
LColsHigh : Integer;
LRowsLow : Integer;
LRowsHigh : Integer;
LVarValue : OleVariant;
iCol : Integer;
iRow : Integer;
i : Integer;
j : Integer;
begin
//配列の初期化
LDataArray := Unassigned;
//描画の更新を停止
AGrid.Rows[0].BeginUpdate;
//データを見やすくするために列幅の変更を可能にしておく
AGrid.Options := AGrid.Options + [goColSizing];
//TStringGridの初期化
AGrid.FixedCols := 1;
AGrid.FixedRows := 1;
AGrid.ColCount := AGrid.FixedCols + 1;
AGrid.RowCount := AGrid.FixedRows + 1;
for i := 0 to AGrid.RowCount - 1 do begin
AGrid.Rows[i].Clear;
end;
try
if Recordset1.State = adStateClosed then exit;
//データベースの項目名だけを取得してTStringGridの固定行に表示
AGrid.ColCount := ARecordSet.Fields.Count + 1;
for i := 0 to ARecordSet.Fields.Count - 1 do begin
AGrid.Cells[i + AGrid.FixedCols, 0] := ARecordSet.Fields[i].Name;
end;
if ARecordSet.RecordCount = 0 then exit;
//先頭のレコードに移動しておく
Recordset1.MoveFirst;
//クエリーの結果のデータを取得
//adGetRowsRestは全てのデータ
LDataArray := ARecordSet.GetRows(Integer(adGetRowsRest));
//取得した2次元配列の要素番号の下限と上限値を取得
LColsLow := VarArrayLowBound(LDataArray, 1);
LColsHigh := VarArrayHighBound(LDataArray, 1);
LRowsLow := VarArrayLowBound(LDataArray, 2);
LRowsHigh := VarArrayHighBound(LDataArray, 2);
//TStringGridの行数を設定
AGrid.RowCount := (LRowsHigh - LRowsLow) + 1 + AGrid.FixedRows;
//セルに値を代入していく
for i := LRowsLow to LRowsHigh do begin
for j := LColsLow to LColsHigh do begin
iCol := j - LColsLow + AGrid.FixedCols;
iRow := i - LRowsLow + AGrid.FixedRows;
LVarValue := LDataArray[j, i];
if VarType(LVarValue) = VarNull then begin
AGrid.Cells[iCol, iRow] := '';
end else begin
AGrid.Cells[iCol, iRow] := VarToStr(LVarValue);
end;
end;
end;
finally
//描画の更新を再開
AGrid.Rows[0].EndUpdate;
end;
end;
end.
05_TConnection を使用しないデータベース接続 |
TConnection を使用すればトランザクション処理もできて便利ですが,TConnection を使用しなくてもデータベースに接続できます.
TRecordset.Open メソッドの第 2 引数に接続文字列を指定します. |
リスト4
TConnectiion を使用しないデータベースの接続例
DisplayToStringGrid は上のサンプルで作成したメソッド |
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
LDBFile : WideString;
begin
//データベースのファイル名
LDBFile := ExtractFilePath(Application.ExeName) + 'ForExport.mdb';
FUserID := '';
FPassword := '';
//接続文字列を作成
FStrConnect := 'Provider=Microsoft.Jet.OLEDB.4.0'
+ ';Data Source=' + LDBFile
+ ';User ID=' + FUserID
+ ';Password=' + FPassword;
end;
//=============================================================================
// フォームを閉じる時の処理
//=============================================================================
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if Recordset1.State = adStateOpen then begin
Recordset1.Close;
end;
end;
//=============================================================================
// 読み出し専用クエリの実行例
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LStrSQL : OleVariant;
begin
//レコードセットを閉じる
if Recordset1.State = adStateOpen then begin
RecordSet1.Close;
end;
//SQL文を指定してレコードセットを取得
LStrSQL := 'SELECT 番号,食品群,食品名,キロカロリ FROM 食品カロリ表';
Recordset1.Open(LStrSQL, FStrConnect, adOpenForwardOnly, adLockReadOnly, adCmdText);
//取得したデータの表示
DisplayToStringGrid(StringGrid1, RecordSet1);
end;
ADO でエクセルのブックをデータベースとして扱う例です.
エクセルの場合,ブックのファイル名 (フルパス) がデータベース名です.シートがデータベースの表に相当します.シートを表名として認識させるには,[Sheet2$] のように,シート名の末尾に $ を付け,全体を [] で括ります.
JET エンジンデータベースへの接続との違いは,接続文字列です.
テストには,以下のリンクのサンプルに添付しているブックを使用しました.
ADO によるエクセルへの接続の場合,データの編集はできますが,レコードの削除はできません. |
|
図9
設計時画面
- TConnection, TRecordset コンポーネントを配置 ([ActiveX] タブ)
- データ表示用に TStringGrid を配置
|
|
図10
実行結果
- [食品群] の先頭に '野菜' という文字が含まれているデータだけを取得
- [キロカロリ] の値でソートして表示
|
リスト5
エクセルのブックをデータベースとして扱う例
DisplayToStringGrid は上のサンプルで作成したメソッド |
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
LDBFile : WideString;
begin
//データベースのファイル名
LDBFile := ExtractFilePath(Application.ExeName) + 'ADOTestBook.xls';
FUserID := '';
FPassword := '';
//接続文字列を作成
FStrConnect := 'Provider=Microsoft.Jet.OLEDB.4.0'
+ ';Data Source=' + LDBFile
+ ';Extended Properties="Excel 8.0"';
end;
//=============================================================================
// フォームを閉じる時の処理
//=============================================================================
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if Connection1.State = adStateOpen then begin
if Recordset1.State = adStateOpen then begin
RecordSet1.Close;
end;
Connection1.Close;
end;
end;
//=============================================================================
// 更新可能なクエリの実行例
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LStrSQL : OleVariant;
LText : String;
begin
//データベース接続
if Connection1.State = adStateClosed then begin
try
Connection1.Open(FStrConnect, FUserID, FPassword, Integer(adConnectUnspecified));
except
LText := '他のユーザが編集中です' + sLineBreak + '開くことは出来ません';
MessageBox(Handle, PChar(LText), '情報', MB_ICONINFORMATION);
exit;
end;
end else begin
//レコードセットを閉じる
if Recordset1.State = adStateOpen then begin
RecordSet1.Close;
end;
end;
//SQL文を指定してレコードセットを取得
//項目の[食品群]の先頭に'野菜'があるデータを取り出す
//'SELECT * FROM [Sheet2$]'とすれば全てのデータの取得
LStrSQL := 'SELECT * FROM [Sheet2$] WHERE 食品群 LIKE "野菜%" ORDER BY キロカロリ';
Recordset1.Open(LStrSQL,
Connection1.DefaultInterface,
adOpenKeyset,
adLockOptimistic,
adCmdText);
//取得したデータの表示
DisplayToStringGrid(StringGrid1, RecordSet1);
end;
エクセルのブックをデータベースとして使用する場合,編集可能なモードで開くと,他のユーザからは開けなくなります.以下のように,TConnection.Mode := adModeRead; にして読み出し専用にすれば,他のユーザも開くことができます.TConnection.Mode の値を adModeRead にすると,TRecordset.Open の引数の値に関係なく読み出し専用となります.
この動作仕様は,エクセルのブックの「共有設定」には関係ありません. |
if Connection1.State = adStateClosed then begin
Connection1.Mode := adModeRead;
Connection1.Open(FStrConnect, FUserID, FPassword, Integer(adConnectUnspecified));
end else begin
//レコードセットを閉じる
if Recordset1.State = adStateOpen then begin
RecordSet1.Close;
end;
end;
07_TStringGrid の行番号とカレントレコード位置 |
TStringGrid の行番号に相当するレコード位置や,カレントレコード位置に相当する TStringGrid の行番号は,例えば TRecordset.AbsolutePosition プロパティを利用して,以下のコードで取得,設定できます. |
|
図11
実行例
- TStringGrid のアクティブな行にカレントレコード位置を移動
- そのレコードの項目の値を取得して表示
|
//=============================================================================
// 指定レコード位置に相当するTStringGridの行番号を取得する
// TRecordset.AbsolutePositionプロパティによるレコード位置の移動
// TRecordset.AbsolutePositionの最小値は1
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
if Recordset1.State = adStateClosed then exit;
if Recordset1.RecordCount = 0 then exit;
//テスト用にカレントレコードを移動
Recordset1.AbsolutePosition := 5;
StringGrid1.Row := Recordset1.AbsolutePosition + StringGrid1.FixedRows - 1;
end;
//=============================================================================
// TStringGridの行番号に相当する位置にカレントレコードを変更する
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
LRecPos : Integer;
begin
if Recordset1.State = adStateClosed then exit;
if Recordset1.RecordCount = 0 then exit;
LRecPos := StringGrid1.Row + StringGrid1.FixedRows - 1;
if Recordset1.RecordCount >= LRecPos then begin
Recordset1.AbsolutePosition := LRecPos;
//確認用の表示
//項目番号でなく項目名でFields['食品名']でも可
ShowMessage(Recordset1.Fields[2].Value);
end;
end;
|