9

都市での各ユーザーのチェックインを保持するデータベーステーブルがあります。ユーザーが都市に何日滞在したか、次にユーザーが都市に何回訪問したかを知る必要があります(訪問は都市での連続した日数で構成されます)。

したがって、次の表があると考えてください(簡略化され、DATETIME同じユーザーと都市のみが含まれています)。

      datetime
-------------------
2011-06-30 12:11:46
2011-07-01 13:16:34
2011-07-01 15:22:45
2011-07-01 22:35:00
2011-07-02 13:45:12
2011-08-01 00:11:45
2011-08-05 17:14:34
2011-08-05 18:11:46
2011-08-06 20:22:12

このユーザーがこの都市行っ日数6です30.06、01.07、02.07、01.08、05.08、06.08

私はこれを使用してこれを行うことを考えましたSELECT COUNT(id) FROM table GROUP BY DATE(datetime)

次に、このユーザーがこの都市に行った訪問数について、クエリは3 30.06-02.07、01.08、05.08-06.08 返す必要があります。

問題は、このクエリをどのように作成すればよいかわからないことです。

どんな助けでも大歓迎です!

4

5 に答える 5

12

前日にチェックインがなかったチェックインを見つけることで、各訪問の初日を見つけることができます。

select count(distinct date(start_of_visit.datetime))
from checkin start_of_visit
left join checkin previous_day
    on start_of_visit.user = previous_day.user
    and start_of_visit.city = previous_day.city
    and date(start_of_visit.datetime) - interval 1 day = date(previous_day.datetime)
where previous_day.id is null

このクエリにはいくつかの重要な部分があります。

まず、各チェックインは前日のチェックインに参加します。ただし、これは外部結合であるため、前日にチェックインがなかった場合は、結合の右側にNULL結果が表示されます。フィルタリングはWHERE結合後に行われるため、左側からのチェックインのみが保持され、右側からのチェックインは保持されません。 物事がないLEFT OUTER JOIN/WHERE IS NULL場所を見つけるのに本当に便利です。

次に、ユーザーが訪問の初日に複数回チェックインした場合に二重にカウントされないように、個別のチェックイン日をカウントします。(エラーの可能性を見つけたときに、実際に編集時にその部分を追加しました。)

編集:最初の質問に対して提案されたクエリを読み直しました。クエリは、日付の数ではなく、特定の日付のチェックインの数を取得します。代わりに次のようなものが必要だと思います。

select count(distinct date(datetime))
from checkin
where user='some user' and city='some city'
于 2011-08-17T13:56:10.960 に答える
3

このコードをタスクに適用してみてください-

CREATE TABLE visits(
  user_id INT(11) NOT NULL,
  dt DATETIME DEFAULT NULL
);

INSERT INTO visits VALUES 
  (1, '2011-06-30 12:11:46'),
  (1, '2011-07-01 13:16:34'),
  (1, '2011-07-01 15:22:45'),
  (1, '2011-07-01 22:35:00'),
  (1, '2011-07-02 13:45:12'),
  (1, '2011-08-01 00:11:45'),
  (1, '2011-08-05 17:14:34'),
  (1, '2011-08-05 18:11:46'),
  (1, '2011-08-06 20:22:12'),
  (2, '2011-08-30 16:13:34'),
  (2, '2011-08-31 16:13:41');


SET @i = 0;
SET @last_dt = NULL;
SET @last_user = NULL;

SELECT v.user_id,
  COUNT(DISTINCT(DATE(dt))) number_of_days,
  MAX(days) number_of_visits
FROM
  (SELECT user_id, dt
        @i := IF(@last_user IS NULL OR @last_user <> user_id, 1, IF(@last_dt IS NULL OR (DATE(dt) - INTERVAL 1 DAY) > DATE(@last_dt), @i + 1, @i)) AS days,
        @last_dt := DATE(dt),
        @last_user := user_id
   FROM
     visits
   ORDER BY
     user_id, dt
  ) v
GROUP BY
  v.user_id;

----------------
Output:

+---------+----------------+------------------+
| user_id | number_of_days | number_of_visits |
+---------+----------------+------------------+
|       1 |              6 |                3 |
|       2 |              2 |                1 |
+---------+----------------+------------------+

説明:

それがどのように機能するかを理解するために、サブクエリをチェックしてみましょう。

SET @i = 0;
SET @last_dt = NULL;
SET @last_user = NULL;


SELECT user_id, dt,
        @i := IF(@last_user IS NULL OR @last_user <> user_id, 1, IF(@last_dt IS NULL OR (DATE(dt) - INTERVAL 1 DAY) > DATE(@last_dt), @i + 1, @i)) AS 

