猫型iPS細胞研究所

Windows、Linux、iOS、DB、Network

DataBase MySQL

MySQLで相関サブクエリを実行する

投稿日:2013年7月10日 更新日:

相関サブクエリとは、SQLの実行結果の行と行を比較することです。
ある行とある行の時間差を求めることで何秒間隔でデータが登録されているかがわかったり、
ある行とある行の売上差を求めることで、売上の上がり下がりをだしたりできます。

相関サブクエリを知らなければ、
SELECTしたデータをもとに、
プログラムを書く必要がありますが、
データベースだけでもある程度のことはできてしまうのです!

ポイントは2点あります。

(ポイント1)必要なデータだけをソートして抜き出し、行番号を振る
このときに、ある行と次の行が比較対象となるようにソートしましょう。

(ポイント2)自己結合をして比較する
以上の2点です。

必要なデータだけをソートして抜き出し、行番号を振

必要なデータを抜き出すのは、whereで条件をつけます。
ソースするのはもちろんorder byです。
結果に行番号をつけることがポイントです。
Oracleではrownumものがありますが、MySQLではありません。
結果に行番号をつけるには(select @i:=0) とユーザー変数を利用します。

自己結合をして比較する

自己結合の詳細はここでは省略します。
自己結合した結果の行番号を1つずつずらして比較していくことがポイントです。

実際にやってみる

以下のようなテーブルから、
2013年06月14日分のデータがどのぐらいの間隔で登録されたかを確認します。
(サンプルがいまいちなのはわかっています・・・ :x001: )

mysql> select * from test;
+----+--------+---------------------+
| id | name   | insert_time         |
+----+--------+---------------------+
|  1 | ice    | 2013-06-13 09:10:11 |
|  2 | cake   | 2013-06-13 11:10:20 |
|  3 | apple  | 2013-06-13 13:11:05 |
|  4 | juce   | 2013-06-14 10:09:00 |
|  5 | pizza  | 2013-06-14 16:11:44 |
|  6 | orange | 2013-06-14 20:15:55 |
+----+--------+---------------------+
6 rows in set (0.00 sec)

ここから2013年06月14日分を抜き出して、その結果に行番号をつけると以下のようになります。
t1_rownum というのが、SELECTの結果に対してついた行番号です。
idとは違うということに注意してください。

SELECT
    t1.rownum as t1_rownum
   ,t1.id
   ,t1.insert_time
FROM
(
    select
        @i:=@i+1 as rownum,
        t.*
    from
        (select @i:=0)tmp
       ,test t
    where
        t.insert_time >= '2013-06-14'
    order by
        insert_time)t1;
+-----------+----+---------------------+
| t1_rownum | id | insert_time         |
+-----------+----+---------------------+
|         1 |  4 | 2013-06-14 10:09:00 |
|         2 |  5 | 2013-06-14 16:11:44 |
|         3 |  6 | 2013-06-14 20:15:55 |
+-----------+----+---------------------+
3 rows in set (0.00 sec)

自己結合して比較すると以下のようになります。

SELECT
    timediff(t2.insert_time,t1.insert_time)
FROM
(select @i:=@i+1 as rownum,t.* from (select @i:=0)tmp ,test t where t.insert_time >= '2013-06-14' order by insert_time)t1,
(select @j:=@j+1 as rownum,t.* from (select @j:=0)tmp ,test t where t.insert_time >= '2013-06-14' order by insert_time)t2
where
    t1.rownum = t2.rownum -1;
+-----------------------------------------+
| timediff(t2.insert_time,t1.insert_time) |
+-----------------------------------------+
| 06:02:44                                |
| 04:04:11                                |
+-----------------------------------------+
2 rows in set (0.00 sec)

データが少なすぎて分かりにくいかもしれませんが、
2013年06月14日分のデータ登録された間隔を表示しています。

最後のポイントとして、
自己結合する2つのユーザー変数は、別名の変数を使う必要があるということです。
この場合はiとjを使用しています。
同じiだと2つのテーブルを通した連番になってしまいます。

参考になったでしょうか? :x002:

Gooleアドセンス用336

Gooleアドセンス用336

-DataBase, MySQL

執筆者:

関連記事

sqlserver

application log ってどこ?

SQLSERVERのメンテナンスプランを使用してバックアップを取得していたのですが、取得できていませんでした。 SQL Serverログには下記メッセージが表示されていました。 BACKUP fail …

mysql

MySQL ERROR: Can’t connect to MySQL server on ‘xxx.xxx.x.xx’ (13)

MySQL ERROR: Can’t connect to MySQL server on ‘xxx.xxx.xxx.xxx’ (13) はSELinuxによる接続 …

mysql

MySQLのエラー、クエリ、スロークエリのログ

全て /etc/my.cnf の [mysqld] に追記してサーバーを再起動すれば出力されるようになります。 エラーログは開発時には警告も出力しておけばよいでしょう。 クエリログは全てのSELECT …

sqlserver

SQL Server Management Studio でテーブルが変更できない

私は以前はOracleをメインで使用し、そしてここ数年間はMySQLをメインで使用しています。SQL Serverは少しかじった程度といえるでしょう。 こんな私が選択したのはSQL Server Ma …

mysql

SQLで時間の重複をチェックする

同じ期間のデータは事前にチェックしてエラーにするという処理は、 あらゆる場面で想定されそうなことですが、 以外と難しかったのでここに方法をのこしておきます。 他にも素晴らしい方法があると思いますが、 …