「Web出版サイト」ベータ公開

Q&A集[?]

当サイトでのご質問の受付は終了しました

すべてのコンテンツを読み込み専用としたため、回答欄からも投稿できません

Apache OpenOffice/LibreOfficeのご質問はそれぞれのフォーラムへご投稿ください

質問コーナー

サイト内検索

分類メニュー

関連サイト


本日:1
昨日:2
総数:5669
現在:4


マクロからodbファイル内のテーブルを別のodbファイルにコピーするには?

ページOpenOffice.org FAQの登録ページ
投稿者gorodoku
分類
edit/refer
優先順位
edit/refer
状態
edit/refer
カテゴリー
edit/refer
投稿日2009-08-03 15:10:00 (月)
OSWindows Vista
依存するページ
バージョン
edit/refer

メッセージ

回答ページでは行末に「~」を付加する必要はありません

いつもお世話になっております。

タイトル通りの質問ですが、例えば「file_A.odb」内の「テーブルA」を、そっくりそのまま「file_B.odb」にコピーすることは可能でしょうか?


まず考え方ですが

M.Kamataki (2009-08-04 00:37:15 (火))

Accessにはリンクテーブルという機能があって、データベースAにデータベースBのテーブルをリンクできます。一応、この機能が実現すれば、マクロを使わずともご質問の目的は可能です。拡張要望のIssueも登録されていて、faq/4/460「外部DB接続と新規のテーブル作成」で紹介しています。

マクロは、同じデータベース内ならSQLの「SELECT INTO」文で実現できそうですが、異なるデータベースだとテーブルのデータをどう保持するのかが肝になりそうですね。一時ファイルにCSVを使うのが無難かな〜。

簡単にはいかないかもしれませんね

gorodoku (2009-08-04 08:44:09 (火))

おはようございます。引き続きfaq/4/1680の材料表フォームの作成をやっております。

フォームで作成した材料表テーブルを、個別案件ごとのodbファイルに保存する方法を考えてました。一案件内の各テーブルは各製品毎の材料表にしたいと思ってます。

出力されるテーブルの形式は必ず同じなので、空のテーブルを用意しておいてそれを(例えばフォーム上のボタンを押すなどのアクションで)指定したodbファイル内に指定したテーブル名としてコピーできればなぁ、と模索しておりました。(なので、コピー後のテーブルはコピー元のテーブルとリンクしてなくて良いです)

OOoBasicでSQL文をどこまでサポートしてるか分かりませんが、決まりきった形式なのでなんとなくですが

1.create table文でコピー先のodbファイルにテーブルを作成
2.フォーム上で一時的に作成されたコピー元のテーブルと、新しいテーブルをそれぞれ別個にRowSet接続
3.コピー元の先頭行から順次データを取得し、コピー先のテーブルへupdateでデータを流し込む(コピー元の行数文ループ)

、、、といった感じになるかなぁと思ってたんですがあまり簡単な方法ではないかなぁ、と。

ちなみにrowsetを二つ接続して逆のパターン(すでにある外部odbファイル内の材料表テーブルを、ファイル名・テーブル名を指定してフォームに読み込む)はできました。この場合はすでにある一時的なテーブルの中身をクリアしてデータを読み込んでるのであまり悩まずにできました。(といってもテーブルの形式のチェックは端折ってます。ダメですね)

書き込みの場合にはodbファイルがない場合にファイルそのものを作成させたり、ファイルやテーブルがすでにある場合は上書きする、しないなどいろいろ枝葉の処理も必要になり、結構面倒なことになりそうですorz

CSVファイルについてはまったく考えにありませんでした。もし宜しかったらさわりだけでも紹介していただければ、と思います。

無題

ike@九州 (2009-08-04 10:28:19 (火))

3.コピー元の先頭行から順次データを取得し、コピー先のテーブルへupdateでデータを流し込む(コピー元の行数文ループ)

SQL(INSERT INTO 〜)文を使っても行数分必要ですね
おまけに string型 は "'" で囲まないといけないし
簡単なテストでは出来ましたが…列数多いとINSERT INTO文作成に難儀しそうです。

個別案件ごとのodbファイルにフォームを入れてそのフォームで作業はダメなのでしょうか。もしくはフォームのみデスクトップに作成しておくとか…

CSVファイルを使う方法

M.Kamataki (2009-08-04 13:48:29 (火))

手作業で行う場合の手順です。データベースのA、Bが同じフォルダにある場合、データベースAで以下のSQLを「ツール」−「SQL」で実行します。

  1. CREATE TEXT TABLE "foo" (col1 INTEGER, col2 VARCHAR(100), col3 ...) ;
  2. SET TABLE "foo" SOURCE "foo.csv;encoding='UTF-8'" ;
  3. INSERT INTO "foo" SELECT * FROM "table_name" ;

3.のところで、foo.csvというファイルがデータベースAと同じフォルダ(ディレクトリ)にエクスポートされます。

インポートするデータベースBでは、上記の2.までを実行します。

