開発メモ,主に補足by子連れ親父プログラマー

2010-08-31

jQueryでFCKEditorのvalueを取り出す

$.ajaxとかでpostしてたらFCKEditor部分の値が渡ってなかったので仕方なく。

$('iframe').contents().find('td#xEditingArea iframe').contents().find('body').html();

めちゃめちゃじゃないですか!

2010-08-12

window.openとかlocationとかsubmitとか、普通の昔から使ってたJavascriptをjQueryで書く

まあ、昔はみんな button に onclick つけたり、画像にjavascriptのリンク貼ったり、
して「戻る」ボタンにてた訳で。
大体もうデザイナーさんから上がってきた時点で、
<a href="javascript:history.back()"><img src="images/B.gif" width="170" height="36" alt="戻る" /></a>
こんなんになってたんだけども。
jQuery とか使い始めるとこういうソースがどうにも耐えられない。
html ソースの中に Javascript のコードが書いてあるのが許せない状態になってしまう。
ということで、これまではよく見かけたコードを書き換えるにはどうしたらいいか調べてみた。

まずはボタンクリックで特定のURLにリダイレクトさせるやつ。

こんなボタンがあるとして、
<input type="button" name="back" value="back" />
こんな感じっすか。
$(':button[name=back]').click(function() {
    window.location = 'index.php';
});
次、ボタンクリックで window.open サイズ指定も付けて。
$(':button[name=back]').click(function(e) {
    window.open('index.html','_blank',
        'width=360, height=500, menubar=no, toolbar=no, scrollbars=no');
    e.preventDefault();
    return false;
});
最後、ただ単にサブミットする。ついでに hidden の値を Javascript で指定。
こんなフォームがあるとして、
<form action="index.php" method="post">
<input type="hidden" name="key" value="" />
<input type="submit" />
</form>
こんな感じで。
$(':submit').click(function() {
    $(':hidden[name=key]').val('123');
    $('form').submit();
});
あと、追記だけども、HTML側で指定したフォームの飛び先(action)とは全然別のアドレスに飛ばしたい時もあるよね。
こんなんなってて、
<form action="index.php" method="post">
<input type="button" id="preview" value="preview" />
<input type="submit" />
</form>
プレビューボタンを押したら、preview.phpに飛ばしたい、っていう場合、
ついでにpostじゃなくてgetにしたい、っていう場合、
こうなる。
$(':button#preview').click(function() {
    $('form').attr('action', 'preview.php');
    $('form').attr('method', 'get');
    $('form').submit();
});

2010-08-11

phpとMySQLで、住所から郵便番号を検索する(逆引き)

大昔に会社のホームページ用に用意した原稿だけど、このまま埋もれてるのも何なのでこちらに載せておきます。

今回は、リアルな住所から郵便番号を検索するプログラムについて考えてみることにします。
例えば「札幌市清田区美しが丘一条N丁目M-1」というような、都道府県の部分だけを除いてあとは全部ひとつつながりの住所があるとする。
これがまあ、15万件くらいですかね、それくらいの件数の住所データを渡されて、「これの郵便番号を出せ」と、そういう指令が出たとする訳です。
さあどうしようかと。
話を簡単にするために、その住所データは以下のようなMySQLデータベースのテーブルに入っているとします。
CREATE TABLE users (
    id int(11) NOT NULL auto_increment,
    name varchar(100),
    address varchar(255),
    zip varchar(7),
    PRIMARY KEY (id)
)DEFAULT CHARSET=utf8;
住所はaddressのカラムで、郵便番号はzipですが、今はzipには何も入っていません。

で、まず考えるのは

日本郵便 http://www.post.japanpost.jp/zipcode/index.html

のページへ行って、郵便番号のデータをもらってくることです。ここに「郵便番号データのダウンロード」というリンクがありますのでクリックして、「全国一括」というやつをダウンロードします。
問題はその中味なのですが、詳しくは

郵便番号データの説明 http://www.post.japanpost.jp/zipcode/dl/readme.html

