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

ラベル MySQL の投稿を表示しています。 すべての投稿を表示
ラベル MySQL の投稿を表示しています。 すべての投稿を表示

2012-09-14

mysqli(MySQL 改良版拡張モジュール)を使う場合のサンプルいろいろ

mysqli(MySQL 改良版拡張モジュール)を使う場合のサンプルいろいろ


いやあよもやね、mysql_real_escape_string とかが deprecated になる日がくるとは思わんかったね。
まずいでしょう、これは。まじで。

error_reporting(0);

にしとかないとあっという間にサーバーのログがパンクするんじゃないか・・・。

ま、それはともかく、mysqli に書き換えないといけない。
いや、そもそも mysqli でいいのか? PDO っつーのもあるぞ、って話なんだが。

俺は mysqli でいく。

まずは接続。

// データベース接続
$dbh = new mysqli("localhost", "username", "password", "db_name");
if ($dbh->connect_error) {
    die('Db Connect Error: '.$dbh->connect_errno.':'.$dbh->connect_error);
}
$dbh->set_charset('utf8');

id= で一個だけのデータをセレクトする。

$sth = $dbh->stmt_init();
$sql = "SELECT id, name FROM users WHERE id = ?";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('s', $id);
    $id = '1';
    $sth->execute();
    $result = $sth->get_result();

    while ($row = $result->fetch_assoc()) {
        echo $row['id']."\n";
        echo $row['name']."\n";
    }
    $sth->close();
}

同じsqlをセレクトする値を変えて複数回実行する。

$sth = $dbh->stmt_init();
$sql = "SELECT id, name FROM users WHERE id = ?";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('s', $id);
    $id_array = array('1', '2');
    foreach($id_array as $id) {
        $sth->execute();
        $result = $sth->get_result();

        while ($row = $result->fetch_assoc()) {
            foreach($row as $k => $v) {
                echo "$k $v";
            }
        }
    }
    $sth->close();
}

クエリの結果の行数を取得してなんかする。

$sth = $dbh->stmt_init();
$sql = "SELECT id, name FROM users";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->execute();
    $sth->store_result();
    if ($sth->num_rows == '3') {
        $sth->bind_result($id, $name);
        while ($sth->fetch()) {
            echo "$id, $name\n";
        }
        printf("Number of rows: %d.\n", $sth->num_rows);
    }
    $sth->free_result();
    $sth->close();
}


クエリでINを使う。

$sth = $dbh->stmt_init();
$sql = "SELECT id, name FROM users WHERE id IN (?, ?)";
if (! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $list = array('1','3');
    $sth->bind_param('ii', $list[0], $list[1]);
    $sth->execute();
    $sth->store_result();
    $sth->bind_result($id, $name);
    while ($sth->fetch()) {
        echo "$id, $name\n";
    }
    $sth->free_result();
    $sth->close();
}

使えねーなこりゃ。

次、LIKE検索。

$sth = $dbh->stmt_init();
$sql = "SELECT id, name FROM users WHERE name LIKE ?";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('s', $param);
    $myname = "理";
    $param = "%".$myname."%";
    $sth->execute();
    $sth->store_result();
    $sth->bind_result($id, $name);
    while ($sth->fetch()) {
        echo "$id, $name\n";
    }
    $sth->free_result();
    $sth->close();
}

一行だけ更新。

$sth = $dbh->stmt_init();
$sql = "UPDATE users SET name = ? WHERE id = ?";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('ss', $myname, $myid);
    $myname = "里香";
    $myid = "3";
    $sth->execute();
    printf("%d Row affected.\n", $sth->affected_rows);
    $sth->close();
}

ループで複数行更新。

$sth = $dbh->stmt_init();
$sql = "UPDATE users SET name = ? WHERE id = ?";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('ss', $myname, $myid);
    $id_array = array('8'=>'佐藤', '2'=>'山田');
    foreach($id_array as $myid => $myname) {
        $sth->execute();
    }
    printf("%d Row affected.\n", $sth->affected_rows);
    $sth->close();
}

削除。

$sth = $dbh->stmt_init();
$sql = "DELETE FROM users WHERE id = ?";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('s', $id);
    $id = "2";
    $sth->execute();
    printf("%d Row affected.\n", $sth->affected_rows);
    $sth->close();
}

インサートとインサートしたIDの取得。

