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

Count rows where value in row is also in previous row

我想得到一个计数,其中一行中的值的内容也在前一行中。

   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 行会增加马和猫的数量,因为它们都出现在第 4 行中。

最多可以有 100 个项目或 SKU,我可以在任何或所有字段上建立索引。在任何给定时间,可能有 1000 到 2000 行。

除了 "SELECT * FROM table WHERE"

之外,我什至不知道从哪里开始这个查询

这可以通过 window 函数完成(在 MySQL 8.0 中可用)。

一个选项是逆透视结果集,然后使用lag()检查以前的记录。假设 id 总是增加 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中,两个查询return:

item  | cnt_consecutive
:---- | --------------:
Bird  |               1
Cat   |               2
Dog   |               0
Horse |               3
Rat   |               0

首先,使用 SKU 的所有可用唯一值创建 table:

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);

前一行可以通过左连接主 table 再次与自身获得,即 LEFT JOIN example AS previous ON previous.id + 1 = example.id.

之后,我们必须检查示例 table 中当前行和上一行中是否存在每个唯一结果,最后得到:

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

我看到@frost-nzcr4 的建议非常好,我正在做我自己的版本与昨天非常相似。但是,我正在做的方法有点不同,因为我没有专门创建一个 table 来存储唯一值。相反,我在做类似@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;

Fiddle 这里:https://www.db-fiddle.com/f/bUUEsaeyPpAMfR2bK1VpBb/2

如您所见,这与 frost 的建议完全相似,只是稍作修改。在子查询 allitem 中,只要插入了新值,值就会更新,因此您不需要不断地将新的唯一数据插入到单独的 table 中。

此外,此查询通常会在上面的 MySQL v5.7 上出现 this is incompatible with sql_mode=only_full_group_by 错误,除非您删除 sql_mode。