トップ 最新 追記

Masa's blog

検索キーワード:

2009年08月04日 Microsoft SQL Server儂(わし)的解釈によるメモ(sqlcmd編) [長年日記]

_ Microsoft SQL Server儂(わし)的解釈によるメモ(sqlcmd編)

Microsoft SQL ServerCUIでコントロールする為にsqlcmdコマンドが用意されている。SQL Server Management Studioを使えばGUIで操作できるが、やはりコマンドラインで操りたい...。

なぜなら、

  • 操作の内容を記録に残しやすい。
  • 操作の伝承がしやすい。
  • 記録の再利用がしやすい。

などの理由があげられる。

例によって、各種ウェブページの切り貼り、再編集。

当然、無保証。

階層構造(あくまで個人的な解釈)

サーバ/インスタンス(SQLExpress)/データベース(master)/スキーマ(dbo)/テーブル

括弧内はデフォルト値。

データ型

int
  • 整数データを格納するデータ型
  • 記憶域サイズ:64bits
  • 範囲:-2^31 (-2,147,483,648) 〜 2^31 - 1 (2,147,483,647)
decimal
  • 固定長桁数、固定長小数桁数を持つ数値データを格納するデータ型
  • 範囲:桁数最大38桁(標準セットアップでは28桁)
money
  • 通貨を格納するデータ型
  • 記憶域サイズ:64bits
  • 範囲:-2^63(-922,337,203,685,477.5808)〜2^63 - 1(+922,337,203,685,477.5807)
float
  • 浮動小数点数値を格納するデータ型
  • 記憶域サイズ:64bits、128bits
  • 範囲:- 1.79E + 308 〜1.79E + 308
char, nchar
  • 8000文字以内の固定長の文字列を格納するデータ型。UNICODEで格納する場合はncharを使用する(4000文字以内)
varchar, nvarchar
  • 8000文字以内の可変長の文字列を格納するデータ型。UNICODEで格納する場合はnvarcharを使用する(4000文字以内)
text, ntext
  • 約20億文字以内の可変長の文字列を格納するデータ型。UNICODEで格納する場合はntextを使用する(約10億文字以内)
datetime
  • 日付を格納するデータ型
  • 範囲:1753 年 1 月 1 日〜9999 年 12 月 31 日
  • 精度:3.33ミリ秒

sqlcmdのヘルプ

C:\Documents and Settings\m-ito>sqlcmd -?
Microsoft (R) SQL Server Command Line Tool
Version 10.0.1600.22 NT INTEL X86
Copyright (C) Microsoft Corporation.  All rights reserved.

使用法: Sqlcmd            [-U login id]          [-P password]
  [-S server]            [-H hostname]          [-E trusted connection]
  [-d use database name] [-l login timeout]     [-t query timeout]
  [-h headers]           [-s colseparator]      [-w screen width]
  [-a packetsize]        [-e echo input]        [-I Enable Quoted Identifiers]
  [-c cmdend]            [-L[c] list servers[clean output]]
  [-q "cmdline query"]   [-Q "cmdline query" and exit]
  [-m errorlevel]        [-V severitylevel]     [-W remove trailing spaces]
  [-u unicode output]    [-r[0|1] msgs to stderr]
  [-i inputfile]         [-o outputfile]        [-z new password]
  [-f <codepage> | i:<codepage>[,o:<codepage>]] [-Z new password and exit]
  [-k[1|2] remove[replace] control characters]
  [-y variable length type display width]
  [-Y fixed length type display width]
  [-p[1] print statistics[colon format]]
  [-R use client regional setting]
  [-b On error batch abort]
  [-v var = "value"...]  [-A dedicated admin connection]
  [-X[1] disable commands, startup script, enviroment variables [and exit]]
  [-x disable variable substitution]
  [-? show syntax summary]

データベース作成

masterデータベースには重要なシステム情報が保存されるので、ユーザデータは別データベースを作成して、そちらに格納するのが好ましい。

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> CREATE DATABASE testdb
2> GO

データベース削除

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> DROP DATABASE testdb
2> GO

データベースの指定

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> USE testdb
2> GO

スキーマ作成

スキーマてなんじゃ?。データベースは複数のスキーマを含む器であり、スキーマは複数のテーブルを含む器である。具体的な使い方としては、同じ性格のテーブルをグループ化して管理しやすくしたり、グループ化することによりセキュリティを高めたりという事ではないかと想像する。

ちなみに、dboというスキーマがあらかじめ用意されており、明示的に指定しない場合はdbo内にテーブルが作成される。

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> use testdb
2> GO
3> CREATE SCHEMA testschema
2> GO

スキーマ削除

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> use testdb
2> GO
3> DROP SCHEMA testschema
2> GO

テーブル作成

スキーマを省略した場合はdboという名称のスキーマ内に作成される。

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> use testdb
3> CREATE TABLE testschema.testtable (id int)
2> GO

テーブル削除

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> use testdb
3> DROP TABLE testschema.testtable
2> GO

ログイン作成(SQL Server認証)

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> USE master
2> CREATE LOGIN testuser WITH PASSWORD = 'TESTUSER-PASSWORD'
3> GO

ログイン削除

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> USE master
2> DROP LOGIN testuser
3> GO

パスワードの変更

sqlcmd -U testuser -P CURRENT-PASSWORD -Z NEW-PASSWORD -S localhost\SQLEXPRESS

ユーザー作成

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> USE testdb
2> CREATE USER testuser FOR LOGIN testuser
3> GO

ユーザー削除

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> USE testdb
2> DROP USER testuser
3> GO

アクセス権の付与(管理者)

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> USE testdb
2> GRANT CONTROL ON DATABASE::testdb TO testuser WITH GRANT OPTION
3> GO

アクセス権の付与(ユーザ)

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> USE testdb
2> GRANT SELECT,INSERT,UPDATE,DELETE ON testtable TO testuser
3> GO

アクセス権の削除

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> USE testdb
2> REVOKE SELECT,INSERT,UPDATE,DELETE ON testtable FROM testuser
3> GO

