高级 SQL 选择查询

Posted

技术标签:

【中文标题】高级 SQL 选择查询【英文标题】:Advanced SQL Select Query 【发布时间】:2011-10-13 21:02:33 【问题描述】:
week      cookie
1         a
1         b
1         c
1         d
2         a 
2         b
3         a
3         c
3         d

此表代表某人在特定的一周内访问了一个网站。每个 cookie 代表一个人。每个条目代表某人在特定周内访问此站点。例如,最后一个条目表示“d”在第 3 周访问该站点。

我想知道有多少(相同的)人在下周继续回来,如果有一个开始周可以查看。

例如,如果我查看第 1 周。我会得到如下结果:

1 | 4
2 | 2
3 | 1

因为 4 位用户在第 1 周回来。他们中只有 2 位 (a,b) 在第 2 周回来。他们中只有 1 (a) 位在这 3 周内全部回来。

如何进行选择查询以找出答案?桌子会很大:可能有 100 周,所以我想找到正确的方法。

【问题讨论】:

对不起,对你们来说可能没那么难,但我想不通。 非常感谢您的帮助!! 其实这是一个有趣的问题。所以你是说给定一个周数n,然后对于每个周数w > n,给出在所有周访问nw 的用户数?或者至少访问nw 我的意思是所有的星期。例如,我想在第 3 周后的 10 周进行检查。 ​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​## Thanks.请帮忙。 不客气,但这个很难。不要因为被卡住而感到难过。 SQL 中的“万能”很难。我正在做某事...... 【参考方案1】:

使用自联接:

SELECT ... FROM visits AS v1 LEFT JOIN visits AS v2 ON v2.week = v1.week+1
WHERE v2.week IS NOT NULL
GROUP BY cookie

这将为您提供第二次及以后访问的记录。

但我认为最好是GROUP BY cookie,它可以让您获得每个 cookie 的访问次数;任何大于 1 的数字都是回访用户。

【讨论】:

thankyyouu                                                               =但是  仍然无法得到工作。 1)什么意思? v2.week+1 应该是 v1.week+1 还是不知道怎么用。这个解决方案似乎是最简单的一个,它有效,对吧?【参考方案2】:

此查询使用变量来跟踪相邻的周并计算它们是否是连续的:

set @start_week = 2, @week := 0, @conseq := 0, @cookie:='';
select conseq_weeks, count(*)
from (
select 
  cookie,
  if (cookie != @cookie or week != @week + 1, @conseq := 0, @conseq := @conseq + 1) + 1 as conseq_weeks,
  (cookie != @cookie and week <= @start_week) or (cookie = @cookie and week = @week + 1) as conseq,
  @cookie := cookie as lastcookie,
  @week := week as lastweek
from (select week, cookie from webhist where week >= @start_week order by 2, 1) x
) y
where conseq
group by 1;

这是第 2 周的。再过一周,更改顶部的 start_week 变量。

这是测试:

create table webhist(week int, cookie char);
insert into webhist values (1, 'a'), (1, 'b'), (1, 'c'), (1, 'd'), (2, 'a'), (2, 'b'), (3, 'a'), (3, 'c'), (3, 'd');

上述查询的输出where week &gt;= 1:

+--------------+----------+
| conseq_weeks | count(*) |
+--------------+----------+
|            1 |        4 |
|            2 |        2 |
|            3 |        1 |
+--------------+----------+

上述查询的输出where week &gt;= 2:

+--------------+----------+
| conseq_weeks | count(*) |
+--------------+----------+
|            1 |        2 |
|            2 |        1 |
+--------------+----------+

附言很好的问题,但有点破绽百出

【讨论】:

当然。谢谢。另一件事。如果我也想知道结果,请继续关注第二次,然后,我可以修改查询吗?在这种情况下,它应该返回 1|2, 2|1,因为 a,b 出现在第 2 周,而只有 1 出现在第 3 周。 我试图更改为“set @week_id := 2 ..........WHERE week_id >=2 .....”,但它给了我 1|3, 2|1 @JJ @week := 0 需要保持原样。要获得第 2 周,只需将 where week &gt;= 1 更改为 where week &gt;= 2。我会确保它在接下来的几个小时内正常工作。 谢谢。我试过了,但它给了我1|4, 2|1,但它应该返回1|2, 2|1。希望你能测试一下。非常感谢! 谢谢,但如果从第 2 周开始,输出不应该是 1|2, 2|1 吗?第 2 周只有 2 个不同的用户访问该网站。【参考方案3】:

