yamaさんに事情を伺って軽くインストーラーのSQLを拝見しました。
instprocessor.phpの316行目
コード: 全て選択
$rs = mysql_query(
"REPLACE INTO $dbase.`".$table_prefix."categories` (`id`,`category`) ( SELECT MIN(`id`), '$category' FROM ( SELECT `id` FROM $dbase.`" . $table_prefix . "categories` WHERE `category` = '$category' UNION SELECT (CASE COUNT(*) WHEN 0 THEN 1 ELSE MAX(`id`)+1 END ) `id` FROM $dbase.`" . $table_prefix . "categories` ) AS _tmp )", $sqlParser->conn);
変数だらけで読みづらいですが、実際のSQLに近づけるとこうなります。
コード: 全て選択
REPLACE INTO modx.`modx_categories`(`id`,`category`)
SELECT MIN(`id`), '$category'
FROM (SELECT `id` FROM modx.`modx_categories` WHERE `category` = '$category'
UNION
SELECT (CASE COUNT(*) WHEN 0 THEN 1 ELSE MAX(`id`) + 1 END) `id`
FROM $dbase.`modx_categories`) AS _tmp
このクエリは指定されたカテゴリを強制的にmodx_categoriesテーブルに追加するものです。
INSERTのかわりにREPLACEを使っているのは既に存在している場合に対応するためです。UNIONを使って既存IDの検索結果と新しいID(CASE文で生成している)をくっつけており、小さい方の値を取ることで既存データがなければ新しいIDに書き込む、という動作になっていますね。
instprocessor.phpの316行目
コード: 全て選択
if (!@ mysql_query("INSERT INTO $dbase.`" . $table_prefix . "site_htmlsnippets` (name,description,snippet,category) VALUES('$name','$desc','$chunk',(SELECT (CASE COUNT(*) WHEN 0 THEN 0 ELSE `id` END) `id` FROM $dbase.`" . $table_prefix . "categories` WHERE `category` = '$category'));", $sqlParser->conn)) {
同じくクエリ部分だけ抜粋
コード: 全て選択
INSERT INTO modx.`modx_site_htmlsnippets` (name,description,snippet,category)
VALUES ( '$name', '$desc', 'chunk',
(SELECT (CASE COUNT(*) WHEN 0 THEN 0 ELSE `id` END) `id`
FROM modx.`modx_categories`
WHERE `category` = '$category')
);
先ほどと似た要領で、新しいチャンクのカテゴリID値を検索しています。CASE文を使っているのは該当レコードがないときに0を返すためですね。(たぶんこれを指定しないと該当レコードがないときは0ではなくEmptyが返るはず)
で、問題になっている4.0の件ですが、MySQLのマニュアルに次のような記事がありました。
UNION の結果セット内のカラムの型と長さでは、すべての SELECT ステートメントで取り出された値が考慮されます。 MySQL 4.1.1 より前のバージョンの UNION では、最初の SELECT で使用された値のみに基づいて結果の型と長さが決まる、という制約がありました。 この場合、たとえば、最初の SELECT の値よりも長い値が 2 番目の SELECT で取り出されると、切り捨てが行われることがあります。
― 6.4.1.2. UNION 構文 / MySQL 4.1 リファレンスマニュアル(
http://dev.mysql.com/doc/refman/4.1/ja/union.html) より