こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

UPDATE時にSETしたい日本語文字列が文字化けし、失敗する。

UPDATE時にSETしたい日本語文字列が文字化けし、失敗する。
POSTした時点では正しく表示されているのですが、(Printで確認)
UPDATEを実行しますと、日本語文字列が化けまして正常に実行できません。

他の箇所でも同様の、ユーザー入力の日本語文字列によるUPDATEを実施しており、
そちらは上手く言っています。
そちらとの違いは可変変数を使用しているか否かといった程度で、
それが原因とは少々考えにくいです。
また、仮に原因であったとしても解決法が私の技量・知識では見つけることが困難です。
まる1日悩んでいるのですが解決できないのでご教授いただきたく思います。

文字コードに関しては、MYSQLテーブル・項目はutf8_general_ci,
ソースはphpも、POST元のhtmlもUTF-8に統一しております。
ソースは下記のようなものを書いております。

<?php
session_start();
//編集する文書IDを取得
$seisansyo_id = $_POST['seisanconbo'];

//更新フラグを確認
$update_flg = $_POST['update_flg'];

//接続文字列
$url = "localhost";
$user = "USER_1";
$pass = "userpass";
$db = "garyu_db";

if($update_flg == 1){
 //ループカウンタ
 $r = 1;
 while($r <= 5 ) {
 $update_para1 = $_POST{"day_".$r};
 $update_para2 = $_POST{"youken_".$r};
 $update_para3 = $_POST{"line_".$r};
 $update_para4 = $_POST{"from_".$r};
 $update_para5 = $_POST{"to_".$r};
 $update_para8 = $_POST{"cost_".$r};

/* printして表示されるもの
 $update_para1 = 2010-3-22(日付)
 $update_para2 = 「打合せ」「外出」(ユーザー入力文字列)
 $update_para3 = 0~6の整数
 $update_para4 = 「出発点」「自宅」(ユーザー入力文字列)
 $update_para5 = 「到着店」「自社」(ユーザー入力文字列)
 $update_para8 = $_POST{"cost_".$r};

$link = mysql_connect($url,$user,$pass) or die("接続失敗。");
$sdb = mysql_select_db($db,$link) or die("データベースの選択失敗。");

// クエリを作成。※の行の、日本語文字列を省くと正常にUPDATEできる。
$query = "
  UPDATE transportation SET
REC_DATE = '".$update_para1."' ,
(※)USAGE = '".$update_para2."' ,
LINE = '".$update_para3."' ,
(※)START = '".$update_para4."' ,
(※)ARRIVE = '".$update_para5."' ,
COST = '".$update_para8."'
  WHERE SEISANSHO_ID = '".$seisansyo_id."' AND LINE_NO = '".$r."' ";
  $result = mysql_query($query);

// MySQLへの接続を閉じる
   mysql_close($link) or die("MySQL切断に失敗しました。");
   $r++;
}
}
?>

投稿日時 - 2010-03-21 05:22:21

QNo.5767722

すぐに回答ほしいです

質問者が選んだベストアンサー

>#5
ちょっと、話が逸れるけど。
`(バッククォート)は、MySQL限定でオブジェクト名(テーブル名、フィールド名等)が予約語と重なる際や、特殊文字を使用する場合などに用いられる、エスケープ文字のようなもの。
もちろん、オブジェクト名すべてに用いることは別に問題はないです。

とは言え、MySQL限定というのがクセモノで、他のDBではまた別のエスケープ文字が使われるわけで。
『すべてのオブジェクトをエスケープしましょう』というルールは、たとえば、PEAR::MDB2などの汎用インターフェイスの、『DBが変わった場合でもソースコードの修正個所は最小限に抑えられるように作る』というのと真逆のルールとも言えます。
もちろん、DBが変わった場合、DB固有の書き方に改めなければならない個所は出てきますが、すべてのSQLを書きかえる必要があるっていうよりは格段に少なくなるでしょう。

なので、仕事の現場では、『エスケープしなければならないオブジェクト名は付けてはいけない』というルールが一般的ですね。
(と言うよりも、当たり前すぎてわざわざそんな事明文ルール化せずに、やってしまった奴がこっぴどく怒られるだけだと思いますが。)
※逆にテーブル名は[T_],フィールド名は[F_]を頭に付けることをルール化して、上記を満たす場合もあります。
そのうえで、エスケープはしません。

仕事の現場で一般的にはって話なだけで、`(バッククォート)を付けるのが間違いと言うわけではないですが、趣味で個人でって人を含めても、かなりの少数派だとは言えます。

ちなみに、`(バッククォート)を入力するには、キーボードではShift+@です。

投稿日時 - 2010-03-21 14:26:06

補足

皆様の回答、ありがとうございます。
現在まだ理解、納得、解決が出来ていませんが、
解決に近づいているような気はしています。

解決後、再度報告に参ります。本当にありがとうございます。

投稿日時 - 2010-03-21 17:57:19

お礼

皆様お世話になっております。

詰まっていた処理の部分は解決する事ができ、
機能自体も期待動作を果たす事が出来ました。
未だに原因の特定は出来ませんでしたが、
文字列の形式と、クエリ文字列の記述の仕方に問題があったように思えます。

解決の手順はまず初めに
i80286さん、samchayさんの回答を参考に、
 $link1 = mysql_connect($url,$user,$pass,TRUE);
 mysql_selectdb($db,$link1);
 mysql_set_charset('utf8',$link1);
と、接続の直後にUTF8である事を明示する記述を追加し、

クエリ文は、
BellBellさん、yambejpさんの助言を下に、
1.phpMyAdminで目的とするUPDATE操作を実行
2.表示されるSQL文をそのまま貼り付け
3.シングルクォートで囲っている部分(項目名)が
  PHP,MYSQLの予約語と被らないかを再確認
4.変数部分等、最低限の変更(2.で表示されているシングルクォートもそのまま)

と、修正する事で解決できました。
phpMyAdminで表示されるSQL文は、
テーブル名や項目名を全てシングルクォートで囲っています。
これまでの開発で余り目にしなかったので、
なんとなく気持ち悪くて消してしまっていたのです。
無くても問題なく動く時もあれば、
今回のように、残さないと上手くいかない時もあります。

ここら辺の理解は、今後理解を深めていくか、
BellBellさんが申されているように
使わなくても問題が起きないように、
ネーミングを設計段階から徹底していく等の習慣を心がけたいと思います。

投稿日時 - 2010-03-22 11:54:23

このQ&Aは役に立ちましたか?

4人が「このQ&Aが役に立った」と投票しています

回答(6)

ANo.5

とにかく一度クォートでくくりましょうよ

>$sql = "UPDATE transportation SET USAGE = ".$update_para2." WHERE SEISANSHO_ID = ".$seisansyo_id." AND LINE_NO = ".$r." ";

$sql = "UPDATE `transportation` SET `USAGE` = '{$update_para2}' WHERE `SEISANSHO_ID` = '{$seisansyo_id}' AND `LINE_NO` = '{$r}' ";

投稿日時 - 2010-03-21 13:13:34

お礼

yambejpさま、回答ありがとうございます。
お礼が遅くなり申し訳ありません。

詳細・経緯はベストアンサーとしました、
BellBell様の回答にレスさせて頂きました

おかげさまで解決致しまして、現在実装できています。
解決の手がかりとなったのはyambejpさまの指摘に有りました
シングルクォートでした。
ありがとうございました。

投稿日時 - 2010-03-26 09:00:53

ANo.4

気になることで、原因として考えられなくもないのは、
mysql_real_escape_string
等を利用して、特殊文字をエスケープしていない事ですね。

特にShift-JIS環境で\マークをエスケープして文字化けを防ぐ事でも知られていますが、'をエスケープしてSQLインジェクションを防ぐことにも使用されます。
もし、2バイト文字列を1バイトに分解した場合に、'が含まれる可能性があればそのせいでクエリーが動作しない事も防ぐ事になります。
※2バイト文字中に'が含まれる問題、というのは寡聞にして聞いたことがありませんので、文字コードの関係上発生しないのかも知れません。

あと、直接問題とは関係ない事で気になる点が
ループの中でDBへの接続、切断を繰り返していること。
通常は、DBへの接続、および切断はループ外で一度ずつ行い、ループ内では確立された接続に対しての操作を行う事が普通です。
※無駄にDBへの接続、切断を繰り返すことで接続できないエラーが発生する可能性を高めているだけ。
しかもたとえば10回ループ中の、3回まで成功して途中で失敗するなどという可能性もあり、内容によっては致命的なシステムトラブルの可能性が。
さらに言えば、無駄に実行時に時間の掛かる処理(DB接続)を繰り返し行っているだけという見方もできます。

また、ループ外にDBを置く前提ですが、
mysql_close($link) or die("MySQL切断に失敗しました。");
一般にmysql_closeはエラーチェックしません。
仮に切断に失敗したとしても、PHP終了時にはDB接続は解放されるため。
※人によっては、DB接続の終了は自動解放に任せてmysql_closeそのものを呼び出さない事も多々あります。

ちなみに、私がどうしてもクエリーの間違いを見つけられない場合
クエリー発行直前にprintなどでクエリーを表示して、
そのクエリーに失敗した場合は、そのSQLをコピペでPHPMyAdminなどから実行してみる
そのエラーメッセージを元に、クエリーの間違いを探す。
間違いが見つかれば、その情報を元にソースコードを見直し
という手順を取る事があります。

最後に
USAGE,START,ARRIVEなどのフィールド名を間違えて書いているなんて事はないですよね?

投稿日時 - 2010-03-21 12:21:37

お礼

Bellbell様、毎度お世話になっております。

毎度、問題点以外での重要な指摘、参考になっております。
現在問題は解決しまして、全体のコードを整えている所です。
他にも無駄な接続などをしていた箇所があったか、
余計な処理をしていないか、見直し、すっきりした物にしようとしている所です。

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

投稿日時 - 2010-03-26 09:08:27

ANo.3

MySQL との接続で使われている文字コードセットが期待するものでない場合、文字化けしてしまいます。PHP、MySQL のバージョンに依存しますが、次の関数を利用して文字コードセットを指定することが出来ます。
http://jp.php.net/manual/ja/function.mysql-set-charset.php

もし MySQLi が利用できるのであれば、切り替えたほうがよいと思います。現在の接続の文字コードセットを確認できますので、より正確な情報が得られるでしょう。
http://jp.php.net/manual/ja/mysqli.character-set-name.php

また、クエリを準備する場合、適切なエスケープを施すことが大事です。これを行うためには、接続の文字コードセットが適切な手順で指定されることが必要となります。
「SET NAMES」は MySQL CLI クライアントで利用できますが、PHP から利用してはいけません。

外部からのデータを処理する方法や、データベースとの接続方法など、もう少し吟味すべき箇所があると思います。慎重な作業をされますことを願っております。

投稿日時 - 2010-03-21 11:31:53

お礼

samchayさま、回答ありがとうございます。
お礼が遅くなり申し訳ありません。
詳細・経緯はベストアンサーとしました、
BellBell様の回答にレスさせて頂きました

おかげさまで解決致しまして、現在実装できています。
解決の手がかりとなったのはクエリのくくり(クォート等)でした。
指摘頂きましたとおり、注意深く吟味する事で解決が出来た事と実感しております。
ありがとうございました。

投稿日時 - 2010-03-26 09:03:14

UPDATEクエリを投げる前に下記を実行してみてください。

$sql = "SET NAMES utf8";
mysql_query($sql);

投稿日時 - 2010-03-21 10:34:47

お礼

i80286さま、お世話になっております。
お礼が遅れて申し訳ありません。

Bellbell様の回答も書かせて頂きましたが
直接の原因はクエリ文のクォート、ダブルクォートの括りでした。
しかしながらそれ以前は文字化け=文字コードが原因というタカを括っていた為、
i80286様が指摘してくださった、UTF-8をはっきりと指示することで
「文字コードが原因ではない」と、原因を断定して絞っていく事が出来ました。
現在解決しまして、実装に至っております。
ありがとうございました。

投稿日時 - 2010-03-26 09:06:17

ANo.1

シングルクォーテーションで囲うと変数展開しないが
ダブルクォーテーションで囲うと変数展開する、じゃ無かったですかね?

USAGE = ".$update_para2." ,
にするとか。

投稿日時 - 2010-03-21 08:08:32

補足

bin-chan さんありがとうございます。
シングルくくりを初めとして、ダブルくくり、.の有無、.$等など…
いろいろ試しましたが上手く行かないようです。

今は、
$sql = "UPDATE transportation SET USAGE = ".$update_para2." WHERE SEISANSHO_ID = ".$seisansyo_id." AND LINE_NO = ".$r." ";

で、指定したクエリを実行して、エラー内容が、
クエリの送信に失敗しました。
SQL:UPDATE transportation SET USAGE = 謇灘粋 WHERE SEISANSHO_ID = 3 AND LINE_NO = 1

とでています。
「打合せ」だったUSAGEの中身が文字化けしています。

投稿日時 - 2010-03-21 09:32:44

あなたにオススメの質問