WHERE句のORの後ろはINDEXが効かない

タイトルでそのまま完結してしまっていますが、
ORを使用した場合、INDEXが効くのは左辺のみ

■「そもそもORを使うな」という話
■どうしても使わざるを得ないケースがあったとしても、クエリを二回実行するほうがベター


ということであります。



7.INはINDEXが有効でしかも結構速いが、ORは左辺と右辺のどちらかのみのINDEX適応(基本的には左辺)となる。
できるだけORを使うSQLを発行しなくてすむような設計にすることが望ましいですが、多くの要件では100%は難しいと思うので、その場合には、ANDで結合できる部分をできるだけ前に持ってきて、ORの後ろの条件をできるだけ簡潔なものとする(なぜなら原則としてORの後ろはINDEXが聞かないから)



 

データ更新時に、元データを別カラムに追記録していく(際の注意点も)

CONCAT関数が使える

mysql> UPDATE tbl SET 
status='NG',
up_time = NOW(), 
up_time_log = CONCAT(IFNULL(up_time_log,''),CONCAT(',',up_time)) 
WHERE id = ?

例えば、上記SQLのように更新時間をup_time_logカラムに全て残しておきたい場合(ログデータを取るほどでもない)
注意しておきたいのは、UPDATEが左から順次実行されて拾っていくこと

分かり易いのが、次の例で

mysql> UPDATE data SET price=price-1000, discount_rate=(price-1000)/price;

割引きした値段と、元値の割引率をセットしようとしても、割引率セットの前に既にpriceは1000円引かれているので、正しい値が入りません。
よって

オリジナルの値を変更する処理が入る場合は、それを一番最後に持ってくるのが鉄則

mysql> UPDATE data SET discount_rate=(price-1000)/price, price=price-1000;

なので、最初のSQL

mysql> UPDATE tbl SET 
status='NG',
up_time_log = CONCAT(IFNULL(up_time_log,''),CONCAT(',',up_time)), 
up_time = NOW() 
WHERE id = ?

となります。

MySQLリファレンス

ローディング中gifアニメーションを画面全体にかぶせる(オーバーレイ)


こういうやつを画面全体のレイヤーに被せる方法です。




実装方法は、色々な手法で紹介されていますが、
なるべくシンプルに、かつ汎用的に使えるようにしました。
colorboxやlightboxプラグインを使っているサイトであれば、より楽です。

css,js呼び出し

<link href="../css/colorbox.css" type="text/css" rel="stylesheet">
<script type="text/javascript" src="/js/jquery.colorbox.js"></script>

ローディング画像をの最初に持ってくる

http://loadinfo.net/などからいただきましょう)

<div id="loading"><img src="/img/loading.gif" width="48" height="48" alt="Loading..." /></div>
<div id="fade"></div><!-- colorboxなどを使っている場合は不要-->

CSS記述

#loading {
    width: 48px;
    height: 48px;
    display: none;
    position: fixed;
    _position: absolute; /* forIE6 */
    top: 50%;
    left: 50%;
    margin-top: -24px; /* harlf of height */
    margin-left: -24px; /* half of width */
    z-index: 10000; // cboxOverlayが9999になっているため
}

ex) Ajaxでデータを読み込むケース

	$("#loading").bind("ajaxSend", function(){
	  $(this).show();
	  $("#cboxOverlay").css("opacity", "0.3").show();
	}).bind("ajaxComplete", function(){
	    $("#cboxOverlay").fadeOut(100);
	    $("#loading").fadeOut(100); // hide()でもOK(好みで】
	});

colorboxなどを使っていない、もしくは使いたくない場合は、上記jsのコード内の

#cboxOverlay => #fade

と変えて、css

#fade {
    width: 100%;
    height: 100%;
    display: none;
    background-color: #FFFFFF; // 好みで
    opacity: 0.3;
    position: absolute;
    top: 0px;
    left: 0px;
    z-index: 50;
}

追加で完了




これで設置したいページヘ最小限で可能になります。
もっとシンプルな方法を御存知の方いらっしゃれば、おしえてくださいmm

foreach()の中で配列ポインタを動かしたいと思ったものの


prev(),next()のような配列の内部ポインタを操作する関数を使い、
foreachループの中で再度繰り返しやスキップなどをさせたい
(スキップはcontinueでもOKだけど)

しかし


foreach は内部の配列ポインタに依存するので、 ループ内で配列ポインタを変更すると予期せぬ振る舞いを引き起こします。

だと。
実際、foreachの第一引数で与えられる変数は、foreach内で直接操作されていないっぽい。


Note: Unless the array is referenced, foreach operates on a copy of the specified array and not the array itself. foreach has some side effects on the array pointer. Don't rely on the array pointer during or after the foreach without resetting it.


