Mysql SQL:按顺序计算访问次数

Mysql SQL:按顺序计算访问次数,mysql,sql,Mysql,Sql,我有下表(访问次数): 查询运行后,我希望得到以下结果: sequence count 5 1 (1 user visited 5 flipbooks in a row: 1123) 2 2 (2 users visited 2 flipbooks in a row: 1124, 1125) 1 1 (1 user visited only 1 flipbook: 1126) 有没有办法做到这一点?SQL很难找到用户访问顺序的

我有下表(访问次数):

查询运行后,我希望得到以下结果:

sequence  count
  5         1  (1 user visited 5 flipbooks in a row: 1123)
  2         2  (2 users visited 2 flipbooks in a row: 1124, 1125)
  1         1  (1 user visited only 1 flipbook: 1126)

有没有办法做到这一点?

SQL很难找到用户访问顺序的结果

但是,如果使用其他方法,例如:创建或重用一个表,则会更容易:

users(fb_id, best_sequence, current_sequence, last_modified)
-------------------------------------------------------
1123         5              3                 2016-01
在你的动画书上打圈

SELECT * FROM visits WHERE fb_id=1123 AND flipbook >= .... 
(you might redesign data to make SQL easier here)
如果序列匹配,则将当前_序列更新为4;如果发现间隙,则将当前_序列更新为0

如果当前\u序列>最佳\u序列,则设置最佳\u序列=当前\u序列

您可以通过cron作业、触发器或其他您觉得最舒服的方法来完成


这是一个想法,并编写自己的代码。

SQL很难实现查找用户访问顺序的结果

但是,如果使用其他方法,例如:创建或重用一个表,则会更容易:

users(fb_id, best_sequence, current_sequence, last_modified)
-------------------------------------------------------
1123         5              3                 2016-01
在你的动画书上打圈

SELECT * FROM visits WHERE fb_id=1123 AND flipbook >= .... 
(you might redesign data to make SQL easier here)
如果序列匹配,则将当前_序列更新为4;如果发现间隙,则将当前_序列更新为0

如果当前\u序列>最佳\u序列,则设置最佳\u序列=当前\u序列

您可以通过cron作业、触发器或其他您觉得最舒服的方法来完成


这是一个想法,并编写自己的代码。

fb_id=1124
一行只浏览了一本动画书。除非行
id=4
应该是“12月20日14日”而不是“12月20日15日

是的,这可以在MySQL中实现,使用用户定义的变量。MySQL参考手册警告说,在同一语句中使用用户定义变量的行为是未定义的