このケースでは、同じフォルダにあるCSVファイルを共有することになります。最終的にデータベースBでリンクされたCSVテーブルから、「SELECT INTO」文でテーブルにするかどうかは、必要に応じてだと思います。マクロを組むことを考えると比較的容易かつ汎用的な手順だと思います。

参考:

マクロだとこんな感じでしょうか

gorodoku (2009-08-05 10:05:52 (水))

おはようございます。マクロでやるとこんな感じでしょうか、とりあえず試しに作ってみました。

同じフォルダにコピー元「tblA.odb」、コピー先「tblB.odb」があり、「tblA.odb」内のtbl1テ-ブルを「tblB.odb」にtbl1copyという名のテーブルとしてコピーする場合。
odbファイルはあらかじめデータソースに登録してあるものとします。

Sub Main

	dbcont = createUnoService("com.sun.star.sdb.DatabaseContext")

	'コピー元のデータベースに接続
	oDB = dbcont.getRegisteredObject("tblA")
	connection = oDB.getConnection("","")
	Statement = Connection.createStatement()

	'コピー元のデータベースに一時的にテキストテーブルを作成
	Sql = "CREATE TEXT TABLE ""tmp"" (""pkey"" integer,""tdv"" varchar(50),""gdv"" varchar(50),""shp"" varchar(50));"
	Statement.execute( Sql )

	'コピー元の一時的なテキストテーブルのソースとしてCSVファイルを指定
	Sql = "SET TABLE ""tmp"" SOURCE ""tmp.csv;encoding='UTF-8'"";"
	Statement.execute( Sql )

	'コピー元のテーブルのすべてのレコードを一時的なテキストテーブルへ挿入
	Sql = "INSERT INTO  ""tmp"" SELECT * FROM ""tbl1"";"
	Statement.execute( Sql )

	'コピー元の一時的なテキストテーブルを削除
	Sql = "DROP TABLE  ""tmp"";"
	Statement.execute( Sql )

	'コピー元のデータベース接続終了
	connection.close()
	
	'コピー先のデータベースに接続
	oDB = dbcont.getRegisteredObject("tblB")
	connection = oDB.getConnection("","")

	Statement = Connection.createStatement()

	'コピー先のデータベースに一時的にテキストテーブルを作成
	Sql = "CREATE TEXT TABLE ""tmp"" (""pkey"" integer,""tdv"" varchar(50),""gdv"" varchar(50),""shp"" varchar(50));"
	Statement.execute( Sql )

	'コピー先の一時的なテキストテーブルのソースとしてCSVファイルを指定
	Sql = "SET TABLE ""tmp"" SOURCE ""tmp.csv;encoding='UTF-8'"";"
	Statement.execute( Sql )

	'コピー先のデータベースに新しいテーブルを作成
	Sql = "CREATE TABLE ""tbl1copy"" (""pkey"" integer,""tdv"" varchar(50),""gdv"" varchar(50),""shp"" varchar(50));"
	Statement.execute( Sql )

	'コピー先の一時的なテーブルのすべてのレコードを新しいテーブルへ挿入
	Sql = "INSERT INTO  ""tbl1copy"" SELECT * FROM ""tmp"";"
	Statement.execute( Sql )

	'コピー先の一時的なテキストテーブルを削除
	Sql = "DROP TABLE  ""tmp"";"
	Statement.execute( Sql )

	'コピー先のデータベース接続終了
	connection.close()

End Sub