接続

sqlcmd
1> :CONNECT localhost\SQLEXpress -U sa -P SA-PASSWORD

ヘルプを表示

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> :HELP

コマンド取消(GO実行前)

1> :RESET

ユーザ一覧

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> USE master
2> select name from syslogins
3> GO

データベース一覧

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> select name from sys.databases
2> GO

テーブル一覧

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> select name from sys.tables
2> GO

スキーマ一覧

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> select name from sys.schemas
2> GO

スキーマ、テーブル一覧

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> select SCHEMA_NAME(schema_id),name from sys.tables
2> GO

テーブル構造を見る

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> sp_columns 'testtable'
2> GO

バックアップモデル

単純復旧モデル : バックアップはフルバックアップ差分バックアップで運用する。ログ領域は自動的に削除される。(リストアに利用できない)。

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> ALTER DATABASE testdb SET RECOVERY SIMPLE
2> GO

完全復旧モデル : バックアップはフルバックアップ差分バックアップログバックアップで運用する。ログ領域はバックアップを取るまで削除されない。

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> ALTER DATABASE testdb SET RECOVERY FULL
2> GO

現在設定されている復旧モデルの表示。

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> SELECT name, recovery_model_desc FROM sys.databases WHERE name = 'testdb'
2> GO

バックアップ(フル)

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> BACKUP DATABASE testdb TO DISK = 'D:\backup\testdb_full.bak' WITH INIT
2> GO

バックアップ(差分 : 前回フルバックアップ以降の更新分)

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> BACKUP DATABASE testdb TO DISK = 'D:\backup\testdb_diff.bak' WITH INIT,DIFFERENTIAL
2> GO

バックアップ(トランザクションログ : 前回ログバックアップ以降のログ)

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> BACKUP LOG testdb TO DISK = 'D:\backup\testdb_log_YYYYMMDDhhmmss.bak' WITH INIT
2> GO

バックアップ計画

  • 月の第一営業日の業務終了後に「バックアップ(フル)」を実行。
  • 月の第二営業日〜月末営業日の業務終了後に「バックアップ(差分)」を実行。
  • 一日の営業時間中に適当な間隔で「バックアップ(トランザクションログ)」を実行。バックアップ先のファイルは毎回別のファイルに取る。ファイル名は昇順に並ぶように適当なシーケンス(YYYYMMDDhhmmssのような?)を付けると良い。

リストア(フル+差分+ログ)

障害が発生した時点で、アクティブなログ(=ログの末端)のバックアップを試みる

WITH NORECOVERYを指定してバックアップを取得した時点で、データベースがリストア(復旧)を受け付ける状態になり、通常の利用は出来なくなる。

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> USE master
2> BACKUP LOG testdb TO DISK = 'D:\backup\testdb_log_last.bak' WITH INIT,NORECOVERY
3> GO
上記ではバックアップできない場合は下記のいずれかの方法でバックアップを試みる(?)
sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> USE master
2> BACKUP LOG testdb TO DISK = 'D:\backup\testdb_log_last.bak' WITH INIT,CONTINUE_AFTER_ERROR
3> GO
sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> USE master
2> BACKUP LOG testdb TO DISK = 'D:\backup\testdb_log_last.bak' WITH INIT,NO_TRUNCATE
3> GO
その後、リストアを行う

以下の手順で、たとえばDROP DATABASE testdbされた様な状態からでも復旧可能。

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLEXPRESS
1> USE master
2> RESTORE DATABASE testdb FROM DISK = 'D:\backup\testdb_full.bak' WITH NORECOVERY
3> RESTORE DATABASE testdb FROM DISK = 'D:\backup\testdb_diff.bak' WITH NORECOVERY
4> RESTORE LOG testdb FROM DISK = 'D:\backup\testdb_log_20090804080000.bak' WITH NORECOVERY
6> RESTORE LOG testdb FROM DISK = 'D:\backup\testdb_log_20090804083000.bak' WITH NORECOVERY
7> RESTORE LOG testdb FROM DISK = 'D:\backup\testdb_log_20090804090000.bak' WITH NORECOVERY
8> RESTORE LOG testdb FROM DISK = 'D:\backup\testdb_log_20090804093000.bak' WITH NORECOVERY
9> RESTORE LOG testdb FROM DISK = 'D:\backup\testdb_log_last.bak' WITH NORECOVERY
10> RESTORE DATABASE testdb WITH RECOVERY
11> GO
特定時刻の状態までリストアしたい場合

上記の例で、末端のログの2009年8月4日 9時45分時点の状態に戻すには以下のようにする。

9> RESTORE LOG testdb FROM DISK = 'D:\backup\testdb_log_last.bak' WITH NORECOVERY,STOPAT='2009/08/04 09:45:00'
ちょっとずつ確認しながら、特定時刻の状態までリストアしたい場合
9> RESTORE LOG testdb FROM DISK = 'D:\backup\testdb_log_last.bak' WITH STANDBY='standby_file.tmp',STOPAT='2009/08/04 09:31:00'
10> select * from testdb.dbo.確認対象のテーブル
11> GO
12> RESTORE LOG testdb FROM DISK = 'D:\backup\testdb_log_last.bak' WITH STANDBY='standby_file.tmp',STOPAT='2009/08/04 09:32:00'
13> select * from testdb.dbo.確認対象のテーブル
14> GO
15> RESTORE LOG testdb FROM DISK = 'D:\backup\testdb_log_last.bak' WITH STANDBY='standby_file.tmp',STOPAT='2009/08/04 09:33:00'
16> select * from testdb.dbo.確認対象のテーブル
17> GO
18> RESTORE DATABASE testdb WITH RECOVER
19> GO

ログからのリストアを、ちょっとずつ進めて、行きすぎても、時間を遡ってのリストアは出来ないので注意すること。その場合は再度最初からトライする...。

その他

実際業務で利用する場合は、コマンドをテキストファイルに打ち込んで -i オプションで実行するのが良いと思われる。打ち込んだテキストファイルは設定資料として、ファイル名に日時を入れて管理するとかどうだろう。