$sth = $dbh->stmt_init();
$sql = "INSERT INTO users (name, email) VALUES (?, ?)";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('ss', $myname, $myemail);
    $myname = "歌代";
    $myemail = "xxxx@xxxx.xxx";
    $sth->execute();

    // INSERTED_ID の取得
    printf("%d Inserted.\n", $dbh->insert_id);// こっち
    printf("%d Inserted.\n", $sth->insert_id);
    $sth->close();
}

インサート処理でエラー処理を付ける。

$sth = $dbh->stmt_init();
$sql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?)";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('iss', $myid, $myname, $myemail);
    $myid = "2";
    $myname = "歌代";
    $myemail = "xxxx@xxxx.xxx";
    if ( ! $sth->execute()) {
        die('failed'.$sth->error);
    }
    $sth->close();
}

トランザクションで失敗したらロールバック。

$dbh->autocommit(FALSE);
$sth = $dbh->stmt_init();
$sql = "INSERT INTO myusers (name, email) VALUES (?, ?)";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('ss', $myname, $myemail);
    $myname = "夢";
    $myemail = "xxxx@xxxx.xxx";
    if ( ! $sth->execute()) {
        $dbh->rollback();
    } else {
        $dbh->commit();
    }
    $sth->close();
}

うーん、、こんなとこかー。


2012-08-17

OS X Mountaion Lion で MacPorts を使って開発環境を整える(MySQL編)

OS X Mountaion Lion で MacPorts を使って開発環境を整える(MySQL編)


引き続きMySQLのインストールへ。
まずは、

$ sudo port install mysql5-server

終わり際に、こんなメッセージと、

###########################################################
# A startup item has been generated that will aid in
# starting mysql5-server with launchd. It is disabled
# by default. Execute the following command to start it,
# and to cause it to launch at startup:
#
# sudo port load mysql5-server
###########################################################

こんなメッセージが出る。

If this is a new install, in order to setup the database you might want to run:
sudo -u _mysql mysql_install_db5

悩ましいね。
とりあえず、

$ sudo port load mysql5-server

なにも起きない。
続いて、

$ sudo -u _mysql mysql_install_db5

と入れてみると、

Installing MySQL system tables...
OK
Filling help tables...
OK

To start mysqld at boot time you have to copy
support-files/mysql.server to the right place for your system

PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !
To do so, start the server, then issue the following commands:

/opt/local/lib/mysql5/bin/mysqladmin -u root password 'new-password'
/opt/local/lib/mysql5/bin/mysqladmin -u root -h bohr.local password 'new-password'

Alternatively you can run:
/opt/local/lib/mysql5/bin/mysql_secure_installation

which will also give you the option of removing the test
databases and anonymous user created by default.  This is
strongly recommended for production servers.

See the manual for more instructions.

You can start the MySQL daemon with:
cd /opt/local ; /opt/local/lib/mysql5/bin/mysqld_safe &

You can test the MySQL daemon with mysql-test-run.pl
cd /opt/local/mysql-test ; perl mysql-test-run.pl

Please report any problems with the /opt/local/lib/mysql5/bin/mysqlbug script!

だーっと何か出た。
さっぱりわからんのでとりあえず、

$ /opt/local/lib/mysql5/bin/mysql_secure_installation

をやってみる。

NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MySQL
      SERVERS IN PRODUCTION USE!  PLEASE READ EACH STEP CAREFULLY!


In order to log into MySQL to secure it, we'll need the current
password for the root user.  If you've just installed MySQL, and
you haven't set the root password yet, the password will be blank,
so you should just press enter here.

Enter current password for root (enter for none):
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/opt/local/var/run/mysql5/mysqld.sock' (2)

現在のrootのパスワードをいれろってメッセージが出るが設定してないじゃん。
設定してないから何もいれなくてもいいのか?
が、空でenterしても反応なし。
これは何かオカシイ。
なんか間違ってる。
ということでmac本体を再起動だ!
・・・・・
で、もう一回ここから。

$ /opt/local/lib/mysql5/bin/mysql_secure_installation

NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MySQL
      SERVERS IN PRODUCTION USE!  PLEASE READ EACH STEP CAREFULLY!


In order to log into MySQL to secure it, we'll need the current
password for the root user.  If you've just installed MySQL, and
you haven't set the root password yet, the password will be blank,
so you should just press enter here.

Enter current password for root (enter for none):
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
Enter current password for root (enter for none):
OK, successfully used password, moving on...

やっぱり、ただエンターキーだけ打てばいいってことらしい。
進む。

Setting the root password ensures that nobody can log into the MySQL
root user without the proper authorisation.

