计算行中的值也在前一行中的行
Posted
技术标签:
【中文标题】计算行中的值也在前一行中的行【英文标题】:Count rows where value in row is also in previous row 【发布时间】:2020-03-01 21:27:17 【问题描述】:我想获得一个计数,其中一行中的值的内容也在前一行中。
Row | Item1 | Item2 | Item 3 |
1 | Dog | Cat | Rat
2 | Bird | Cat | Horse
3 | Horse | Dog | Rat
4 | Bird | Cat | Horse
5 | Horse | Bird | Cat
第 2 行会增加 Cat 的数量,因为 Cat 在第 1 行和第 2 行中
第 3 行会增加马的数量,因为马也在第 2 行中
第 4 行会增加马的数量,因为马也在第 3 行
第 5 行会增加 Horse AND Cat 的数量,因为它们都出现在第 4 行中。
最多可以有 100 个项目或 SKU,我可以对任何或所有字段进行索引。在任何给定时间,可能有 1000 到 2000 行。
除了“SELECT * FROM table WHERE”之外,我什至不知道从哪里开始这个查询
【问题讨论】:
Item1、Item2 等列的设计很差。您应该有另一个表,每个项目 1 行。 你运行的是哪个版本的 mysql? 用这个设计做你想做的事是可能的,但会非常冗长,你需要 100 个条件。 是的。认真考虑修改架构 是的,item1、item2 等列的设计很糟糕,但有时您必须使用所提供的内容。 @GMB MySQL 版本 5.6.41-84.1 【参考方案1】:首先,使用 SKU 的所有可用唯一值创建表:
CREATE TABLE results(
id VARCHAR(255) NOT NULL PRIMARY KEY
);
-- All fields should be listed here one-by-one.
INSERT IGNORE INTO results (select Item1 from example);
INSERT IGNORE INTO results (select Item2 from example);
INSERT IGNORE INTO results (select Item3 from example);
与自己再次左连接主表可以获得上一行,即LEFT JOIN example AS previous ON previous.id + 1 = example.id
。
之后,我们必须检查当前行和上一行的示例表中是否存在每个唯一结果,最后得到:
SELECT
r.*,
SUM(
CASE WHEN r.id IN (
prv.Item1, prv.Item2, prv.Item3 -- All fields should be listed here.
) THEN 1 ELSE 0 END
) AS total
FROM
results AS r
LEFT JOIN
example AS cur ON r.id IN (
cur.Item1, cur.Item2, cur.Item3 -- All fields should be listed here.
)
LEFT JOIN
example AS prv ON prv.id + 1 = cur.id
GROUP BY
r.id
ORDER BY
cur.id
;
查看工作示例http://www.sqlfiddle.com/#!9/7ebd85/1/0
【讨论】:
【参考方案2】:这可以通过窗口函数来完成(在 MySQL 8.0 中可用)。
一个选项是取消透视结果集,然后使用lag()
检查以前的记录。假设id
s 总是增加1
,你可以这样做:
select
item,
sum(case when id = lag_id + 1 then 1 else 0 end) cnt_consecutive
from (
select
t.*,
lag(id) over(partition by item order by id) lag_id
from (
select id, item1 item from mytable
union all select id, item2 from mytable
union all select id, item3 from mytable
) t
) t
group by item
order by item
如果您没有递增的列,您可以使用dense_rank()
生成一个:
select
item,
sum(case when new_id = lag_new_id + 1 then 1 else 0 end) cnt_consecutive
from (
select
t.*,
lag(new_id) over(partition by item order by new_id) lag_new_id
from (
select
t.*,
dense_rank() over(order by id) new_id
from (
select id, item1 item from mytable
union all select id, item2 from mytable
union all select id, item3 from mytable
) t
) t
) t
group by item
order by item
在 this DB Fiddle 中,两个查询都返回:
项目 | cnt_consecutive :---- | --------------: 鸟 | 1 猫 | 2 狗 | 0 马 | 3 大鼠 | 0【讨论】:
我真的很喜欢其中的一些新功能,但该系统仍在运行 5.6.41-84.1,因此我接受了另一个答案,因为它在这台旧服务器上运行。【参考方案3】:我看到@frost-nzcr4 的建议非常好,我正在做我自己的版本,与昨天的非常相似。但是,我正在做的方法有点不同,因为我没有专门创建一个表来存储唯一值。相反,我在做类似@GMB UNION
子查询的操作,结果是这样的:
SELECT B.row, A.allitem,
SUM(CASE WHEN A.allitem IN (C.Item1, C.Item2, C.Item3) THEN 1
ELSE 0 END) AS total
FROM
-- this sub-query will be dynamic and UNION will eliminate any duplicate
(SELECT item1 AS allitem FROM mytable UNION
SELECT item2 FROM mytable UNION
SELECT item3 FROM mytable) AS A
LEFT JOIN mytable AS B ON A.allitem IN (B.Item1, B.Item2, B.Item3)
LEFT JOIN mytable AS C ON C.row + 1 = B.row
GROUP BY A.allitem
ORDER BY B.row;
在这里提琴:https://www.db-fiddle.com/f/bUUEsaeyPpAMfR2bK1VpBb/2
如您所见,这与frost 的建议完全相同,只是稍作修改。在子查询allitem
中,只要插入了新值,值就会更新,因此您无需继续将新的唯一数据插入单独的表中。
此外,除非您删除 sql_mode,否则此查询在上述 MySQL v5.7 上通常会出现 this is incompatible with sql_mode=only_full_group_by
错误。
【讨论】:
以上是关于计算行中的值也在前一行中的行的主要内容,如果未能解决你的问题,请参考以下文章