sqlcmd -U sa -P SA-PASSWORD -S localhost\SQLExpress -s "," -W -i Hoge.sql

2009年08月05日 mplayerplug-in には安全な更新方法が用意されていないため、インストールされません [長年日記]

_ mplayerplug-in には安全な更新方法が用意されていないため、インストールされません

about:configから

extensions.checkCompatibility  -> false
extensions.checkUpdateSecurity -> false

を設定すれば、インストールできるようになる。


2009年08月06日 Microsoft SQL Server儂(わし)的解釈によるメモ(bcp編) [長年日記]

_ Microsoft SQL Server儂(わし)的解釈によるメモ(bcp編)

テーブルをCSVファイルにエキスポートする方法

bcp testdb.dbo.testtable out Hoge.csv -U sa -P SA-PASSWORD -S localhost\SQLExpress -c -t "\t"
  • データベース : testdb
  • スキーマ : dbo(これが既定値の「スキーマ」。省略可能。)
  • テーブル : testtable
  • 出力先CSVファイル : out Hoge.csv
  • ログインユーザ : -U sa
  • パスワード : -P SA-PASSWORD
  • 接続先ホスト : -S localhost
  • 接続先インスタンス : /SQLExpress(既定のインスタンスに接続する場合は省略可能。)
  • 出力形式 : -c (SJISテキストで出力する。UNICODE(UTF16LE)で出力する場合は -w)
  • 区切り文字 : -t "\t" (タブを指定。)

テーブルをselectしてCSVファイルにエキスポートする方法

bcp "select * from testdb.dbo.testtable" queryout Hoge.csv -U sa -P SA-PASSWORD -S localhost\SQLExpress -c -t "\t"
  • 抽出クエリ : "select * from testdb.dbo.testtable"
  • 出力先CSVファイル : queryout Hoge.csv
  • ログインユーザ : -U sa
  • パスワード : -P SA-PASSWORD
  • 接続先ホスト : -S localhost
  • 接続先インスタンス : /SQLExpress(既定のインスタンスに接続する場合は省略可能。)
  • 出力形式 : -c (SJISテキストで出力する。UNICODE(UTF16LE)で出力する場合は -w)
  • 区切り文字 : -t "\t" (タブを指定。)

CSVファイルをテーブルにインポートする方法

bcp testdb.dbo.testtable in Hoge.csv -U sa -P SA-PASSWORD -S localhost\SQLExpress -c -t "\t"
  • データベース : testdb
  • スキーマ : dbo(これが既定値の「スキーマ」。省略可能。)
  • テーブル : testtable
  • 入力CSVファイル : in Hoge.csv
  • ログインユーザ : -U sa
  • パスワード : -P SA-PASSWORD
  • 接続先ホスト : -S localhost
  • 接続先インスタンス : /SQLExpress(既定のインスタンスに接続する場合は省略可能。)
  • 入力形式 : -c (入力CSVがSJISテキストファイルであることの指定。入力がUNICODE(UTF16LE)テキストファイルの場合は -w)
  • 区切り文字 : -t "\t" (タブを指定。)

2009年08月12日 lib100(myh.no-ip.org) linux-2.4.37.4 version up [長年日記]

_ lib100(myh.no-ip.org) linux-2.4.37.4 version up

カーネルバージョンアップ linux-2.4.37.4

2.4.37.3は見逃してた...


2009年08月15日 Java儂(わし)的解釈によるメモ(JDBC + Oracle Database 10g Express Edition編) [長年日記]

_ Java儂(わし)的解釈によるメモ(JDBC + Oracle Database 10g Express Edition編)

さて、Oracleにも無償版があるとの事で、早速試してみた(Oracle Database 10g Express Edition)。

サーバはWindows版とLinux版が有る。とりあえずインストールの簡単そうだったWindows版で試してみる。

クライアントはLinuxで。

こちらも基本的にはJava儂(わし)的解釈によるメモ(JDBC + MySQL編)と同様だが、MS SQL Serverの時の様な方言的な動きは無く、ロードするドライバの部分だけをOracle用に書き換えれば動作した。

サーバ側(IP=192.168.0.1)にインストールする物

クライアント側にインストールする物

サンプルプログラム

//
// 「jdbc oracle」テスト
//
// compile : javac -encoding uutf-8 jdbc_oracle.java
// run     : java -cp .:ojdbc14.jar jdbc_oracle
//
//import java.sql.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;

public class jdbc_oracle {

	static Connection conn = null;
	static Statement stmt = null;
	static PreparedStatement pstmt = null;
	static ResultSet rs = null;
	static ResultSetMetaData rsmd = null;

