| H2 | H2 Databaseの基本的な使い方 | |
| 2026/09/12 |
| H2 Databaseのインストール |
| コマンドライン(シェル) |
java -cp C:\Wintools\h2\bin\h2-2.4.240.jar org.h2.tools.Shell -url jdbc:h2:C:/usr/dbms/h2/testdb -user testuser -password testpass
sql>RUNSCRIPT FROM './tables/item_categories.sql'; sql>RUNSCRIPT FROM './tables/items.sql';
| サーバーモードで実行する |
java -cp C:\Wintools\h2\bin\h2-2.4.240.jar org.h2.tools.Server -tcp -tcpPassword ShutDownPass123 -ifExists
java -cp C:\Wintools\h2\bin\h2-2.4.240.jar org.h2.tools.Server -tcpShutdown "tcp://localhost" -tcpPassword ShutDownPass123
java -cp C:\Wintools\h2\bin\h2-2.4.240.jar org.h2.tools.Shell -url jdbc:h2:tcp://localhost/./stdb -user testuser -password testpass
| コマンドラインからのスクリプト実行 |
java -cp C:\Wintools\h2\bin\h2-2.4.240.jar org.h2.tools.RunScript ^ -url jdbc:h2:C:/usr/dbms/h2/database/testdb -user testuser -password testpass -script items.sql -showResults
| データ型 |
| 自動的に現在時刻を持つ列の定義 |
UDATE TIMESTAMP GENERATED ALWAYS CURRENT_TIMESTAMP
CDATE TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
| CSVファイルの読み書き |
INSERT INTO ITEMS(ITEM_NAME,CATEGORY_ID,SIZE_DESC,PRICE,MAKER_DESC,NOTES,DISPLAY_ORDER)
SELECT * FROM CSVREAD('./tables/items.dat', NULL, 'UTF-8', CHR(9))
INSERT INTO ITEMS(ITEM_NAME,CATEGORY_ID,SIZE_DESC,PRICE,MAKER_DESC,NOTES,DISPLAY_ORDER)
SELECT * FROM CSVREAD('./tables/items.dat', NULL, 'charset=UTF-8 fieldSeparator=|')
●数値でORDER BYする
CSVREAD関数を使ったSQL文にORDER BY句を付けることは可能ですが、
CSVファイルの全ての列は文字列として読み取られる点に注意が必要です。
つまり整数を想定している列でORDER BYしても、「1, 2, 20, 21, 225, 3, 4…」のような並び順になってしまいます。
これは変ですね。
これを整数の順に並べ直すには?
2つ方法があって、一つはLPAD関数を使って
ORDER BY LPAD(DISPLAY_ORDER, 3, '0') とすれば、「001,002,003,004,020,021,225…」と並びます。
もう一つはCAST関数を使って ORDER BY CAST(DISPLAY_ORDER AS INT)とする方法です。
後者の方が、取りうる最大桁数を予め知っておく必要がない点では優れていますが、
その代わり整数に変換できない値が1行でも含まれているとエラーになるので注意が必要です。
●集合関数も使える
CSVREADはこのように集合関数とも合わせて使えます。素晴らしい!
SELECT MAX(CAST(EMPLOYEE_NUM AS INT)), MIN(CAST(EMPLOYEE_NUM AS INT)), SUM(CAST(EMPLOYEE_NUM AS INT))
FROM CSVREAD('C:/usr/dbms/csv/csvdb/shiten.dat', null, 'UTF-8', CHAR(9))
WHERE AREA_CODE = '5'
●CSVを読むためだけに使う
CSVREADはINSERT文と合わせて使うとテーブルへのロードに使えますが、
JDBCなどのアプリケーションの中で、ただ単にCSVファイルを読むためだけにSELECT文として使うこともできます。
以下の例では、H2はインメモリデータベースとして立ち上げているもののデータベース操作は一切しておらず、
単にCSVファイルをアプリケーションに読み取って表示するだけでプログラムを終了しています。
public static void main(String args[]) {
Connection conn = null;
Statement stmt = null;
try {
Class.forName("org.h2.Driver");
conn = DriverManager.getConnection("jdbc:h2:mem:");
System.out.println("connected.");
stmt = conn.createStatement();
ResultSet rset = stmt.executeQuery(
"SELECT SHITEN_NAME, ADDRESS, EMPLOYEE_NUM " +
" FROM CSVREAD('C:/usr/dbms/csv/csvdb/shiten.dat', null, 'UTF-8', CHAR(9)) " +
"WHERE AREA_CODE = '5' ORDER BY CAST(EMPLOYEE_NUM AS INT) DESC");
while (rset.next()){
System.out.println(rset.getString(1) + "\t" + rset.getString(2) + "\t" + rset.getString(3));
}
rset.close();
} catch(Exception ex) {
ex.printStackTrace();
} finally {
try {
stmt.close();
conn.close();
} catch(SQLException ex) {
ex.printStackTrace();
}
}
}
| Csvクラスを使う |
public static void main(String args[]) throws SQLException {
Csv csv = new Csv();
csv.setFieldSeparatorRead('\t');
ResultSet rs = csv.read("C:/usr/dbms/csv/csvdb/shiten.dat", null, "UTF-8");
ResultSetMetaData meta = rs.getMetaData();
while (rs.next()) {
for (int i = 0; i < meta.getColumnCount(); i++) {
System.out.println(meta.getColumnLabel(i + 1) + ": " + rs.getString(i + 1));
}
System.out.println();
}
rs.close();
}
| MERGE文について |
CREATE TABLE SCORES(
NAME VARCHAR NOT NULL PRIMARY KEY,
SCORE INTEGER
)
INSERT INTO SCORES(NAME,SCORE) VALUES ('Taro', 90)
更新数:1
INSERT INTO SCORES(NAME,SCORE) VALUES ('Jiro', 85)
更新数:1
INSERT INTO SCORES(NAME,SCORE) VALUES ('Taro', 93)
ユニークインデックスまたはプライマリキー違反(エラー)
MERGE INTO SCORES USING DUAL ON NAME = 'Taro'
WHEN MATCHED THEN UPDATE SET SCORE = 93
WHEN NOT MATCHED THEN INSERT (NAME,SCORE) VALUES ('Taro', 93)
更新数:1 (Updateが実行された)
MERGE INTO SCORES USING DUAL ON NAME = 'Saburo'
WHEN MATCHED THEN UPDATE SET SCORE = 81
WHEN NOT MATCHED THEN INSERT (NAME,SCORE) VALUES ('Saburo', 81)
更新数:1 (Insertが実行された)
MERGE INTO SCORES USING DUAL ON NAME = 'Taro'
WHEN NOT MATCHED THEN INSERT (NAME,SCORE) VALUES ('Taro', 94)
更新数:0 (該当行が既にあれば何もしないので)
MERGE INTO SCORES USING DUAL ON NAME = 'Goro'
WHEN NOT MATCHED THEN INSERT (NAME,SCORE) VALUES ('Goro', 82)
更新数:1 (Insertが実行された)
●バインド変数を最適化する書き方
上の例の最後の2つのSQLには欠点があります。それは 'Taro' や 'Goro' が2回出てくるため、
アプリケーションから呼び出す際にバインド変数(パラメータ)をNAME列に対しては2回渡す必要があります。
つまりアプリケーション側がここはMERGE文を使っているのかINSERT文を使っているのかを把握している必要があり、
保守性が悪くなってしまいます。
これを改良するのに以下のような書き方も可能です。これの出典はOracle公式の
こちらです。
MERGE INTO SCORES a USING (SELECT 'Taro' AS NAME, 94 AS SCORE) newrec ON a.NAME = newrec.NAME WHEN NOT MATCHED THEN INSERT (NAME,SCORE) VALUES (newrec.NAME, newrec.SCORE) 更新数:0 (該当行が既にあれば何もしないので) MERGE INTO SCORES a USING (SELECT 'Goro' AS NAME, 82 AS SCORE) newrec ON a.NAME = newrec.NAME WHEN NOT MATCHED THEN INSERT (NAME,SCORE) VALUES (newrec.NAME, newrec.SCORE) 更新数:1 (Insertが実行された)
MERGE INTO SCORES a USING (SELECT ? AS NAME, ? AS SCORE) newrec ON a.NAME = newrec.NAME WHEN NOT MATCHED THEN INSERT (NAME,SCORE) VALUES (newrec.NAME, newrec.SCORE)
MERGE INTO SCORES a USING (SELECT CAST(? AS VARCHAR) AS NAME, CAST(? AS INT) AS SCORE) newrec ON a.NAME = newrec.NAME WHEN NOT MATCHED THEN INSERT (NAME,SCORE) VALUES (newrec.NAME, newrec.SCORE)
| もくじ | ||
| H2 | (C) 2002-2025 MISUMI URANO (2023/08/23 - 2026/09/12) |