2012-12-14 6 views
0

2 일 이상 동안 데이터베이스 테이블에 로그인 한 순 사용자 수를 반환하기 위해 누군가가 SQL로 도움을 줄 수 있는지 궁금합니다. (7 일을 참조로 사용합시다).주간 활성 사용자 로그에서 매일

내 로그 테이블에는 각 행에 타임 스탬프 (ts)와 user_id가 포함되어 있으며 그 시간에 해당 사용자의 활동을 나타냅니다. 이제

SELECT FLOOR(ts/86400) AS day, COUNT(DISTINCT user_id) AS dau 
FROM log 
GROUP BY day ORDER BY day ASC 

의 내가이 단일 쿼리에 추가 할 말 (또는 적어도 가장 효율적인 가능한 방식으로 검색)하자 :

다음 쿼리는이 로그에서 일일 사용자 또는 DAU를 반환 주간 활성 사용자 또는 7 일 동안 기록 된 총 고유 사용자 그러나, 나는 겹치지 않는 주에서 나의 시간을 나누고 싶지 않다. 내가 필요한 것은 매일 그 날과 6 일 전의 뚜렷한 user_ids를 세는 것입니다. 예를 들어

는 :

day users wau 
1 1,2 2 
4 1,3 3 
7 3,4,5 5 
8 5  4 (user_id 2 lost from count) 
15 2  2 (user_ids 1,3,4 lost from count) 

사용자가 제공하고 추가 설명이 필요한 경우 주석을 통해 문의 주시기 바랍니다 수있는 모든 도움을 주셔서 감사합니다.

답변

3

"주간 평균 사용자 수"(귀하의 사양을 이해할 때마다 ... "매일 해당 일과 이전 6 일 동안 본 고유 한 user_ids 수")를 얻으려면 해당 줄에 대한 쿼리 아래에있는 것 중 하나를 사용할 수 있습니다. (쿼리는 또한 "일 평균 사용자"수를 반환

SELECT d.day 
    , COUNT(DISTINCT u.user_id) AS wau 
    , COUNT(DISTINCT IF(u.day=d.day,u.user_id,NULL)) AS dau 
    FROM (SELECT FLOOR(k.ts/86400) AS `day` 
      FROM `log` k 
      GROUP BY `day` 
     ) d 
    JOIN (SELECT FLOOR(l.ts/86400) AS `day` 
       , l.user_id 
      FROM `log` l 
      GROUP BY `day`, l.user_id 
     ) u 
    ON u.day <= d.day 
    AND u.day > d.day - 7 
GROUP BY d.day 
ORDER BY d.day 

는 (나는 아직이의 테스트를 실행하지 않은;..하지만 나중에 것이며, 어떤 수정이 필요한 경우, 나는이 문장을 업데이트합니다)

이 쿼리는 주어진 날 (u rowsource에서)의 사용자 목록을 로그 테이블 (d rowsource)의 일 집합에 합류합니다. 조인 조건 자에 나타나는 리터럴 "7" ON 절), 이는 이전 6 일간 사용자 목록을 "일치"시키는 것입니다.

예를 들어, SELECT 목록에 다른 표현식을 추가하여 지난 3 일 동안 별개의 사용자 수를 얻습니다.

 , COUNT(DISTINCT IF(u.day<=d.day AND u.day>d.day-3,u.user_id,NULL)) AS 3day 

더 큰 범위를 얻으려면 리터럴 "7"을 증가시킬 수 있습니다. 위의 표현에서 문자 3은 임의의 일 수를 얻기 위해 변경 될 수 있습니다 ... 우리는 u에서 각 행에 결합 된 전날 행 (d)을 충분히 확보해야합니다.

성능 참고 : 인라인 뷰 (또는 파생 테이블, MySQL이 호출하기 때문에) 때문에 이러한 인라인 뷰에 대한 결과 집합은 중간 MyISAM 테이블로 구체화되어야하므로이 쿼리는 매우 빠르지 않을 수 있습니다.

u으로 앨리어싱 된 인라인보기는 최적이 아닐 수 있습니다. 로그 테이블에 직접 결합하는 것이 더 빠를 수도 있습니다. 주어진 날에 대한 고유 한 사용자 목록을 얻는 관점에서 인라인보기의 해당 쿼리가 나에게 어떤 생각인지 생각하고있었습니다. 무슨 일이 일어나고 있는지 개념화하는 것이 더 쉬웠습니다. 그리고 나는 당신이 같은 날에 수백 명의 동일한 사용자를 입력했다면 인라인 뷰는 우리가 다른 날과 합류하기 전에 복제물 전체를 제거 할 것이라고 생각했습니다. 반환 할 일수를 제한하는 WHERE 절은 ud 인라인보기 내에 가장 잘 추가됩니다.합니다 (d 인라인 뷰는 이전 추가 육일을 포함해야합니다.)


