2

MySQLをデータベースとして使用するアプリケーション用に書き直しているレポートがあります。現在、レポートはphpからの多くの不平を言う作業を使用しています。これは、配列を作成し、それらを一時データベースに再格納してから、その一時DBから結果を生成します。

このすべてのコードの大部分を書き直すことの主な目標の1つは、古いコードの多くを単純化してクリーンアップすることであり、以下のプロセスを単純化できるかどうか、またはphpにクライアントへのデータ。

作成したシナリオを使用して、私がやろうとしていることを説明します。

次のテーブルを想定しましょう(実際のアプリでは、このテーブルの情報は実際にはいくつかのテーブルから取得されますが、わかりやすくするためにポイントを理解する必要があります)。

+----+-----------+--------------+--------------+
| id | location  | date_visited | time_visited |
+----+-----------+--------------+--------------+
| 1  | place 1   | 2012-04-20   | 11:00:00     |
+----+-----------+--------------+--------------+
| 2  | place 2   | 2012-04-20   | 11:06:00     |
+----+-----------+--------------+--------------+
| 3  | place 1   | 2012-04-20   | 11:06:00     |
+----+-----------+--------------+--------------+
| 4  | place 3   | 2012-04-20   | 11:20:00     |
+----+-----------+--------------+--------------+
| 5  | place 2   | 2012-04-20   | 11:21:00     |
+----+-----------+--------------+--------------+
| 6  | place 1   | 2012-04-20   | 11:22:00     |
+----+-----------+--------------+--------------+
| 7  | place 3   | 2012-04-20   | 11:23:00     |
+----+-----------+--------------+--------------+

必要なレポートでは、最初に各場所をリストし、次にその場所への訪問数をリストする必要があります。ただし、注意点と私にとってクエリを困難にしているのは、このレポートでカウントするために訪問のために満たされる時間間隔が必要であるということです。

例:特定の場所への訪問の間隔が10分であるとします。

最初のエントリは前のエントリがないため自動的にロックインされ、2番目のエントリは'place2'の他のエントリがまだないためロックインされます。ただし、3番目のエントリでは、場所1が最後にアクセスされた時刻がチェックされます。これは、定義された間隔(10分)よりも短いため、レポートはこのエントリを無視して次のエントリに移動します。

基本的に、時間間隔が最後のエントリからではなく、同じ場所からの最後のエントリからであるケースバイケースのシナリオをチェックしています。

レポートの結果は、最終的に次のようになります。

+----+-----------+--------+
| id | location  | visits |
+----+-----------+--------+
| 1  | place 1   | 2      |
+----+-----------+--------+
| 2  | place 2   | 2      |
+----+-----------+--------+
| 3  | place 3   | 1      |
+----+-----------+--------+

基本レベルでの現在の実装では、上記の結果セットを取得するために次の手順を実行します。

  1. MySQLクエリは、必要なすべての場所とそのIDのリストを含む1つの一時テーブルを作成します。
  2. MySQLクエリは、指定された時間枠内のすべての訪問データを選択し、それをPHPに渡します。
  3. PHPとMySQLは一時テーブルに訪問データを入力し、PHPはここでうなり声を上げます。
  4. MySQLは一時テーブルからデータを選択し、表示のためにクライアントに返します。

私の質問はです。MySQLだけでこれのほとんどを行う方法はありますか?私が見つけようとしているのは、selectステートメントを解析して上記の基準を満たす訪問のみを選択し、最後に場所ごとにグループ化してCOUNT(*)を提供するMySQLクエリを作成する方法です。各グループ。

私はそれが可能かどうか本当にわかりません、そしてそこにいるデータベースの達人の一人がこれを行う方法にいくらかの光を当てることができるかもしれないことを望んでいます。

4

2 に答える 2

2

わずかに異なる構造のテーブル(おそらく一時的なもの)があるとします。

CREATE TABLE  `visits` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `location` varchar(45) NOT NULL,
  `visited` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `loc_vis` (`location`,`visited`)
) ENGINE=InnoDB;

INSERT INTO visits (location, visited) VALUES
('place 1', '2012-04-20 11:00:00'),
('place 2', '2012-04-20 11:06:00'),
('place 1', '2012-04-20 11:06:00'),
('place 3', '2012-04-20 11:20:00'),
('place 2', '2012-04-20 11:21:00'),
('place 1', '2012-04-20 11:22:00'),
('place 1', '2012-04-20 11:23:00');

locationご覧のとおり、これには( 、 )のインデックスがありますvisited。次に、次のクエリはインデックスを使用します。つまり、インデックスの順序でデータを読み取り、期待した結果を返します。

SELECT
  location,
  COUNT(IF(@loc <> @loc:=location,
           @vis:=visited,
           IF(@vis + INTERVAL 10 MINUTE < @vis:=visited,
              visited,
              NULL))) as visit_count
FROM visits,
     (SELECT @loc:='', @vis:=FROM_UNIXTIME(0)) as init
GROUP BY location;

結果:

+----------+-------------+
| location | visit_count |
+----------+-------------+
| place 1  |           2 |
| place 2  |           2 |
| place 3  |           1 |
+----------+-------------+
3 rows in set (0.00 sec)

いくつかの説明

ソリューションの鍵は、SQLの機能的性質をフェードアウトし、MySQL実装の詳細を使用することです(彼らはそれが悪いと言っています、二度とそれをしないでください!!!)。

  1. テーブルにインデックス(列値の順序付き表現)があり、そのインデックスがクエリで使用される場合、テーブルのデータがインデックスの順序で読み取られることを意味します。

  2. GROUP BY操作は、インデックスの恩恵を受け(データはすでにそこにグループ化されているため)、該当する場合はインデックスを選択します。

  3. SQLのすべての集計関数(COUNT(*)特別な意味を持つものを除く)は各行をチェックし、値がNULLでない場合にのみ値を使用します(上記のCOUNT内の式は間違った条件に対してNULLを返します)

  4. 残りは、行のリスト(インデックスの順序で読み取られ、順序付けられますlocation asc, visisted asc)に対する手続き型反復のハッキーな表現です:場所が前の行と異なる場合、いくつかの変数を初期化します-私はそれを数えます、 not-間隔をチェックし、間違っている場合はNULLを返します。

于 2012-04-20T05:34:31.147 に答える
0

INSERT / SELECTステートメントを使用して、一時テーブルにデータを取り込むことができます。

マニュアルを参照してください。http://dev.mysql.com/doc/refman/5.0/en/insert-select.html

SELECTステートメントでGROUPBYを使用して、場所を絞り込みます。

COUNT操作として入力できるvisits列については、INSERT/SELECTの一部としても実行できる可能性があると思います。

マニュアルを参照してください。http://dev.mysql.com/doc/refman/5.1/en/counting-rows.html

したがって、SQLは次のようになります。

INSERT INTO temp 
    SELECT * FROM (
        SELECT *,COUNT('visits') 
             FROM source AS table1 
             GROUP BY location
             WHERE date_visited > xxxx AND date_visited < xxxx
        )
       AS table2

真剣に、それは私の頭から離れていますが、SQLをどのように構造化できるかについてのいくつかのアイデアを与えるはずです。ただし、適切なクエリを1つだけ使用してレポートを作成できる可能性があります。

于 2012-04-20T02:21:00.007 に答える