0

ちょっと難しい SQL の問題があります。次のようなページビューのテーブルがあるとします。

CREATE TABLE pageviews (
  id          INT(11) NOT NULL AUTO_INCREMENT,
  user_id     INT(11) NOT NULL,
  timestamp   DATETIME NOT NULL,
  PRIMARY KEY (id)
)

このテーブルには、非常に多数のレコード (> 1 億) があります。このデータから、次のような別のテーブルを生成します。

CREATE TABLE sessions (
  id          INT(11) NOT NULL AUTO_INCREMENT,
  user_id     INT(11) NOT NULL,
  started_at  DATETIME NOT NULL,
  ended_at    DATETIME NOT NULL,
  PRIMARY KEY (id)
)

ルールは、セッションとは、30 分を超えるギャップを含まない、任意の数のページビューの任意のシーケンスであるということです。

これで、ループを使用してセッションを取得するストアド プロシージャを使用して、このテーブルを生成できました。

DELIMITER |
CREATE PROCEDURE generate_sessions() 
BEGIN
  TRUNCATE sessions;

  INSERT INTO sessions
  SELECT NULL, p.user_id, p.timestamp, p.timestamp FROM pageviews p
  LEFT JOIN pageviews2 p2 ON p2.user_id = p.user_id AND p2.timestamp > p.timestamp AND p2.timestamp < DATE_ADD(p.timestamp, INTERVAL 30 MINUTE)
  WHERE p2.id IS NULL;

  REPEAT    
    UPDATE sessions s
    LEFT JOIN pageviews p ON p.user_id = s.user_id AND p.timestamp < s.started_at AND p.timestamp > DATE_SUB(s.started_at, INTERVAL 30 MINUTE)
    SET s.started_at = p.timestamp
    WHERE p.id IS NOT NULL;
  UNTIL ROW_COUNT() = 0 END REPEAT;
END |

基本的に、この手順では、最初に任意のセッションの最新のページビューを取得し、それをテーブルに挿入してから、すべてのセッションが完了するまで繰り返しバックトラックします。

言うまでもなく、これは信じられないほど遅いです。誰でもより良い解決策、できれば1つのクエリのみを含む解決策を持っていますか?

4

1 に答える 1

0

これは MySQL では難しい問題です。これにはウィンドウ関数が本当に必要です。

しかし、方法はあります。まず、各セッションを定義する必要があります。このために、ページビュー間の 30 分を超えるギャップを見つけます。次のクエリは逆方向に検索するため、これは と呼ばれPrevSessionEndます。

次に、時間が増加しているため、特定のページ ビューと同じかそれ以前に発生したユーザーのすべてのページ ビューに対して、この値の最大値を選択します。その結果、すべてのページ ビューがセッション全体で一定の値を取得するはずです。1 つ目は NULL、2 つ目は最初のセッションの最大タイム スタンプなどです。

次に、この量でグループ化します。

select USER_ID, MIN(timestamp) as started_at, MAX(timestamp) as ended_at
from (select pv.*,
             (select MAX(prevSessionEnd)
              from (select pv.*,
                           (select timestamp
                            from pageviews pv2
                            where pv2.useid = pv.user_id and pv2.timestamp < pv.timestamp and
                                  (pv.timestamp - pv2.timestamp) > 0.5/24
                            order by timestamp
                            limit 1
                          ) as PrevSessionEnd
                    from pageviews pv
                   ) pv2
              where pv.user_id = pv2.user_id and pv2.timestamp <= pv.timestamp
             ) as SessionGrouper
      from pageviews pv
     ) pv
group by user_id, SessionGrouper

この特定のクエリはテストされていないため、構文エラーがある可能性があります。

最終回はお任せinsertします。

にインデックスがある場合、これは順番に高速に実行されpageviews(user_id, timestamp)ます。サブクエリは、このインデックスを使用してのみ解決できます。

于 2013-02-21T14:52:41.730 に答える