	public static void main(String[] args) {
		try {
//
// JDBC for ORACLEのロード
//
			Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
			System.out.println("JDBCドライバをロードしました。");
//
// データベースへの接続
//
			String url = "jdbc:oracle:thin:@192.168.0.1:1521:XE";
			conn = DriverManager.getConnection(url, "system", "SYSTEM-PASSWORD");
			System.out.println("XE(データベース)に接続しました。");
//
// 自動コミット停止(トランザクションの開始)
//
			conn.setAutoCommit(false);
			System.out.println("自動コミットをOFFにしました。");
//
// SQLステートメントオブジェクト生成
//
			stmt = conn.createStatement();
//
// テーブルの削除
//
			try {
				stmt.executeUpdate("drop table member");
				System.out.println("テーブル member を削除しました。");
			} catch(Exception e){
				System.out.println("テーブル member を削除できませんでした(" + e.getMessage() + ")");
			}
//
// テーブルの作成
//
			stmt.executeUpdate(
				"create table member (" +
					"id INT PRIMARY KEY," +
					"name NVARCHAR2(50)," +
					"job NVARCHAR2(50)" +
				")"
			);
			System.out.println("テーブル member を作成しました。");
//
// わざと2重にテーブル作成してみる
//
			try {
				stmt.executeUpdate(
					"create table member (" +
						"id INT PRIMARY KEY," +
						"name NVARCHAR2(50)," +
						"job NVARCHAR2(50)" +
					")"
				);
				System.out.println("テーブル member を作成しました(2)。");
			} catch(Exception e){
				System.out.println("テーブル member を作成できませんでした(2)(" + e.getMessage() + ")");
			}
//
// 2次インデックス作成
//
			stmt.executeUpdate("create index i_member_name on member(name)");
			System.out.println("インデックスを作成しました。");
//
// タプルの追加
//
			try {
				pstmt = conn.prepareStatement("insert into member (id, name, job) values (?, ?, ?)");
//
// 1件目
//
				pstmt.setInt(1, 1);
				pstmt.setString(2, "伊藤 太郎");
				pstmt.setString(3, "サラリーマン");
				pstmt.executeUpdate();
				System.out.println("インサートしました。");
//
// 2件目
//
				pstmt.setInt(1, 2);
				pstmt.setString(2, "山田 太郎");
				pstmt.setString(3, "野球選手");
				pstmt.executeUpdate();
				System.out.println("インサートしました。");
//
// 3件目
//
				pstmt.setInt(1, 3);
				pstmt.setString(2, "山田 花子");
				pstmt.setString(3, "タレント");
				pstmt.executeUpdate();
				System.out.println("インサートしました。");
//
// わざと3件目をもう一度追加してみる
//
				pstmt.setInt(1, 3);
				pstmt.setString(2, "山田 花子");
				pstmt.setString(3, "タレント");
				pstmt.executeUpdate();
				System.out.println("インサートしました。");
			} catch (Exception e) {
				System.out.println("インサートできませんでした(" + e.getMessage() + ")");
			}
//
// タプルの検索
//
			try {
//
// クエリの実行
//
				pstmt = conn.prepareStatement("select * from member where id >= ?");
				pstmt.setInt(1, 1);
				rs = pstmt.executeQuery();
//
// 1件目の取り出し
//
				rs.next();
				System.out.println("セレクトしました(id=" +
					rs.getInt("id") + ")(name=" +
					rs.getString("name") + ")(job=" +
					rs.getString("job") + ")。");
//
// 2件目の取り出し
//
				rs.next();
				System.out.println("セレクトしました(id=" +
					rs.getInt("id") + ")(name=" +
					rs.getString("name") + ")(job=" +
					rs.getString("job") + ")。");
//
// 3件目の取り出し
//
				rs.next();
				System.out.println("セレクトしました(id=" +
					rs.getInt("id") + ")(name=" +
					rs.getString("name") + ")(job=" +
					rs.getString("job") + ")。");
//
// わざと存在しない4件目を取り出してみる
//
				rs.next();
				System.out.println("セレクトしました(id=" +
					rs.getInt("id") + ")(name=" +
					rs.getString("name") + ")(job=" +
					rs.getString("job") + ")。");
			} catch (Exception e) {
				System.out.println("セレクトできませんでした(" + e.getMessage() + ")");
			}
//
// タプルの更新
//
			try {
//
// 更新ステートメント実行
//
				pstmt = conn.prepareStatement("update member set job = ? where name = ?");
				pstmt.setString(1, "裁判官");
				pstmt.setString(2, "山田 太郎");
				pstmt.executeUpdate();
				System.out.println("アップデートしました。");
//
// 更新結果を取り出すクエリの実行
//
				pstmt = conn.prepareStatement("select * from member where name = ?");
				pstmt.setString(1, "山田 太郎");
				rs = pstmt.executeQuery();
//
// クエリ結果の取り出し
//
				rs.next();
				System.out.println("アップデート結果(id=" +
					rs.getInt("id") + ")(name=" +
					rs.getString("name") + ")(job=" +
					rs.getString("job") + ")。");
			} catch (Exception e) {
				System.out.println("アップデートできませんでした(" + e.getMessage() + ")");
			}
//
// タプルの削除
//
			try {
//
// 削除ステートメント実行
//
				pstmt = conn.prepareStatement("delete from member where name like ?");
				pstmt.setString(1, "山田%");
				pstmt.executeUpdate();
				System.out.println("デリートしました(" + pstmt.getUpdateCount() + "件)。");
//
// 削除後の状態の検索クエリ実行
//
				pstmt = conn.prepareStatement("select * from member");
				rs = pstmt.executeQuery();
//
// クエリ結果の取り出し
//
				while (rs.next()){
					System.out.println("デリート結果(id=" +
						rs.getInt("id") + ")(name=" +
						rs.getString("name") + ")(job=" +
						rs.getString("job") + ")。");
				}
			} catch (Exception e) {
				System.out.println("デリートできませんでした(" + e.getMessage() + ")");
			}
//
// メタデータの取得
//
			try {
//
// メタデータの取得
//
				rsmd = rs.getMetaData();
//
// カラム数の取得
//
				int numberOfColumns = rsmd.getColumnCount();
				System.out.print("メタデータを取得しました(");
				for (int i = 1; i <= numberOfColumns; i++) {
//
// カラム名称の取得
//
					String name = rsmd.getColumnName (i);
					System.out.print(name + "\t");
				}
				System.out.println(")。");
			} catch (Exception e) {
				System.out.println("メタデータを取得できませんでした(" + e.getMessage() + ")");
			}
//
// コミット(トランザクションの反映)
//
			try {
				conn.commit();
				System.out.println("コミットしました。");
			} catch (Exception e) {
				System.out.println("コミットできませんでした(" + e.getMessage() + ")");
			}
		} catch (SQLException e) {
			System.out.println("SQLException: " + e.getMessage());
			System.out.println("SQLState: " + e.getSQLState());
			System.out.println("VendorError: " + e.getErrorCode());
//
// ロールバック(トランザクションの破棄)
//
			try {
				conn.rollback();
			} catch(Exception e2){
				System.out.println(e2.getMessage());
			}
		} catch(Exception e){
			System.out.println(e.getMessage());
//
// ロールバック(トランザクションの破棄)
//
			try {
				conn.rollback();
			} catch(Exception e2){
				System.out.println(e2.getMessage());
			}
		}

		try {
//
// 検索クエリの結果セットオブジェクトを破棄
//
			rs.close();
//
// SQLステートメントオブジェクトを破棄
//
			stmt.close();
			pstmt.close();
//
// データベースからの切断
//
			conn.close();
		} catch(Exception e){
			System.out.println(e.getMessage());
		}
		System.out.println("XE(データベース)から切断しました。");

	}
}