days,
        @last_dt := DATE(dt) lt,
        @last_user := user_id lu
FROM
  visits
ORDER BY
  user_id, dt;

ご覧のとおり、クエリはすべての行を返し、訪問数のランク付けを実行します。これは変数に基づく既知のランク付け方法です。行はユーザーフィールドと日付フィールドの順に並べられていることに注意してください。daysこのクエリはユーザーの訪問数を計算し、列が訪問数のランクを提供する次のデータセットを出力します-

+---------+---------------------+------+------------+----+
| user_id | dt                  | days | lt         | lu |
+---------+---------------------+------+------------+----+
|       1 | 2011-06-30 12:11:46 |    1 | 2011-06-30 |  1 |
|       1 | 2011-07-01 13:16:34 |    1 | 2011-07-01 |  1 |
|       1 | 2011-07-01 15:22:45 |    1 | 2011-07-01 |  1 |
|       1 | 2011-07-01 22:35:00 |    1 | 2011-07-01 |  1 |
|       1 | 2011-07-02 13:45:12 |    1 | 2011-07-02 |  1 |
|       1 | 2011-08-01 00:11:45 |    2 | 2011-08-01 |  1 |
|       1 | 2011-08-05 17:14:34 |    3 | 2011-08-05 |  1 |
|       1 | 2011-08-05 18:11:46 |    3 | 2011-08-05 |  1 |
|       1 | 2011-08-06 20:22:12 |    3 | 2011-08-06 |  1 |
|       2 | 2011-08-30 16:13:34 |    1 | 2011-08-30 |  2 |
|       2 | 2011-08-31 16:13:41 |    1 | 2011-08-31 |  2 |
+---------+---------------------+------+------------+----+

次に、このデータセットをユーザーごとにグループ化し、集計関数を使用します。'COUNT(DISTINCT(DATE(dt)))'-日数をカウントします' MAX(days)'-訪問数。これは、daysサブクエリのフィールド。

それがすべてです;)

于 2011-08-31T13:38:55.380 に答える
1

Devartが提供するデータサンプルとして、内部の「PreQuery」はSQL変数で機能します。@LUserをデフォルトで-1(存在しない可能性のあるユーザーID)に設定することにより、IF()テストは最後のユーザーと現在のユーザーの違いをチェックします。新しいユーザーが1の値を取得するとすぐに...さらに、最終日がチェックインの新しい日付から1日を超える場合、値は1になります。その後、後続の列は@LUserと@LDateは、次のサイクルでテストされたばかりの着信レコードの値になります。次に、外部クエリはそれらを合計し、Devartデータセットごとに最終的な正しい結果をカウントします。

User ID    Distinct Visits   Total Days
1           3                 9
2           1                 2

select PreQuery.User_ID,
       sum( PreQuery.NextVisit ) as DistinctVisits,
       count(*) as TotalDays
   from
      (  select v.user_id,
               if( @LUser <> v.User_ID OR @LDate < ( date( v.dt ) - Interval 1 day ), 1, 0 ) as NextVisit,
               @LUser := v.user_id,
               @LDate := date( v.dt )
            from 
               Visits v,
               ( select @LUser := -1, @LDate := date(now()) ) AtVars 
            order by
               v.user_id,
               v.dt  ) PreQuery
    group by 
       PreQuery.User_ID
于 2011-09-02T13:46:43.867 に答える
0

最初のサブタスクの場合:

select count(*) 
from (
select TO_DAYS(p.d)
from p
group by TO_DAYS(p.d)
) t
于 2011-08-17T13:53:50.413 に答える
0

データベース構造の変更を検討する必要があると思います。テーブル訪問とvisit_idをチェックインテーブルに追加できます。新しいチェックインを登録するたびに、前日にチェックインがあるかどうかを確認します。はいの場合は、昨日のチェックインのvisit_idを使用して新しいチェックインを追加します。そうでない場合は、新しい訪問に新しい訪問を追加し、新しいvisit_idを使用して新しいチェックインを追加します。

次に、次のような1つのクエリでデータを取得できます。 SELECT COUNT(id) AS number_of_days, COUNT(DISTINCT visit_id) number_of_visits FROM checkin GROUP BY user, city

これはあまり最適ではありませんが、現在の構造で何かを行うよりも優れており、機能します。また、結果を個別のクエリにできる場合は、非常に高速に動作します。

ただし、もちろん欠点は、データベース構造を変更し、スクリプトを追加して、現在のデータを新しい構造に変換する必要があることです(つまり、visit_idを現在のデータに追加する必要があります)。

于 2011-08-30T19:05:10.423 に答える