Set root password? [Y/n] y
New password:
Re-enter new password:
Password updated successfully!
Reloading privilege tables..
 ... Success!

引き続きrootのパスワード設定になったので、2回入れる。
成功!

By default, a MySQL installation has an anonymous user, allowing anyone
to log into MySQL without having to have a user account created for
them.  This is intended only for testing, and to make the installation
go a bit smoother.  You should remove them before moving into a
production environment.

Remove anonymous users? [Y/n] y
 ... Success!

Normally, root should only be allowed to connect from 'localhost'.  This
ensures that someone cannot guess at the root password from the network.

Disallow root login remotely? [Y/n] y
 ... Success!

By default, MySQL comes with a database named 'test' that anyone can
access.  This is also intended only for testing, and should be removed
before moving into a production environment.

Remove test database and access to it? [Y/n] y
 - Dropping test database...
 ... Success!
 - Removing privileges on test database...
 ... Success!

Reloading the privilege tables will ensure that all changes made so far
will take effect immediately.

Reload privilege tables now? [Y/n] y
 ... Success!

Cleaning up...



All done!  If you've completed all of the above steps, your MySQL
installation should now be secure.

Thanks for using MySQL!

あとは、なんじゃようわからんが全部yesで完了!
いやあ、こんな設定初めてやったかも。

$ /opt/local/lib/mysql5/bin/mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 10
Server version: 5.1.63 Source distribution

Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

オッケーだ。
パスが通っていないので、

$ vim ~/.profile

して、

export PATH=/opt/local/lib/mysql5/bin:$PATH

を追加。

次へ。

$ sudo port install php5-mysql

終わり際に、以下のメッセージが出るので、

To use mysqlnd with a local MySQL server, edit /opt/local/etc/php5/php.ini and set
mysql.default_socket, mysqli.default_socket and pdo_mysql.default_socket
to /opt/local/var/run/mysql5/mysqld.sock

php.ini を編集する。

$ sudo vim /opt/local/etc/php5/php.ini

で、default_socket の空になっている = に書きこむ。
PDO。

; Default socket name for local MySQL connects.  If empty, uses the built-in
; MySQL defaults.
; http://php.net/pdo_mysql.default-socket
pdo_mysql.default_socket = /opt/local/var/run/mysql5/mysqld.sock

mysql。

; Default socket name for local MySQL connects.  If empty, uses the built-in
; MySQL defaults.
; http://php.net/mysql.default-socket
mysql.default_socket = /opt/local/var/run/mysql5/mysqld.sock

mysqli。

; Default socket name for local MySQL connects.  If empty, uses the built-in
; MySQL defaults.
; http://php.net/mysqli.default-socket
mysqli.default_socket = /opt/local/var/run/mysql5/mysqld.sock

アパッチ再起動。

$ sudo /opt/local/apache2/bin/apachectl restart

infophp()でmysqlが入ってることを確認。
テスト用データベースを作成する。

mysql> CREATE DATABASE sample_db DEFAULT CHARACTER SET utf8;
Query OK, 1 row affected (0.00 sec)

ユーザー権限を設定。

mysql> GRANT ALL ON sample_db.* TO sample_user@localhost IDENTIFIED BY 'password';
Query OK, 0 rows affected (0.00 sec)

データベースを指定して、

mysql> use sample_db
Database changed

テーブルを作成。

mysql> CREATE TABLE users (
    -> id INT AUTO_INCREMENT,
    -> name varchar(255),
    -> PRIMARY KEY  (id)
    -> );
Query OK, 0 rows affected (0.04 sec)

テーブルを確認。

mysql> show tables;
+---------------------+
| Tables_in_sample_db |
+---------------------+
| users               |
+---------------------+
1 row in set (0.00 sec)

一個データをインサート。

mysql> INSERT INTO users (name) VALUES ('yamada');
Query OK, 1 row affected (0.00 sec)

セレクトして確認。

mysql> select * from users;
+----+--------+
| id | name   |
+----+--------+
|  1 | yamada |
+----+--------+
1 row in set (0.00 sec)

DBに接続してセレクトする簡単なphpを書く。
php.netからそのまま。

<?php
$link = mysqli_connect('localhost', 'sample_user', 'password', 'sample_db');
if (!$link) {
    die('Connect Error (' . mysqli_connect_errno() . ') '
            . mysqli_connect_error());
}
echo 'Success... ' . mysqli_get_host_info($link) . "\n";