_ 実行結果

JDBCドライバをロードしました。
XE(データベース)に接続しました。
自動コミットをOFFにしました。
テーブル member を削除しました。
テーブル member を作成しました。
テーブル member を作成できませんでした(2)(ORA-00955: すでに使用されているオブジェクト名です。
)
インデックスを作成しました。
インサートしました。
インサートしました。
インサートしました。
インサートできませんでした(ORA-00001: 一意制約(TESTUSER.SYS_C004065)に反しています
)
セレクトしました(id=1)(name=伊藤 太郎)(job=サラリーマン)。
セレクトしました(id=2)(name=山田 太郎)(job=野球選手)。
セレクトしました(id=3)(name=山田 花子)(job=タレント)。
セレクトできませんでした(空の結果セットです。)
アップデートしました。
アップデート結果(id=2)(name=山田 太郎)(job=裁判官)。
デリートしました(2件)。
デリート結果(id=1)(name=伊藤 太郎)(job=サラリーマン)。
メタデータを取得しました(ID     NAME    JOB     )。
コミットしました。
XE(データベース)から切断しました。

追記

  • stmt.executeUpdate(INSERT|DELETE|UPDATE) の戻り値は更新されたタプル数
  • stmt.executeUpdate(上記以外の更新系SQL) の戻り値は 0
  • エラー発生時は SQLExceptin 発生。getSQLState()にてエラーコードを取得できる。

_ Oracle 儂(わし)的解釈によるメモ(sqlplus編)

階層構造

データベース(XE)/スキーマ(ユーザ名と同じ)/テーブル
  • データベース : インスタンスと同義(?)。SIDという識別子で区別される。Express Editionの場合は XE に固定。データベース毎に異なる接続ポートを開く。慣例的に1521を使用する事が多い。
  • スキーマ : テーブル等をグループ分けする為の名前空間。データベースに接続するユーザ毎に自分のスキーマを所有する。その際のスキーマ名はユーザ名と同じになる。ユーザの作成したテーブル等のオブジェクトはデフォルトでユーザの所有するスキーマに(論理的に)格納される。
  • テーブル : 同左。

テーブルスペース

実際にデータが格納されるファイル(又はファイル群)に付けられる名称(表領域)。デフォルトで以下のテーブルスペースが有る。

  • USERS : 非 SYSTEM ユーザーにより作成される全てのデータベース・オブジェクトのデフォルト表領域
  • SYSAUX : SYSTEM 表領域の補助表領域
  • UNDO : ロールバックに必要なデータ領域
  • SYSTEM : データ・ディクショナリ表およびその他の管理データの領域

データベース作成

Express Editionの場合、同時に起動できるインスタンスが1個なので、実質あまり意味が無い。それでも新たに作った場合は既存のインスタンスをshutdownして、別のインスタンスをstartupすることになる。

ここでは説明しない。

sqlplusのヘルプ

C:\Documents and Settings\m-ito>sqlplus -?

SQL*Plus: Release 10.2.0.1.0 - Production

Copyright (c) 1982, 2005, Oracle.  All rights reserved.

使用方法1: sqlplus -H | -V

    -H             SQL*Plusのバージョンおよび
                   使用方法のヘルプを表示します。
    -V             SQL*Plusのバージョンを表示します。

使用方法2: sqlplus [ [<option>] [<logon>] [<start>] ]

  <option>は次のとおりです: [-C <version>] [-L] [-M "<options>"] [-R <level>] [-S]

    -C <version>   影響されるコマンドの互換性を、
                   <version>で指定されたバージョンに設定します。バージョンのフォームは
                   "x.y[.z]"です。たとえば、-C 10.2.0となります
    -L             エラー時に再プロンプト表示するかわりに、
                   1回のみログオンを試行します。
    -M "<options>" 出力の自動HTMLマークアップを設定します。オプション
                   のフォームは次のとおりです:
                   HTML [ON|OFF] [HEAD text] [BODY text] [TABLE text]
                   [ENTMAP {ON|OFF}] [SPOOL {ON|OFF}] [PRE[FORMAT] {ON|OFF}]
    -R <level>     制限モードを設定し、ファイルシステムと対話する
                   SQL*Plusコマンドを無効にします。レベルは
                   1、2または3にできます。最も限定的なのは-R 3であり、
                   ファイルシステムと対話するすべてのユーザー・コマンドを
                   無効にします。
    -S             コマンドのSQL*Plusバナー、プロンプトおよび
                   エコーの表示を抑制するサイレント・モードを
                   設定します。

  <logon>は次のとおりです: (<username>[/<password>][@<connect_identifier>] | /)
              [AS SYSDBA | AS SYSOPER] | /NOLOG

    データベース接続用のデータベース・アカウント・ユーザー名、
    パスワードおよび接続識別子を指定します。接続識別子がないと、
    SQL*Plusはデフォルトのデータベースに接続します。

    AS SYSDBAおよびAS SYSOPERオプションはデータベース管理
    権限です。

    /NOLOGオプションでは、データベースに接続せずに
    SQL*Plusを起動します。

  <start>は次のとおりです: @<URL>|<filename>[.<ext>] [<parameter> ...]

    スクリプト内の置換変数に割り当てられる指定パラメータを使用して、
    Webサーバー(URL)またはローカル・ファイルシステム(filename.ext)から、
    指定のSQL*Plusスクリプトを実行します。

SQL*Plusの起動時でCONNECTコマンドの後に、サイト・プロファイル
($ORACLE_HOME/sqlplus/admin/glogin.sqlなど)およびユーザー・プロファイル
(作業ディレクトリ内のlogin.sqlなど)が実行されます。ファイルには
SQL*Plusコマンドが含まれる場合があります。