sequence   count  info
--------  ------  ---------------------------------------------
       5       1  (1 user visited 5 flipbooks in a row: 1123
       2       1  (1 user visited 2 flipbooks in a row: 1125
       1       2  (2 users visited only 1 flipbook: 1124,1126
该结果集由以下SQL查询生成:

SELECT d.seq AS `sequence`
     , COUNT(1) AS `count`
     , CONCAT('('
             ,COUNT(1)
             ,' user'
             ,IF(COUNT(1)>1,'s','')
             ,' visited'
             ,IF(d.seq>1,'',' only')
             ,' '
             ,d.seq
             ,' flipbook'
             ,IF(d.seq>1,'s in a row: ',': ')
             ,GROUP_CONCAT(d.fb_id ORDER BY d.fb_id)
       ) AS `info`
  FROM ( SELECT c.fb_id
              , MAX(c.cnt) AS seq
           FROM ( SELECT @cnt := IF(@prev_fb_id = v.fb_id AND PERIOD_DIFF(v.yyyymm,@prev_yyyymm)=1, @cnt + 1, 1) AS cnt
                       , @prev_yyyymm := v.yyyymm AS yyyymm
                       , @prev_fb_id  := v.fb_id AS fb_id
                    FROM ( SELECT @prev_fb_id := NULL
                                , @prev_yyyymm := NULL
                                , @cnt := 0
                         ) i
                   CROSS
                    JOIN ( SELECT t.fb_id
                                , DATE_FORMAT(STR_TO_DATE(CONCAT('01 ',t.flipbook),'%d %M %Y'),'%Y%m') AS yyyymm
                             FROM t
                            GROUP BY t.fb_id, yyyymm 
                            ORDER BY t.fb_id, yyyymm
                          ) v  
                ) c
          GROUP BY c.fb_id
       ) d
 GROUP BY d.seq
 ORDER BY d.seq DESC
跟进

源表的表名进入最里面的内联视图中的查询,别名为
v
。(在上面的示例中,表名为
t

要了解这是如何工作的,您可以只运行最内部内联视图的查询,查看它返回的内容。重要的工作是将
动画书
列重新格式化为
YYYYMM
格式,并对行进行排序。(稍后我们将使用
PERIOD_DIFF
函数计算
动画书
值之间的月数。)

内联视图
i
仅用于初始化我们将要使用的用户定义变量。我们在最内部的内联视图查询中执行此操作,以便在外部查询中引用这些变量之前完成。它本质上相当于在查询运行前立即运行单独的SET语句。(我们不希望变量的任何剩余值影响我们的结果。)

一旦
v
i
视图被具体化(作为派生表),可以运行别名为
v
的内联视图中的查询。(FROM子句中的
视图查询基本上与表类似。)

这是一个神奇的查询。我们使用用户定义的变量来保存“上一行”的值,这样我们就可以将其与当前行进行比较。如果当前行是为同一用户,并且正好是前一行之后一个月,我们将序列计数增加1,否则,我们将其设置为1

一旦该查询完成,我们就有了一个派生表,可以用作另一个查询的行源。在该查询中,我们希望为每个用户找到该序列计数器的“最大”值。这将为每个用户提供最长的序列

在这个集合中,最外层的查询几乎是微不足道的…按最长序列降序排列,并折叠行以获得具有相同最大序列值的用户数


为了通过序列中的
fb\u id
获得最高的访问次数,我们可以在最内部视图查询中累积访问次数。计数(1)
或总和(1)将给出每个月的访问次数

这可以输入到下一个查询中。我们可以执行与累积连续月份相同的检查。我们将累积访问总数,而不是增加1

必须修改下一个查询。我们不能只在
tot
周围包装一个
MAX()
,因为我们不能保证总访问量来自同一个最长的序列。我们可能在5个月的序列中有6次访问,但同一用户可能在3个月内访问了8次。因此,我们取消了MAX()函数,而是使用排序(从最高到最低)。我们将保留fb_id第一行的值,并将其他值设置为NULL。然后,在最外层的查询中,我们可以使用
MAX()
聚合将忽略空值,并返回具有相同
序列
值的所有用户的最高总访问量

我们可以得到这样的结果:

sequence   count  highest_tot
--------  ------  -----------
       5       1            6
       2       1            2
       1       2            1
从这样的查询中:

SELECT d.seq AS `sequence`
     , COUNT(1) AS `count`
     , MAX(FLOOR(d.tot)) AS `highest_tot`
  FROM ( SELECT IF(@c_fb_id=c.fb_id,NULL,c.cnt) AS seq
              , IF(@c_fb_id=c.fb_id,NULL,c.tot) AS tot
              , @c_fb_id := c.fb_id AS fb_id
           FROM ( SELECT @cnt := IF(@prev_fb_id = v.fb_id AND PERIOD_DIFF(v.yyyymm,@prev_yyyymm)=1, @cnt + 1, 1) AS cnt
                       , @tot := IF(@prev_fb_id = v.fb_id AND PERIOD_DIFF(v.yyyymm,@prev_yyyymm)=1, @tot + v.tot, v.tot) AS tot
                       , @prev_yyyymm := v.yyyymm AS yyyymm
                       , @prev_fb_id  := v.fb_id AS fb_id
                    FROM ( SELECT @prev_fb_id := NULL
                                , @prev_yyyymm := NULL
                                , @cnt := 0
                                , @tot := 0
                                , @c_fb_id := NULL
                         ) i
                   CROSS
                    JOIN ( SELECT t.fb_id
                                , DATE_FORMAT(STR_TO_DATE(CONCAT('01 ',t.flipbook),'%d %M %Y'),'%Y%m') AS yyyymm
                                , SUM(1) AS tot
                             FROM t
                            GROUP BY t.fb_id, yyyymm 
                            ORDER BY t.fb_id, yyyymm
                          ) v  
                ) c
          ORDER BY c.fb_id DESC, c.cnt DESC, c.tot DESC
       ) d
 WHERE d.seq IS NOT NULL
 GROUP BY d.seq
 ORDER BY d.seq DESC

fb_id=1124
仅“一行”浏览了一本动画书。除非行
id=4
应该是“12月20日14”而不是“12月20日15

是的,这可以在MySQL中使用用户定义的变量来实现。MySQL参考手册警告说,在同一语句中使用用户定义变量的行为是未定义的

sequence   count  info
--------  ------  ---------------------------------------------
       5       1  (1 user visited 5 flipbooks in a row: 1123
       2       1  (1 user visited 2 flipbooks in a row: 1125
       1       2  (2 users visited only 1 flipbook: 1124,1126
该结果集由以下SQL查询生成:

SELECT d.seq AS `sequence`
     , COUNT(1) AS `count`
     , CONCAT('('
             ,COUNT(1)
             ,' user'
             ,IF(COUNT(1)>1,'s','')
             ,' visited'
             ,IF(d.seq>1,'',' only')
             ,' '
             ,d.seq
             ,' flipbook'
             ,IF(d.seq>1,'s in a row: ',': ')
             ,GROUP_CONCAT(d.fb_id ORDER BY d.fb_id)
       ) AS `info`
  FROM ( SELECT c.fb_id
              , MAX(c.cnt) AS seq
           FROM ( SELECT @cnt := IF(@prev_fb_id = v.fb_id AND PERIOD_DIFF(v.yyyymm,@prev_yyyymm)=1, @cnt + 1, 1) AS cnt
                       , @prev_yyyymm := v.yyyymm AS yyyymm
                       , @prev_fb_id  := v.fb_id AS fb_id
                    FROM ( SELECT @prev_fb_id := NULL
                                , @prev_yyyymm := NULL
                                , @cnt := 0
                         ) i
                   CROSS
                    JOIN ( SELECT t.fb_id
                                , DATE_FORMAT(STR_TO_DATE(CONCAT('01 ',t.flipbook),'%d %M %Y'),'%Y%m') AS yyyymm
                             FROM t
                            GROUP BY t.fb_id, yyyymm 
                            ORDER BY t.fb_id, yyyymm
                          ) v  
                ) c
          GROUP BY c.fb_id
       ) d
 GROUP BY d.seq
 ORDER BY d.seq DESC
跟进

源表的表名进入最里面的内联视图中的查询,别名为
v
(在上面的示例中,表名为
t

要了解这是如何工作的,您可以只运行最内部内联视图的查询,查看它返回的内容。重要的工作是将
动画书
列重新格式化为
YYYYMM
格式,并对行进行排序。(稍后我们将使用
PERIOD_DIFF
函数计算
动画书
值之间的月数。)

内联视图
i
仅存在于i中