というわけで、現状foreachで内部ポインタの操作をするのは諦めるしかない

foreach($data as $key => $val){
    if($error)
       prev($data);  // doesn't work correctly
}
↓↓↓↓

while(list($key,$val) = each($data)){
    if($error)
       prev($data);
}

while()で対応する。

XML_Unserializerで配列の要素数が一つだと階層が崩れる問題

XMLの解析に便利なXML_Unserializer

<?
$UNSERIALIZER = new XML_Unserializer($XMLOPTIONS);
$UNSERIALIZER->unserialize($response_body);
$result = $UNSERIALIZER->getUnserializedData();

みたいに使います。

ところが一つ問題があって、

Array
(
    [item] => Array
        (
            [0] => Array
                (
                    [id] => 1001
                    [datetime] => 2012-02-08 19:13:51
                )
            [1] => Array
                (
                    [id] => 1020
                    [datetime] => 2012-02-08 22:52:49
                )

        )
)

のような[item]内に複数要素をもつ場合はそのまま取得できるけれど、

Array
(
    [item] => Array
        (
            [0] => Array
                (
                    [id] => 1001
                    [datetime] => 2012-02-08 19:13:51
                )
        )
)

のように、要素が一つだけの場合、getUnserializedData()して出力したら

print_r($data['item']);

  ↓

Array
(
    [id] => 1001
    [datetime] => 2012-02-08 19:13:51
 )

と配列の階層を一つ無くしてしまう。。。



これの対策として

<?
if(isset($data['item'][0])){

みたいな処理を入れるしかないのかを調査しました。



結論

$XML_OPTIONS

  'forceEnum'       => array('item')

を追加してあげればOKです。

Pearのマニュアルに書いてありますが、いまいち意味が不明
もう少しググる


要素の数によって配列の階層が変わってしまいます。これが以前から悩みの種で、 if (isset($item[0])) 〜 とかで処理の振り分けをしてはモヤモヤした気持ちを抱えていました。
おぉまったく同じプロセス!AZS!!


※補足

オプションの種類


XML_SERIALIZER_OPTION_INDENT

  • > インデントに使用する文字

XML_SERIALIZER_OPTION_LINEBREAKS

  • > 改行に使用する文字

XML_SERIALIZER_OPTION_TYPEHINTS

  • > 各要素の型をXMLに付与するかどうか(true,false)

XML_SERIALIZER_OPTION_XML_DECL_ENABLED

  • > XML宣言を入れるか入れないか(true,false)

XML_SERIALIZER_OPTION_XML_ENCODING

XML_SERIALIZER_OPTION_DEFAULT_TAG

  • > 不正なタグ名や、配列が渡されたときのデフォルトのタグ名

XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME

  • > オブジェクトをSerializeするとき、オブジェクト名をタグ名にするかどうか(true,false)

XML_SERIALIZER_OPTION_MODE

  • > Serializerの振る舞いの指定(XML_SERIALIZER_MODE_SIMPLEXMLを指定すると配列の部分は親要素のキーをタグ名に使用する)

XML_SERIALIZER_OPTION_ROOT_NAME

  • > トップレベルのタグ名を指定する

XML_SERIALIZER_OPTION_ROOT_ATTRIBS

  • > トップレベルタグの属性を指定する(配列)

XML_SERIALIZER_OPTION_ATTRIBUTES_KEY

  • > 属性名を指定する(配列)

XML_SERIALIZER_OPTION_CONTENT_KEY

  • > 要素名を指定する(配列)

XML_SERIALIZER_OPTION_COMMENT_KEY

  • > コメント名を指定する(配列)

XML_SERIALIZER_OPTION_NAMESPACE

XML_SERIALIZER_OPTION_ENTITIES

  • > エンティティを変換するかどうか(true,false)

XML_SERIALIZER_OPTION_RETURN_RESULT

  • > Serializeに成功したかどうかの戻り値を返すかどうか(true,false)

XML_SERIALIZER_OPTION_IGNORE_NULL

  • > NULL値の要素を無視するかどうか(true,false)

mysqlimport: Error: 13, Can't get stat of 'file.txt' (Errcode: 2), when using table:

今回も備忘(たぶん3回目くらい)
ファイルからテーブルにMySQLインポートしようとすると、

mysqlimport -h host hoge -u user -p /path/file.txt
mysqlimport: Error: 13, Can't get stat of 'file.txt' (Errcode: 2), when using table:

というエラー


原因は

 --local, -L

クライアントホストからインプットファイルをローカルで読み込む。

この-localオプションを指定しないと、デフォルトではサーバホストのファイルを探しにいってしまう

http://dev.mysql.com/doc/refman/5.1/ja/mysqlimport.html



というわけで、

mysqlimport -h host hoge -u user -p -L /path/file.txt

で解決