詳細は、SQL*Plusユーザーズ・ガイドおよびリファレンスを参照してください。

ユーザー作成

sqlplus sys/SYS-PASSWORD as sysdba
SQL> CREATE USER testuser IDENTIFIED BY USER-PASSWORD
  2> DEFAULT TABLESPACE users
  3> TEMPORARY TABLESPACE temp
  3> QUOTA UNLIMITED ON users;

ユーザ名はアルファベット、数字以外は避けた方が良い(ハイフン、アンダーバー等の記号を入れるとハマルかも...)。

テーブルスペースを指定しないと、デフォルトでSYSTEM表領域にデータを作りにいくので、明示的にusers表領域を指定しておく。またこの例では容量制限(quota)を外している。

ユーザー削除

sqlplus sys/SYS-PASSWORD as sysdba
SQL> DROP USER testuser;

ORA-01922のエラーメッセージが出る場合は、当該ユーザで既にデータを作成している場合。データごと消して良い場合は、

sqlplus sys/SYS-PASSWORD as sysdba
SQL> DROP USER testuser CASCADE;

アクセス権の付与(管理者)

sqlplus sys/SYS-PASSWORD as sysdba
SQL> GRANT connect,resource,dba TO testuser;
  • connect : データベースに接続する権限
  • resource : データを操作する権限
  • dba : 管理者としての権限(これだけ有れば十分?、sysdbaなら最強!)

アクセス権の付与(一般ユーザ)

データベースに接続する権限(connect)、テーブルを作成する権限(resource)と既存のテーブル(testtable)に対する操作権限(select,insert,update,delete)を与える。

sqlplus sys/SYS-PASSWORD as sysdba
SQL> GRANT connect,resource to testuser;
SQL> GRANT select,insert,update,delete ON testtable TO testuser;

select,insert,update,deleteはallで代用可能。

アクセス権の削除

sqlplus sys/SYS-PASSWORD as sysdba
SQL> REVOKE connect FROM testuser;
SQL> REVOKE select,insert,update,delete ON testtable FROM testuser;

ヘルプを表示

sqlplus sys/SYS-PASSWORD as sysdba
SQL> HELP;

ユーザ一覧

sqlplus sys/SYS-PASSWORD as sysdba
SQL> select * from all_users;

ユーザ名は全て大文字に変換されて登録されている。

テーブル一覧

sqlplus sys/SYS-PASSWORD as sysdba
SQL> select * from all_tables;
SQL> select * from user_tables;

テーブル名は全て大文字に変換されて登録されている。

テーブル構造を見る

sqlplus sys/SYS-PASSWORD as sysdba
SQL> DESC testtable;

バックアップ(フル)

まずバックアップ対称のファイルを調べる。

sqlplus sys/SYS-PASSWORD as sysdba
SQL> select name from v$datafile;
SQL> select name from v$controlfile;
SQL> select name from v$tempfile;

ORACLEデータベースを停止する。

sqlplus sys/SYS-PASSWORD as sysdba
SQL> shutdown immediate;

上記で表示されたファイルを外部媒体等にコピーする。Oracleのインストールディレクトリ以下をまるごとコピーしても良いかも(?)。

ORACLEデータベースを再起動する。

sqlplus sys/SYS-PASSWORD as sysdba
SQL> startup;

テーブルをCSVファイルに書き出す

sqlplus sys/SYS-PASSWORD as sysdba
SQL> set linesize 32767
SQL> set pagesize 0
SQL> set trimspool on
SQL> set colsep ','
SQL> set head off
SQL> spool Hoge.csv
SQL> select * from testtable;
SQL> spool off

CSVファイルからテーブルに読み込む

これと言った方法は無い...しいて言えば、CSVファイルに適当なスクリプトをかまして、insert文の形式に変換してsqlplusに食わせるぐらい。

_ Oracle 儂(わし)的解釈によるメモ(sqlldr編)

CSVファイルからテーブルに読み込む

sqlldrというコマンドで出来る。

制御ファイル作成

ファイル名を sqlldr.ctrl とする。

load data
characterset AL32UTF8        <- infileの文字セット(UTF8を指定)
infile 'INPUT.CSV'           <- 入力ファイルの指定
badfile 'BAD.CSV'            <- インポートに失敗したレコードファイル
discardfile 'DISCARD.CSV'    <- 破棄されたレコードファイル
append into table member     <- インポート先のテーブル
fields terminated by X'09'   <- 項目の区切り文字(UTF16でのTABを指定)
trailing nullcols            <- 項目が無い時、NULLを設定する
(
        id,                  <- テーブル項目の列挙
        name,                <- テーブル項目の列挙
        job                  <- テーブル項目の列挙
)
実行
sqlldr userid=USER/PASSWORD control=sqlldr.ctl log=sqlldr.log

USER/PASSWORDで認証し、sqlldr.ctlの内容に従ってデータをインポートする。実行ログがsqlldr.logに記録される。

_ Oracle 儂(わし)的解釈によるメモ(もろもろ追記編)

How to handle UTF-8 data by sqlplus on windows

Put next lines into operation.sql and save it with encoding UTF-8. If you use notepad, operation.sql will be saved with BOM.

set echo off;
set feedback off;
set termout off;
set colsep ',';
set trimspool on;
set linesize 32767;
set pagesize 0;

drop table tb_sample;
create table tb_sample(
    id char(8),
    name nvarchar2(20)
);

insert into tb_sample names(id,name) values('12345678','オラクル 太郎');
commit;

spool result.txt
select * from tb_sample where id='12345678';
spool off

quit;

rmbom.c

#include <stdio.h>
main()
{
    int ch;
    int i = 0;
    while ((ch = getchar()) != EOF){
        i++;
        if (i > 3){
            putchar(ch);
        }
    }
}

Remove BOM from operation.sql. Operate it in Cygwin.

$ ./rmbom.exe <operation.sql >/tmp/junk
$ mv /tmp/junk operation.sql

Just do it in command prompt!

