计算行中的值也在前一行中的行

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() 检查以前的记录。假设ids 总是增加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 错误。

【讨论】:

以上是关于计算行中的值也在前一行中的行的主要内容,如果未能解决你的问题,请参考以下文章

从Excel中的行中提取唯一值

在 NumPy 数组的行中的特定位置插入零行

在添加Flask,SQLAlchemy之前检查表中的行中的值是不是已经存在

如何遍历数据框的行并检查列行中的值是不是为 NaN

如何使用Python计算数组特定行中的值[重复]

获取单词上方的行中的值