高级 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
,给出在所有周访问n
到w
的用户数?或者至少访问n
和w
?
我的意思是所有的星期。例如,我想在第 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 >= 1
:
+--------------+----------+
| conseq_weeks | count(*) |
+--------------+----------+
| 1 | 4 |
| 2 | 2 |
| 3 | 1 |
+--------------+----------+
上述查询的输出where week >= 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 >= 1
更改为 where week >= 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 >= 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 >= 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 选择查询的主要内容,如果未能解决你的问题,请参考以下文章