> SET NLS_LANG=JAPANESE_JAPAN.AL32UTF8
> sqlplus -s _user_/_password_ @operation.sql
> SET NLS_LANG=
> notepad result.txt

How to make sure about encoding of database

sqlplus sys/sys-password as sysdba
SQL> select value from nls_database_parameters where parameter='NLS_CHARACTERSET';
SQL> select value from nls_database_parameters where parameter='NLS_NCHAR_CHARACTERSET';

How to change ARCHIVELOG mode

sqlplus sys/sys-password as sysdba
SQL> archive log list;
SQL> shutdown immediate;
SQL> startup mount;
SQL> alter database archivelog;
SQL> alter database open;
SQL> archive log list;

How to switch current logfile

sqlplus sys/sys-password as sysdba
SQL> alter system switch logfile;
SQL> select name from v$archived_log;
SQL> select * from v$log;
SQL> select * from v$logfile;

How to archive current log

sqlplus sys/sys-password as sysdba
SQL> alter system archive log current;
SQL> select name from v$archived_log;

How to see current logfile

sqlplus sys/sys-password as sysdba
SQL> select * from v$log;
SQL> select * from v$logfile;

How to delete unused archivelog safely

rman target=sys/sys-password
RMAN> list archivelog all;
RMAN> delete noprompt archivelog until time 'sysdate - 3';
RMAN> delete noprompt archivelog all;
RMAN> quit;

2009年08月20日 Libretto U100 でタブキーが押しっぱなしに... [長年日記]

_ Libretto U100 でタブキーが押しっぱなしに...

Libretto U100のタブキーが前から敏感すぎた(触れるか触れないかぐらいで反応する)のだが、放っておくと遂に押しっぱなし状態になってしまった。

とりあえず、グリグリ押し込むと止まるのだが、すぐに押しっぱなしになってしまう。

しかたないので、~/.Xmodmapで

keycode 23 =

として、タブキーを無効にして、一時しのぎすることに。

検索エンジンで調査してみると(Libretto U100 タブ)すると同じような障害の報告が上がっていた。何か構造上の欠陥があるのかも。

さっそくオクで出物を調査、すぐに新品純正の物を発見し落札(6000円弱)。

交換後は(当然ながら)順調。

交換しながら感じたのだが、キーボードの基板がアルミ板みたいな感じでグニャグニャで、すこぶるたよりない。今後は丁寧に扱おうと心に決めた(?)。


2009年08月28日 Sharp NetWalker PC-Z1 [長年日記]

_ Sharp NetWalker PC-Z1

Sharp NetWalker PC-Z1なる製品が出た。この時代に、あまりにストレートなLinuxマシン、しかもARMアーキテクチャ版を出して来るSharpの無謀さ、いや度胸に感服。

しかし、Zaurus使い(SL-6000D)としては非常に興味のあるところだが、残念ながら商業的に成功するとは...。


2009年08月31日 ruby(1.8.x)儂(わし)的解釈によるメモ(DBI + Microsoft SQL Server編) [長年日記]

_ ruby(1.8.x)儂(わし)的解釈によるメモ(DBI + Microsoft SQL Server編)

ruby(Linux)からMicrosoft SQL Server Express Edition(Windows)に接続してみる。色々と準備してやらないといけない...。

unixODBC-2.2.14

  • vi /usr/local/etc/odbc.ini
[ODBC Data Sources]
freetds = FreeTDS ODBC Driver         <- たぶんコメント的な情報

[freetds]
Driver = /usr/local/lib/libtdsodbc.so
Description = Microsoft SQL Server    <- たぶんコメント的な情報
Servername = sqlserver                <- サーバ名は自由に付けてOK
Database = testdb                     <- 接続したいデータベース名 on SQLServer
Port = 1433                           <- SQLServerの(デフォルト)接続ポート

ruby-odbc-0.9997

dbi-0.4.2

もし上記でdbiがうまくインストール出来なかった場合は、

  • ruby setup.rb config
  • ruby setup.rb setup
  • ruby setup.rb install

を試してみるとよいかも...。

freetds-0.82

オリジナルサイトのhttp://www.freetds.org/は消滅っぽいので、debianとかubuntuのサイトからソースを頂く。

  • vi /usr/local/etc/freetds.conf

末尾に以下の記述を追加する。

[sqlserver]                      <- odbc.iniのServernameの指定と合わせる
        host = 192.168.0.1       <- SQLServerのIPアドレス
        port = 1433              <- SQLServerの接続ポート
        tds version = 8.0        <- プロトコルバージョン
        charset = UTF-16LE       <- SQLServer側の文字コード
        client charset = EUC-JP  <- クライアント側の文字コード

charsetセットは未指定でも構わないかもしれない。

charset, client charsetには以下のような指定が出来る(freetds-0.82/src/tds/encodings.hより)

  • UTF-8
  • UCS-2LE
  • UCS-2BE
  • EUC-JP
  • ISO-2022-JP
  • ISO-2022-JP-1
  • ISO-2022-JP-2
  • SJIS
  • UCS-2
  • UCS-4
  • UCS-4BE
  • UCS-4LE
  • US-ASCII
  • UTF-16
  • UTF-16BE
  • UTF-16LE
  • UTF-32
  • UTF-32BE
  • UTF-32LE

isql

unixODBC-2.2.14にコンソールベースのクライアント(isql)が含まれている。

isql -v freetds USER USER-PASSWORD

サンプルプログラム

たぶん

ruby -> ruby dbi -> ruby dbd-odbc -> ruby-odbc -> unixODBC -> freetds -> SQL Server

のような経路でアクセスが実現されている。