if ($result = mysqli_query($link, "SELECT name FROM users")) {
    printf("Select returned %d rows.\n", mysqli_num_rows($result));

    /* 結果セットを開放します */
    mysqli_free_result($result);
}

mysqli_close($link);

画面に表示される。

Success... Localhost via UNIX socket Select returned 1 rows.

よさそうだ。


2011-12-26

OSX の MacPorts で入れた mysql-connector-java は一体どうやって使えばいいのか?

OSX の MacPorts で入れた mysql-connector-java は一体どうやって使えばいいのか?

引き続き java の servlet でのデータベース接続をテストしていた時のことである。
tomcat も MacPorts で入れたんだから Connector/J も当然 MacPortsで 入れるべきだろう、ということで、

sudo port install mysql-connector-java
こうした訳ですが、これはどうやら以下の場所に設置されるようです。

/opt/local/share/java/mysql-connector-java-5.0.jar

で、こいつをどう使ったらいいのか?
まずはコンパイルする時のために自分の.profile にパスを追加。
エディターで、

vim ~/.profile  
として、以下を追加。

export CLASSPATH=/opt/local/share/java/tomcat6/lib/servlet-api.jar
export CLASSPATH=/opt/local/share/java/mysql-connector-java-5.0.jar:$CLASSPATH
export CLASSPATH=$CLASSPATH:.

パスが通ったかどうか、

echo $CLASSPATH
して確認。

これでコンパイルは通る。

で、tomcat上で、作ったservletを実行するためには、

/opt/local/share/java/mysql-connector-java-5.0.jar
このファイルを、
/opt/local/share/java/tomcat6/webapps/sample/WEB-INF/lib
以下に置く(sampleは自分で作ったservlet置き場)。

もしくは、

/opt/local/share/java/tomcat6/lib/
以下に置く。

シンボリックリンクでもいけるので、

cd /opt/local/share/java/tomcat6/lib
sudo ln -s /opt/local/share/java/mysql-connector-java-5.0.jar mysql-connector-java-5.0.jar
こうしておけばよいかもしれない。

2011-01-18

MySQLで、ログイン用のメールアドレスなんかはバイナリー属性にしてテーブルを作る

なんかなー、デフォルトで大文字小文字区別しないってことが問題なんじゃないの?
って気はするけどなー。
普通にこういうテーブルを作る。
CREATE TABLE samples (
  id INT,
  email VARCHAR(255)
);

emailは大文字でも小文字でもヒットする。
バイナリー属性を付ける。
CREATE TABLE samples (
  id INT,
  email VARCHAR(255) binary
);
emailは大文字は大文字、小文字は小文字でヒットする。
show で見るとこんなになってる。