这是一个有趣的问题。

我尝试计算每个人访问的最后一周是什么时候。 这是在下一周没有访问的开始或之后的第一周计算的。

一旦您知道每个用户的最后访问周数,您只需计算每周的最终访问时间是在该周或之后的不同用户的数量。

SELECT wks.week, COUNT(cookie) as Visitors
FROM (SELECT a.cookie, MIN(a.week) AS FinalVisit
      FROM WeekVisits a 
           INNER JOIN WeekVisits FirstWeek
           ON a.cookie = FirstWeek.cookie
      WHERE a.week >= 1
        AND FirstWeek.week = 1
        AND NOT EXISTS (SELECT 1 
                        FROM WeekVisits b
                        WHERE b.week = a.week + 1
                          AND b.cookie = a.cookie)
      GROUP BY a.cookie) fv
     INNER JOIN
     (SELECT DISTINCT week 
      FROM WeekVisits
      WHERE week >= 1) wks
     ON fv.FinalVisit >= wks.week 
GROUP BY wks.week
ORDER BY wks.week

编辑 - 感谢 ypercube 的关注。我还从“fv”查询中丢失了 group by。哎呀。 -我已经删除了表示参数的 cmets。 - 我已经删除了不必要的不​​同。EDIT再次 - 为 FirstWeek 添加了额外的内容,因为它无法从第 2 周开始处理

当我运行它时(诚然在 MS Access 上)

从第 1 周开始,我得到:

+------+----------+
|周 |访客 |
| 1 | 4 |
| 2 | 2 |
| 3 | 1 |
+--------+----------+

从第 2 周开始,我得到:

+------+----------+
|周 |访客 |
| 2 | 2 |
| 3 | 1 |
+--------+----------+

.. 符合预期。 (从第 2 周开始,您需要在与周列进行比较的三个位置将 1 更改为 2) 该方法看起来不错,但可能需要针对 mysql 调整语法。

【讨论】:

你在) fv之前少了一个括号 谢谢。但是我运行查询返回了1|1,对吗? 绝对有效,我试过了。如果您不介意,另一个快速问题是,如果我的表格有另一个字段“site_id”,它可以是 10、11、12,它代表不同的网站。如何修改查询,使其仅查看特定的 site_id,例如 IN (10,12) 例如:星期、cookie、site_id:1 | a | 10, 1 | b | 11, 2 | a | 10 将返回1|1, 2|1 我尝试在第 6 行后添加 AND a.site_id=10 并在第 16 行添加 AND site_id=10,但没有成功,再次感谢【参考方案4】:

这是我的解决方案,不是很简单,但 - 正如我所测试的 - 它确实解决了您的问题:

首先,我们声明一个存储过程,它将在特定的一周内为我们提供以字符串分隔的访问者,如果您愿意,可以使用 group_concat,但我这样做了 - 考虑到 group_concat 有文本限制。

DELIMITER $$

DROP PROCEDURE IF EXISTS `db`.`get_visitors_for_week`$$

CREATE DEFINER=`root`@`localhost` PROCEDURE `get_visitors_for_week`(id_week INTEGER, OUT result TEXT)
BEGIN
    DECLARE should_continue INT DEFAULT 0;
    DECLARE c_cookie CHAR(1);
    DECLARE r CURSOR FOR SELECT v.cookie
                FROM visits v WHERE v.week = id_week;
    DECLARE CONTINUE HANDLER FOR NOT FOUND
        SET should_continue = 1;
    OPEN r;
    REPEAT
        SET c_cookie = NULL;
        FETCH r INTO c_cookie;
        IF c_cookie IS NOT NULL THEN
            IF result IS NULL OR result = '' THEN
                SET result = c_cookie;
            ELSE SET result = CONCAT(result,',',c_cookie);
            END IF;
        END IF;
        UNTIL should_continue = 1
    END REPEAT;
    CLOSE r;
    END$$

DELIMITER ;

然后我们声明一个函数来包装这个存储过程,这样我们就可以方便地在查询中调用:

DELIMITER $$

DROP FUNCTION IF EXISTS `db`.`concat_values`$$

CREATE DEFINER=`root`@`localhost` FUNCTION `concat_values`(id_week INTEGER) RETURNS TEXT CHARSET latin1
BEGIN
    DECLARE result TEXT;
    CALL get_visitors_for_week(id_week, result);
    RETURN result;
    END$$

