カテゴリー
SugiBlog Webエンジニアのためのお役立ちTips

MySQLでカーソルを使って1行ずつ処理したい

ストアドプロシージャで1行ずつデータを処理したいときはカーソルを使って処理します。
以下、簡単な例をご紹介します。

-- 読み出した値を格納する変数を宣言
DECLARE currentId INT;
DECLARE currentColumnA VARCHAR(255);
DECLARE currentColumnB VARCHAR(255);

-- カーソルが最終行に達した判定するフラグ
DECLARE done INT DEFAULT FALSE;

-- カーソルを定義
DECLARE myCursor CURSOR FOR
SELECT id, columna, columnb FROM example;

-- カーソルが最終行に達したときの動作を制御
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

-- カーソルを開く
OPEN myCursor;

-- ループで1行ずつ処理
read_loop: LOOP
    -- カーソルから1行読み出し
    FETCH myCursor INTO currentId, currentColumnA, currentColumnB;

    -- カーソルからの読み出しが最後に達していればループを抜ける
    IF done THEN
        LEAVE read_loop;
    END IF;

    -- 読み出したデータを使用した処理等を書く

END LOOP;

-- カーソルを閉じる
CLOSE myCursor;
219 views

MySQLトランザクションでROLLBACKが効かない

タイトルにもある通り、MySQLトランザクションにてROLLBACK(ロールバック)ができないケースがあります。
一連の処理を記述し、特定条件下やどこかで失敗した場合等に全てを元に戻せるということで非常に便利なROLLBACKですが、
ROLLBACKできないSQL文がトランザクションに含まれていた場合、ROLLBACK自体がエラーとなり元に戻らないことがあります。

ROLLBACKできないSQLの例
  • CREATE
  • DROP
  • ALTER
  • TRUNCATE
ROLLBACKできるSQL
  • INSERT
  • UPDATE
  • DELETE

但しこちらはMySQLに限ったお話ですので、他のDBの場合は要確認です。
他のDBではROLLBACKできる場合があります。

公式リファレンス
https://dev.mysql.com/doc/refman/8.0/ja/cannot-roll-back.html

253 views

gitコマンドで差分抽出とアーカイブ出力

gitを使ってソース管理し開発をしていて、差分ファイルをリリースするときのTipsです。

差分を抽出する

git diff [古いブランチ名]..[新しいブランチ名]
git diff [古いブランチ名] [新しいブランチ名]

ファイル名だけを列挙したいとき

git diff --name-only [古いブランチ名] [新しいブランチ名]

ファイル名のリストをテキストファイルにリダイレクトで書き出す

git diff --name-only [古いブランチ名]..[新しいブランチ名] >> diff.txt

ブランチをアーカイブ出力

git archive [ブランチ名] -o archive.zip

出力するディレクトリを指定

git archive --prefix=archive/ [ブランチ名] -o archive.zip

差分抽出とアーカイブ出力を組み合わせて、抽出した差分ファイルをアーカイブ出力

git archive --prefix=archive/ [新しいブランチ名] (git diff --name-only [古いブランチ名] [新しいブランチ名] --diff-filter=ACMR) -o diff.zip
フィルターの種類
A 追加
C コピー
M 変更
R リネーム
D 削除

小文字にすると逆になるので、以下のようにしても同じことになります。

git archive --prefix=archive/ [新しいブランチ名] (git diff --name-only [古いブランチ名] [新しいブランチ名] --diff-filter=d) -o diff.zip
1,186 views

CSVにダブルクォーテーションを簡単に付けるには

CSVファイルで各カラムがダブルクォーテーションで括られていないことがたまにあります。
そんな時、皆さんどうしていますか?
私はテキストエディタでカンマ「,」を「”,”」に置換し、行の先頭と末尾にダブルクォーテーションを付ける・・・ということをいちいちやっていました。
しかし、PowerShellのコマンドを使えば、一発で簡単にダブルクォーテーションを付けることが出来るようなのです。

コマンド例はこちら

Import-Csv -Path "C:\source.csv" -Encoding Default | Export-csv -Path "C:\destination.csv" -Encoding Default -NoTypeInformation

インポートしたCSVの内容をパイプでエクスポート側に渡す、という動作になります。

オプションについて
Encoding 既定値(Default)はutf8NoBOM
ASCII, BigEndianUnicode, BigEndianUTF32, OEM, Unicode,
UTF7, UTF8, UTF8BOM, UTF8NoBOM, UTF32
NoTypeInformation TYPE情報ヘッダーをエクスポートしない

変換元のCSV

ColumnA,ColumnB,ColumnC
1,Ipsum,Sed
2,Magna,Est
3,Clita,Stet

変換後はこのようになります。

"ColumnA","ColumnB","ColumnC"
"1","Ipsum","Sed"
"2","Magna","Est"
"3","Clita","Stet"

但し、数値のカラムに対してもダブルクォーテーションが付けられるので、そこは我慢するしかないようです。

1,741 views

PDOで取得したデータが、数値型なのに文字列型になってしまう

PHPのバージョンを7.4系から8.x系にバージョンアップする事案があり、変更点やシステムの動作に問題がないか等の調査をしていました。
その中で公式ページにも見当たらないことがあったので、実際にバージョンを切り替えながら検証して分かったことを共有させていただければと思います。

今回発見した問題点はPDOのプリペアドステートメントです。
セキュリティ上、脆弱性のないように開発する中で必ず使われているかと思います。

ただ、このプリペアドステートメント、数値型のデータを文字列型で返してきます。
困りますね。
しかしながらそういう仕様なので、それに合わせてコーディングするしかないわけなんですが、
これがなんと、8.1以降だと数値型で返ってるのです。

なんということでしょう・・・。

厳密な比較を行っている箇所はすべて修正しないといけません。
例えば以下のような場合です。

if($col["COLUMN_NAME_INDEX"] === "1") { ... }

このCOLUMN_NAME_INDEXのデータ型は数値型(Integer)ですが、PDOのプリペアドステートメントで返ってくるのは文字列型のため、このような比較をしています。
これが8.1以降だとCOLUMN_NAME_INDEXのデータが数値型のため、(1 === "1")という式となり判定はfalseとなります。

この問題については、コーディング自体に間違いがあるわけではありませんので、コード解析ツール等にかけても出てこないと思います。
原因の究明に時間がかかったので、ここに記しておきます。

更に調査したところ、PDOの設定でエミュレート・モードというものがあり、有効/無効(true/false)で設定します。
実際には今回の件に関する具体的な設定ではないのですが、この設定を無効(false)に設定してみると8.0でも数値型で返ってきました。

PDO::setAttribute(PDO::ATTR_EMULATE_PREPARES, false)

検証したバージョンは次の通りです。
7.4.29
8.0.28
8.1.17
8.2.4

公式:PHP 8.0.x から PHP 8.1.x への移行

659 views