CREATE TABLE `samples` (
  `id` int(11) default NULL,
  `email` varchar(255) character set utf8 collate utf8_bin default NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8

ううむ。。。

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

2010-04-17

DISTINCT と ORDER BY を一緒に使う時の、PostgresとMySQLの違い

今やってるやつで、出てきた現象なのでちょっとメモ。
例えば items というようなテーブルがあって、id と name がある。
 id |  name
----+--------
  1 | test
  2 | sample
で、この items には複数のカテゴリーとサブカテゴリーが紐づけられている。
紐づけ用のテーブルは items_categories というようなもので、こんな感じになっている。
 item_id | category_id | sub_category_id | orders
---------+-------------+-----------------+--------
       1 |           1 |               1 |      2
       1 |           1 |               2 |      2
       2 |           1 |               2 |      1
アイテム1はサブカテゴリー1と2に、アイテム2はサブカテゴリー2に紐付いていて、どちらもカテゴリーは1だが、
そのカテゴリー1の中での並ぶ順番が orders ということで設定されている。
アイテム2が順番は1、アイテム1が2番と。

で、こいつをまず順番関係なしに、join してみると
SELECT ic.item_id, i.name 
FROM items_categories ic 
INNER JOIN items i ON ic.item_id=i.id
WHERE ic.category_id=1
こうなる。
 item_id |  name
---------+--------
       1 | test
       1 | test
       2 | sample
orders 順にしたいので、
SELECT ic.item_id, i.name 
FROM items_categories ic 
INNER JOIN items i ON ic.item_id=i.id
WHERE ic.category_id=1
ORDER BY ic.orders
とすると、
 item_id |  name
---------+--------
       2 | sample
       1 | test
       1 | test
これは動く。item_id が重複してるので、distinct を使うと、
SELECT DISTINCT ic.item_id, i.name 
FROM items_categories ic 
INNER JOIN items i ON ic.item_id=i.id
WHERE ic.category_id=1
ORDER BY ic.orders
Postgresの場合、エラーになる。
ERROR:  for SELECT DISTINCT, ORDER BY expressions must appear in select list
MySQLの場合、
 item_id |  name
---------+--------
       2 | sample
       1 | test
出る。
Postgresでも
SELECT DISTINCT ic.item_id, i.name, ic.orders 
FROM items_categories ic 
INNER JOIN items i ON ic.item_id=i.id
WHERE ic.category_id=1
ORDER BY ic.orders
とすればちゃんと出る。
 item_id |  name  | orders
---------+--------+--------
       2 | sample |      1
       1 | test   |      2
そりゃ、おかしいのはMySQLでしょう。この場合。

2010-03-16

XAMPPのインストールと設定

Windowsのphpのバージョンを5.3.2にアップしようとしたらば、apacheまで動かなくなったので、もうあきらめて新規にXAMPPでインストールすることに。

apache friends

ここから XAMPP Windows版 1.7.3 をダウンロードしてインストール。

最初に環境変数に書かれている既存のMySQLとかphpのパスを消去してからやった方がよかったみたいですね。
既存のそれらのプログラムとごっちゃになってしまって、その修正に丸一日かかってしまった。
そもそもMySQLがサービスとして動いていたようで、XAMPPはそっちを見に行ってしまった模様。
マニュアルには最初に「セキュリティ」からMySQLのrootのパスワードを設定せよ、と書いてあるが、既存のMySQLを見に行ってるので既にパスワードがある状態になっている。
こりゃおかしい、ということで既存のMySQLのサービスを無効にして、環境変数の方は
C:\xampp\mysql\bin;
に書き換え、これでようやく正常に戻る。
改めて「セキュリティ」から進んで、rootのパスワードを設定。
あとphpMyAdminのDB接続設定をするために C:\xampp\phpMyAdmin\config.inc.php にrootのパスワードを設定
/* Authentication type and info */
$cfg['Servers'][$i]['auth_type']            = 'config';
$cfg['Servers'][$i]['user']                 = 'root';
$cfg['Servers'][$i]['password']             = 'password';
$cfg['Servers'][$i]['AllowNoPassword']      = true;
それからMySQLの文字コード関係設定。C:\xampp\mysql\bin\my.ini を開いて一番下に、
[mysqld]
skip-character-set-client-handshake
default-character-set = utf8
character-set-server = utf8
collation-server = utf8_unicode_ci
init-connect = SET NAMES utf8

[mysql]
default-character-set=utf8
を追加。

次にapacheの設定。
C:\xampp\apache\conf\httpd.conf を開いて、Directory の
<Directory />
    Options FollowSymLinks
    AllowOverride none
    Order deny,allow
    Deny from all
</Directory>
を以下に変更
<Directory />
    Options FollowSymLinks
    AllowOverride all
    Order deny,allow
    Deny from all
</Directory>
ついでにバーチャルホストの設定。C:\xampp\apache\conf\extra\httpd-vhosts.conf を開いて、
以下のコメントアウトを外す
NameVirtualHost *:80
一番下に以下を追加
<VirtualHost *:80>
    ServerAdmin someone@somedomain
    DocumentRoot "D:/project"
    ServerName project
    ErrorLog "logs/project-error.log"
</VirtualHost>
<Directory "D:/project">
    Options FollowSymLinks
    AllowOverride all
    Order deny,allow
    Allow from all
</Directory>
ここで、最後の Allow from が all になってないと見られないので注意。
さらに、C:\Windows\System32\drivers\etc\hosts を開いて、
127.0.0.1       localhost
127.0.0.1       project
を追加。
あと php.ini のこのヘンを修正
[mbstring]
mbstring.language = Japanese
mbstring.internal_encoding = UTF-8
mbstring.http_input = pass
mbstring.http_output = pass
mbstring.encoding_translation = Off
mbstring.detect_order = UTF-8,SJIS,EUC-JP,JIS,ASCII
mbstring.substitute_character = none;
mbstring.func_overload = 0
最後にpearも既存のphpのパスが悪さしてへんてこりんな設定になっていたので、C:\xampp\php に go-pear を置いて、再インストール
php go-pear.php
これはコマンドプロンプトを管理者モードで実行しないとだめ。
あ、そうそう、あと、こうやってインストールした状態では、apacheの mod_rewrite は最初から使えるようになってるし、phpの short_open_tag はOffになってるぞ。

このブログを検索

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バナー