DELIMITER ;

然后我们必须计算本周和上周来过的访问者——当然是每周——我们通过在串联列表中搜索我们的 cookie 字符串来“看到”这一点。这是最终的查询:

SELECT
  v.week,
  SUM(IF(IFNULL(concat_values(v.week - 1)) OR INSTR(concat_values(v.week - 1),v.cookie) > 0, 1, 0)) AS Visitors
FROM (SELECT
        v.week,
        v.cookie,
        vt.visitors
      FROM visits v
        INNER JOIN (SELECT DISTINCT
                      v.week,
                      concat_values(v.week) AS visitors
                    FROM visits v) AS vt
          ON v.week = vt.week) AS v
WHERE v.week >= 1
GROUP BY v.week

将条件v.week &gt;= 1 -the 1- 替换为您想要开始的周数。

【讨论】:

非常感谢!真的很感激。【参考方案5】:

由于某些原因,这些答案中的大多数都非常复杂,它不需要游标或 for 循环或任何类似的东西......

我想知道有多少(相同的)人不断回来 下周,当给定开始周查看时。

如果您想知道任何一周有多少用户访问了一周,然后是下一周访问的每一周:

SELECT visits.week, COUNT(1) AS [NumRepeatUsers]
FROM visits 
WHERE EXISTS (
    SELECT TOP 1 1 
    FROM visits AS nextWeek 
    WHERE nextWeek.week = visits.week+1 
      AND nextWeek.cookie = visits.cookie
  )
  AND EXISTS (
    SELECT TOP 1 1 
    FROM visits AS searchWeek
    WHERE searchWeek.week = @week 
      AND nextWeek.cookie = visits.cookie
  )
GROUP BY visits.week
ORDER BY visits.week

但是,如果您在第 1 周有 10 个用户,然后在接下来的 5 周访问了 5 个不同的用户,那么这不会显示您的结果会随着时间的推移而减少,您会看到 1=10,2=5,3=5,4 =5,5=5,6=5 以此类推,而您希望看到 5=x 其中 x 是连续 5 周每周访问的用户数。为此,请参见下文:

SELECT visits.week, COUNT(1) AS [NumRepeatUsers]
FROM visits 
WHERE EXISTS (
    SELECT TOP 1 1 
    FROM visits AS nextWeek 
    WHERE nextWeek.week = visits.week+1 
      AND nextWeek.cookie = visits.cookie
  )
  AND EXISTS (
    SELECT TOP 1 1 
    FROM visits AS searchWeek
    WHERE searchWeek.week = @week 
      AND nextWeek.cookie = visits.cookie
  )
  AND visits.week - @week = (
    SELECT COUNT(1) AS [Count]
    FROM visits AS searchWeek
    WHERE searchWeek.week BETWEEN @week+1 AND visits.week
      AND nextWeek.cookie = visits.cookie
  )
GROUP BY visits.week
ORDER BY visits.week

这会给你 1=10,2=5,3=4,4=3,5=2,6=1 等

【讨论】:

感谢您的帮助!救了我的命!【参考方案6】:

好的,假设您的表名为visits,并且您对周号n 感兴趣。您想知道,对于每个周数w &gt;= n,哪些用户出现在每个这样的一周w

那么这样的星期有多少?

select count(*)
from visits
where week >= n;

每个用户在多少周内访问?

select user, count(user)
from visit
group by user
where week >= n;

假设您有第 1、3、4、5、6、7、9、10 和 13 周,并且您对第 5 周感兴趣。所以上面的第一个查询给您 6,因为有 6 周的兴趣: 5, 6, 7, 9, 10, 和 13。第二个查询将为您提供每个用户他们访问的周数。现在您想知道这些用户中有多少是 6。

我认为这可行:

select user, count(user)
from visit
group by user
having count(user) = (
    select count(*)
    from visits
    where week >= n)
where week >= n;

但我现在无法访问 MySQL。如果它不起作用,那么也许这种方法是有意义的,并且会让你朝着正确的方向前进。编辑:我明天可以测试。

【讨论】:

非常感谢!真的很感激。

以上是关于高级 SQL 选择查询的主要内容,如果未能解决你的问题,请参考以下文章

高级 SQL 选择查询

高级查询 - 选择,按条件求和并返回累加值

2017-03-10T-sql基础语句及条件,高级查询

SQL(高级查询)

sql server T-SQL高级查询

SQL高级查询