とりあえず動作するみたいです。コピー先で一時的なテキストテーブルから新しい通常のテーブルにインサートし、コピー元・先とも一時的なテキストテーブルをドロップすると、結果的にはコピー元にリンクしない単なるコピーテーブルが作れました。(でも一時CSVファイルが削除できてないですね(^^;どうしましょ?)

なんとなく突破口が見えてきました。いつもながら大変助かります。ご教示ありがとうございました。

ちなみに

gorodoku (2009-08-05 10:11:41 (水))

コピー元のodbのファイルパスが取得できれば、csvファイル消すのも、異なるフォルダ間のテーブルのコピーもできそうな気が、、、

データソースのファイルパス(またはURL)ってどうやって取得するんでしたっけ?DATASOURCEのURL()プロパティだとドライバとかプロトコルを取得しちゃうみたいですね?

余談ですが

gorodoku (2009-08-05 11:13:43 (水))

今ちょっと別なことやってたんですが、hsqldbだとDROPは通りますがTRUNCATE TABLEは通らないようですね。テーブルを残したまま空にしたいときはDELETE FROM 〜 WHERE {〜}じゃ無いと駄目なようです。

Re: 余談ですが

M.Kamataki (2009-08-05 18:26:14 (水))

テーブルを空にするコマンドは「DELETE FROM "table_name"」でOKです。試してみると、CSVファイルの内容は、完全に削除されるのではなく、データを半角スペースで上書きされるようでした。たぶんこれはデータベースとして正しい動作だと思います。

半角スペースで埋められたCSVファイルに改めて「INSERT INTO 〜」でテーブルから挿入したデータも、このCSVファイルにリンクしていた他のデータベースも半角スペース部分はスキップして存在するレコードだけを表示してくれました。

CSVファイルを一時ファイルに利用する場合は、ときどきお掃除したほうが気持ちが良いと思います。このような操作をHSQLDBではDEFRAG、PostgreSQLではVACUUMと呼んだりします。まぁ、削除という手もありますが。

DELETEコマンド

gorodoku (2009-08-05 19:24:54 (水))

DELETEコマンドはテーブル名指定だけでWHERE句無くても空テーブル化出来るんですね、試してみます。
CHECKPOINT DEFRAGもマクロから実行できるかどうか未確認ですが、可能であればフォーム経由で普段使うファイルにフォーム終了時に実行するようにしておけばすっきりする、、、かな?

ところで一時的に使うCSVファイルは、その作成場所を絶対パスで指定って出来るんでしょうか?
常に決まった場所にある同じCSVファイルを再利用できれば、どこからどこへコピーしてもあちこちにCSVファイルだらけにならずに済みそうな感じなんですが、、、

CSVファイルのフルパス指定

M.Kamataki (2009-08-06 09:53:56 (木))

現状ではodbファイルの存在するフォルダ以外のパスの指定はできませんでした。

http://hsqldb.org/doc/guide/ch06.html#N11075
を見るとHSQLDBとしては可能だと思います。

database_name.propertiesファイルに

textdb.allow_full_path=true

の1行を加えれば良さそうです。

Base標準の埋め込まれたHSQLDBでは、odbファイルをZIPで展開して、propertiesファイルを編集すると実現できそうですが、あまりお薦めはできないですねぇ。

「TRUNCATE TABLE」のサポート

M.Kamataki (2009-08-06 10:00:53 (木))

Base標準の埋め込まれたHSQLDBのバージョンは1.8.0.9のようです。以下を見ると、この7月に発表された次期バージョンの1.9からサポート予定になっています。

New Features in HSQLDB 1.9 and 2.0
http://hsqldb.org/web/features190.html

CSVファイルの挙動

M.Kamataki (2009-08-06 11:06:06 (木))

SHUTDOWN COMPACT ;

したら、CSVファイルが削除されました。0バイトのファイルになると思っていました。

ただ、SHUTDOWNするとHSQLDBとOpenOffice.orgの接続も切れます。接続が切れたままテーブルにアクセスするとクラッシュします。念のため、OpenOffice.orgを再起動したほうが良いと思います。そして、テーブルカテゴリのテーブル一覧を表示したタイミングで0バイトのCSVファイルが復活しました。こういう動作のようです。

CSV経由だと

gorodoku (2009-08-10 23:03:28 (月))

やはり別フォルダのファイル内のテーブルへコピーするのは難しいようですね、、、

そんな訳でとりあえずrowset2つ接続してチマチマコピーさせるようにコード書き書きしております。

フォームを開いている間は一時的にデータがコピーされているように見えるんですが、フォームを終了するとなぜか(?)データが更新されていません。updatexxxの使い方を間違えてるんでしょうかね?挿入行にデータを入れた場合もupdateRowで再転送が必要なんでしょうか。(それとも挿入行の場合はsetxxxじゃないとだめでしたっけ?)

添付して添削お願いしたいですが行き当たりばったりの汚いコードなので(汗)

テーブルリフレッシュとコミット

M.Kamataki (2009-08-11 10:34:42 (火))

コピー先データベースで、テーブルリフレッシュが必要かもしれません。

某所で書いた関数ですが、以下がテーブルリフレッシュ

Sub Refresh_All_Tables
	' Adrew Base本 PDF P17より
	Dim oDoc 'Document to refresh.
	Dim oDisp 'Dispatch helper.
	Dim oFrame 'Current frame.
 	'**** データベースを開く
	sURL = ConvertToURL("C:\database\foo.odb") 
	oDoc = starDesktop.loadComponentFromURL(sURL,"_default",0,Array()) 
		If NOT IsNULL(oDOC) AND NOT IsEmpty(oDoc) Then
			oDisp = createUnoService("com.sun.star.frame.DispatchHelper")
			oFrame = oDoc.getCurrentController().getFrame()
			oDisp.executeDispatch(oFrame,".uno:DBViewTables", "", 0, Array())
			oDisp.executeDispatch(oFrame,".uno:DBRefreshTables", "", 0, Array())
		End If		
	MsgBox "テーブルを更新しました!!"
End Sub

また、コミットとシャットダウンが必要であれば以下も。

Sub Shutdown_Database(sBaseFile)
	' データベースへのデータ書き込み
	sSQL = "CHECKPOINT;"
	' データベースのシャットダウン
	sSQL = "SHUTDOWN;"
	Execute_SQL(sBaseFile, sSQL)
End Sub

sBaseFileには、Baseファイルをフルパスで指定していました。

適当に変更してください。

遅くなりました

gorodoku (2009-08-24 14:48:57 (月))

返答遅くなってスミマセン。現業が忙しくなかなかBaseの起動もままなりませんでした。

まだ確認してませんが、上記の方法でやってみたいと思います。
ということで、一旦完了とさせていただきます。

ありがとうございました。

お名前:
題名:


添付ファイル: filetblB.odb 717件 [詳細] filetblA.odb 714件 [詳細]