또 다른 메모에서, TS 열이 TIMESTAMP 데이터 타입의 경우, 내가 날짜 부분을 추출하는 DATE(ts) 표현을 사용하는 경향이있을 것이다. 그러나 사용자가 지정한 결과 집합에서 다른 것 정수가 아닌, 결과 집합에 날짜 데이터 유형을 반환합니다.)

SELECT d.day 
    , COUNT(DISTINCT u.user_id) AS wau 
    , COUNT(DISTINCT IF(u.day=d.day,u.user_id,NULL)) AS dau 
    FROM (SELECT DATE(k.ts) AS `day` 
      FROM `log` k 
      GROUP BY `day` 
     ) d 
    JOIN (SELECT DATE(l.ts) AS `day` 
       , l.user_id 
      FROM `log` l 
      GROUP BY `day`, l.user_id 
     ) u 
    ON u.day <= d.day 
    AND u.day > DATE_ADD(d.day, INTERVAL -7 DAY) 
GROUP BY d.day 
ORDER BY d.day 
다음
+0

매우 완벽한 답변을 주셔서 감사합니다. ts는 bigint입니다. 첫 번째 쿼리는 효율성에 관계없이 완벽하게 작동합니다 (현재로서는 충분히 효율적입니다). – Protected

2

하나는, 날짜 날짜를 사용해야하는 이유의 또 다른 좋은 예입니다 또는 시간 소인 필드 유형을 사용하여 유닉스 시간 소인이 아닌 데이터베이스의 시간 값을 나타낼 수 있습니다. 항상 정수 필드 값에 대한 본래의 개념이 없으므로 일정 기간을 기준으로 쿼리해야하기 때문에 누군가가 실제로 필드에 대해 쿼리하고 싶어하므로 정수 타임 스탬프 값에 시간 개념에 대한 개념이 없기 때문에 많은 타임 스탬프 변환을 수행해야합니다. 이 과정에서 필드에 인덱스를 활용할 능력을 잃게됩니다.

어쨌든, 꽤 복잡한 쿼리입니다. 내가 제안하는 것보다 더 나은 방법이있을 수 있지만, 적어도 내가 제안하는 것은 의미가 있습니다. 이 방법에서는 테이블을 자체에 조인하여 데카르트 조인을 수행합니다. 그런 다음 ON 조건을 사용하여 레코드 수를 제한하여 두 번째 로그 테이블의 날짜가 첫 번째 로그 테이블의 날짜보다 7 일 이내인지 확인하십시오. 마지막으로 집계 및 그룹화를 수행합니다. 쿼리는 다음과 같이 표시 될 수 있습니다.

SELECT DATE(FROM_UNIXTIME(log1.ts)) as `day`, COUNT(DISTINCT log2.user_id) as `dau` 
FROM log AS log1 
INNER JOIN log AS log2 
ON DATE(FROM_UNIXTIME(log2.ts)) <= DATE(FROM_UNIXTIME(log1.ts)) 
AND DATE(FROM_UNIXTIME(log2.ts)) >= DATE_SUB(DATE(FROM_UNIXTIME(log1.ts)), INTERVAL 7 DAY) 
GROUP BY `day` 
ORDER BY `day` ASC 

경고. 상당히 많은 수의 로그 항목이있는 경우이 쿼리는 실행하는 데 오랜 시간이 걸릴 것이므로 결과 집합의 레코드 수를 몇 배로 늘리면 인덱스를 사용하지 않게됩니다.

실제로 테이블에 새로운 날짜 형식 열을 만들고 업데이트를 실행하여 값을 채울 수 있습니다. 해당 필드에 색인이 있는지 확인하십시오. 그런 다음 쿼리는 다음과 같을 수 있습니다.

SELECT log1.date_field as `day`, COUNT(DISTINCT log2.date_field) as `dau` 
FROM log AS log1 
INNER JOIN log AS log2 
ON log2.date_field <= log1.date_field 
AND log2.date_field >= DATE_SUB(log1.date_field, INTERVAL 7 DAY) 
GROUP BY `day` 
ORDER BY `day` ASC 

그러면 앞으로 모든 로그 항목에이 필드를 채울 수 있습니다.

+0

날짜 및 시간 저장소에 대한 교육을 보내 주셔서 감사합니다. 나는이 시점에서 타임 스탬프 열의 유형을 변경할 수 없다는 사실을 알고 있습니다. 첫 번째 쿼리가 제대로 작동하지 않습니다. 일부 열의 결과가 잘못되었습니다. 이유는 확실하지 않지만. 필요한 결과를 반환하는 쿼리에 대한 스펜서 응답을 확인할 수 있습니다. 어쨌든 고마워! – Protected

0

이 전체 주에 대한 활성 사용자 얻을 단순하고 간단하다 : 1 로그 그룹에서 weeklyactiveusers 로

선택 yearweek (TS) yearwk 266 수 (USER_ID) 등을, 2 카운트 (user_id) = 7;