を読んでもらうことにして、この中で使えそうなのは8番目の市区町村名と9番目の町域名、そしてももちろん3番目の郵便番号(7桁)になりそうです。
その3つだけをインポートしてもよいのですが、それも面倒なので、せっかくのCSV形式のファイルですからそのままMySQLのLOAD DATAを使って一気に入れることにします。
最初に、以下のようなテーブルを作っておきます。
CREATE TABLE zips (
    zkd_code VARCHAR(255),
    kyu_zip VARCHAR(255),
    zip VARCHAR(255),
    ken_yomi VARCHAR(255),
    siku_yomi VARCHAR(255),
    cyou_yomi VARCHAR(255),
    ken VARCHAR(255),
    siku VARCHAR(255),
    cyou VARCHAR(255),
    fg_a INT,
    fg_b INT,
    fg_c INT,
    fg_d INT,
    fg_e INT,
    fg_f INT
);
次に、上記日本郵便のサイトでダウンロードした郵便番号データを適当なファイル名で(ここではyubin.csv)、サーバーに保存して、MySQLのコンソール画面から、
mysql> LOAD DATA LOCAL INFILE "/home/myhome/yubin.csv" INTO TABLE zips FIELDS TERMINATED BY ',' ENCLOSED BY '"';
と、やります。
これ一発で郵便番号データがzipsテーブルに入ります。

以上でデータは揃いましたので、usersテーブルの
+-------+--------------------------------------------------+
| id    | address                                          |
+-------+--------------------------------------------------+
|     1 | 札幌市清田区美しが丘一条N丁目M-1             |
+-------+--------------------------------------------------+
というデータと、zipsテーブルの
+---------+--------------------+--------------------+
| zip     | siku               | cyou               |
+---------+--------------------+--------------------+
| 0040811 | 札幌市清田区       | 美しが丘一条       |
| 0040813 | 札幌市清田区       | 美しが丘三条       |
| 0040812 | 札幌市清田区       | 美しが丘二条       |
| 0040815 | 札幌市清田区       | 美しが丘五条       |
| 0040814 | 札幌市清田区       | 美しが丘四条       |
+---------+--------------------+--------------------+
というようなデータを見比べて、郵便番号を照合するプログラムを組めばよいわけです。

zipsの方は市区町村名と町域名が別々のカラムになっていますので、 CONCAT を使って合体させて処理します。
一方リアルの住所には番地とか何丁目とかいうのが最後の方についていますので、それをどんどん削除していって、それが、 CONCAT で合体した市区町村名と町域名にマッチしたらその時の郵便番号を取得すればよい、という訳です。
文字を後ろから削除するには LEFT を使います。
SELECT 
    zip, siku, cyou 
FROM 
    zips
WHERE 
    CONCAT(siku, cyou) = LEFT('札幌市清田区美しが丘一条N丁目M-1', 10)
このクエリーはヒットしませんが、 LEFT の最後の数字を1ずつ増やして消していけば、いつかはヒットするはずです。

ということで、まずは対象となる住所データとその文字列長さを users テーブルから取得します。
$query = "SELECT address, CHAR_LENGTH(address) as len FROM users";
$result = mysql_query($query);
if ($result) {
    $n = mysql_num_rows($result);
    if ($n){
        while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
            for($i=$row['len'];$i>0;$i--){
                // ここに郵便番号テーブルとの照合を入れる
            }
        }
    }
}
forループを使って、最初に数えた文字列長さから始めて1文字すつ削っていこうという作戦です。
そして forループの中に以下の照合処理を入れます。
$query2 = "SELECT zip FROM zips WHERE CONCAT(siku, cyou) = LEFT('$address', $i)";
$result2 = mysql_query($query2);
if ($result2) {
    $n2 = mysql_num_rows($result2);
    if ($n2){
        while ($row2 = mysql_fetch_array($result2, MYSQL_ASSOC)) {
            echo $row2['zip'] . "\t";
        }
        break;
    }
}
breakを使って、マッチした時点で forループを抜けます。