#! /usr/bin/ruby
#
require 'rubygems'
require 'dbi'
#
# データベースへの接続
#
# 実際の接続先は
#
#   o /usr/local/etc/odbc.ini
#   o /usr/local/etc/freetds.conf
#
# の設定で決まる。
#
dbh = DBI.connect("DBI:ODBC:freetds", "USER", "USER-PASSWORD")
puts("データベース(testdb)に接続しました。")
#
# 自動コミットの開始
#
dbh['AutoCommit'] = true
puts("自動コミット開始しました。")
#
# トランザクション(1)
#
dbh.transaction {|dbh|
#
# テーブルの削除
#
	begin
		sql = <<SQL
			drop table member
SQL
		dbh.do(sql)
		puts "テーブルの削除に成功しました。"
	rescue => e
		puts "テーブルの削除に失敗しました(#{e.to_s})。"
	end
#
# テーブルの作成
#
	begin
		sql = <<SQL
			create table member (
				id INT PRIMARY KEY,
				name NVARCHAR(50),
				job NVARCHAR(50)
			)
SQL
		dbh.do(sql)
		puts "テーブルの作成に成功しました。"
	rescue => e
		puts "テーブルの作成に失敗しました(#{e.to_s})。"
	end
#
# わざと2重にテーブルを作成してみる
#
	begin
		sql = <<SQL
			create table member (
				id INT PRIMARY KEY,
				name NVARCHAR(50),
				job NVARCHAR(50)
			)
SQL
		dbh.do(sql)
		puts "テーブルの作成に成功しました(2)。"
	rescue => e
		puts "テーブルの作成に失敗しました(2)(#{e.to_s})。"
	end
#
# インデックスの作成
#
	sql = <<SQL
		create index i_member_name on member(name)
SQL
	dbh.do(sql)
	puts "インデックスの作成に成功しました。"
}
#
# 自動コミットの停止
#
dbh['AutoCommit'] = false
#
# トランザクション(2)
#
dbh.transaction {|dbh|
#
# データの追加
#
	sql = <<SQL
		insert into member (id, name, job) values (?, ?, ?)
SQL
	sth = dbh.prepare(sql)
	sth.execute(1, '伊藤 太郎', 'サラリーマン')
	puts "インサートに成功しました。"
	sth.execute(2, '山田 太郎', '野球選手')
	puts "インサートに成功しました。"
	sth.execute(3, '山田 花子', 'タレント')
	puts "インサートに成功しました。"
#
# わざと2重キーで登録してみる
#
	begin
		sth.execute(3, '山田 花子', 'タレント')
		puts "インサートに成功しました。"
	rescue => e
		puts "インサートに失敗しました(#{e.to_s})。"
	end
	sth.finish
}
#
# データの検索
#
sql = <<SQL
	select * from member where id >= ?
SQL
sth = dbh.prepare(sql)
sth.execute(1)
while (row = sth.fetch)
	puts "セレクトに成功しました(id=#{row['id']})(name=#{row['name']})(job=#{row['job']})"
end
sth.finish
#
# トランザクション(3)
#
dbh.transaction {|dbh|
#
# データの更新
#
	sql = <<SQL
		update member set job = ? where name = ?
SQL
	sth = dbh.prepare(sql)
	sth.execute('裁判官', '山田 太郎')
	puts ("アップデートに成功しました。")
	sth.finish
}
#
# 更新結果の確認
#
sql = <<SQL
	select * from member where name = ?
SQL
sth = dbh.prepare(sql)
sth.execute('山田 太郎')
while (row = sth.fetch)
	puts "アップデートの確認(id=#{row['id']})(name=#{row['name']})(job=#{row['job']})"
end
sth.finish
#
# トランザクション(4)
#
dbh.transaction {|dbh|
#
# データの削除
#
	sql = <<SQL
		delete from member where name like ?
SQL
	sth = dbh.prepare(sql)
	sth.execute('山田%')
	puts ("デリートに成功しました。")
	sth.finish
}
#
# 削除の確認
#
sql = <<SQL
	select * from member
SQL
sth = dbh.prepare(sql)
sth.execute()
while (row = sth.fetch)
	puts "デリートの確認(id=#{row['id']})(name=#{row['name']})(job=#{row['job']})"
end
sth.finish
#
# メタデータの取得
#
sql = <<SQL
	select * from member
SQL
sth = dbh.prepare(sql)
sth.execute()
puts "メタデータ カラム数=#{sth.column_names.size}"
print "メタデータ カラム名="
sth.column_names.each {|name|
	print "#{name}\t"
}
puts ""
#
sth.column_info.each_with_index {|info, i|
        print "項番 = #{i} / "
        print "カラム名 = #{info.name} / "
        print "精度 = #{info.precision} / "
        print "スケール = #{info.scale}\n"
}
sth.finish
#
# (手動で)のコミット(またはロールバック)の実行
#
#dbh.rollback
#puts "ロールバックしました。"
dbh.commit
puts "コミットしました。"
#
# データベースからの切断
#
dbh.disconnect
puts("データベース(testdb)から切断しました。")

実行結果

データベース(testdb)に接続しました。
自動コミット開始しました。
テーブルの削除に成功しました。
テーブルの作成に成功しました。
テーブルの作成に失敗しました(2)(S0001 (2714) [unixODBC][FreeTDS][SQL Server]There is already an object named 'member' in the database.)。
インデックスの作成に成功しました。
インサートに成功しました。
インサートに成功しました。
インサートに成功しました。
インサートに失敗しました(23000 (2627) [unixODBC][FreeTDS][SQL Server]Violation of PRIMARY KEY constraint 'PK__member__3213E83F7A672E12'. Cannot insert duplicate key in object 'dbo.member'.)。
セレクトに成功しました(id=1)(name=伊藤 太郎)(job=サラリーマン)
セレクトに成功しました(id=2)(name=山田 太郎)(job=野球選手)
セレクトに成功しました(id=3)(name=山田 花子)(job=タレント)
アップデートに成功しました。
アップデートの確認(id=2)(name=山田 太郎)(job=裁判官)
デリートに成功しました。
デリートの確認(id=1)(name=伊藤 太郎)(job=サラリーマン)
メタデータ カラム数=3
メタデータ カラム名=id	name	job
項番 = 0 / カラム名 = id / 精度 = 10 / スケール = 0
項番 = 1 / カラム名 = name / 精度 = 150 / スケール = 0
項番 = 2 / カラム名 = job / 精度 = 150 / スケール = 0
コミットしました。
データベース(testdb)から切断しました。