で、これでテストしてみるとすぐに分かりますが、処理が大変に遅いです。私の試算では15万件のデータを処理するのに、18時間かかると出ました(笑)。
これはおそらく CONCAT でカラムを合体させてるのが原因でしょう。
ということで、市区町村名と町域名をバラバラにしておく意味はないので、カラムとして合体させます。
ALTER TABLE zips ADD COLUMN sikucyou VARCHAR(52);
UPDATE zips SET sikucyou=CONCAT(siku, cyou);
最初に新しいカラムを追加して、UPDATE をかけています。
ついでにインデックスも追加しておきます。
ALTER TABLE zips ADD INDEX (sikucyou);
PHPソース内のSQLの方も修正します。
SQLの CHAR_LENGTH もめためたに遅いのでPHP側での処理に変更します。
$query = "SELECT id, address FROM users";
$result = mysql_query($query);
if ($result) {
    $n = mysql_num_rows($result);
    if ($n){
        while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
            $address = mysql_real_escape_string($row['address']);
            $len = mb_strlen($row['address']);
            $zip = '';
            for($i=$len;$i>0;$i--){
                $query2 = "SELECT zip FROM zips WHERE sikucyou = LEFT('$address', $i)";
                $result2 = mysql_query($query2);
                if ($result2) {
                    $n2 = mysql_num_rows($result2);
                    if ($n2){
                        while ($row2 = mysql_fetch_array($result2, MYSQL_ASSOC)) {
                            $zip = $row2['zip'];
                        }
                        break;
                    }
                }
            }
            $query3 = "UPDATE users SET zip='$zip' where id='{$row['id']}'";
            $result3 = mysql_query($query3);
        }
    }
}

実行してみましょう。私の環境では15万件の住所の照合は、2分で終わりました。
問題はこの方法でやった郵便番号が本当に全部合っているのか、ということですが、全体の約14%は郵便番号を照合することができません。
リアルの住所自体には大字(おおあざ)などの表記のゆれがあるのと、それと、この方法では町域が「以下に掲載がない場合」の判別ができないからです。
照合できなかった住所でGoogle郵便番号検索をしてみましょう。
これはちゃんとヒットしますが、
郵便番号 渋谷区桜丘町
これはヒットしません。
郵便番号 千代田区丸の内
Google先生でも無理なんです。

2010-08-07

GROUP BYを使った表の数値の合計あって、その合計が10個以上とかいう条件で抽出したい時にHAVING句を使う

例えばメンバーIDがあって、それに紐付いたポイント数が複数保存してあるようなテーブルがあるとする。
 member_id | point
-----------+-------
         4 |     2
         4 |     2
         4 |     3
         4 |     3
         4 |     3
         4 |     1
         4 |     3
         4 |     2
         8 |     2
         8 |     0
         4 |     1
         8 |     3
         4 |     6
         8 |     0
これをGROUP BY を使って合計を出すと、
SELECT member_id,SUM(point) AS sum FROM points 
GROUP BY member_id;

 member_id | sum
-----------+-----
         4 |  43
         8 |   7
こんな結果になるわけですが、この結果表をさらに、合計が10個以上、とかそういう条件で絞り込みたい訳です。
もしかしてこんなのあり?
SELECT member_id, SUM(point) AS sum FROM points 
WHERE sum > 10 
GROUP BY member_id;
とかやってみましたが、これは、
ERROR:  column "sum" does not exist
になります。だめです。
ということで、HAVING句を使って、
SELECT member_id, SUM(point) AS sum FROM points 
GROUP BY member_id
HAVING SUM(point) > 10;
というように書きます。
結果。
 member_id | sum
-----------+-----
         4 |  43

Javascriptで配列の後ろからn個だけ要素を削除する

だいたいどこのサイトでも要素の削除にはspliceを使え、マイナスにすれば後ろから削除できる、と書いてあるんだが、そのマイナスの意味がよく分からないので実験してみた。
基本的にはこんな風にしておいて、アラートで結果を確認することとしよう。
items = new Array("1", "2", "3", "4", "5", "6");
items.splice(1, 2);
for(i = 0; i < items.length; i++){
   alert(items[i]);
}
これの結果は、
("1", "4", "5", "6")
じゃあこうすると、
items.splice(-1, 2);
こうなる。
("1", "2", "3", "4", "5")
どうもマイナスにすると、一番最後から数えるってだけのようです。
つまり、一番最後から3個削除したいんだったら、
items.splice(-3, 3);
こうすればいいと。これを
items.splice(-3);
こう書くと、Firefoxやchoromeでは同じく3個削除で動くがIEでは全く削除されない。

このブログを検索

Powered by Blogger.

ラベル

php (17) jQuery (13) OSX (10) MySQL (8) Javascript (7) Postgres (7) port (7) apache (6) Java (3) Smarty (2) html (2) pear (2) FCKEditor (1) XAMPP (1) css (1) git (1) perl (1) ruby (1)

Facebookバナー