Подсчет строк, в которых значение в строке также находится в предыдущей строке - PullRequest
0 голосов
/ 02 марта 2020

Я хочу получить счетчик, в котором содержимое значения в одной строке также находится в предыдущей строке.

   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"

Ответы [ 3 ]

1 голос
/ 02 марта 2020

Во-первых, создайте таблицу со всеми доступными уникальными значениями 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

0 голосов
/ 03 марта 2020

Я вижу, что предложение @ 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;

Fiddle здесь: https://www.db-fiddle.com/f/bUUEsaeyPpAMfR2bK1VpBb/2

Как видите, этот запрос в точности аналогичен предложению Фроста с незначительными изменениями. В подзапросе значение allitem будет обновляться при условии добавления новых значений, поэтому вам не нужно постоянно вставлять новые уникальные данные в отдельную таблицу.

Кроме того, этот запрос обычно выдает ошибку this is incompatible with sql_mode=only_full_group_by на MySQL v5.7 и выше, если вы не удалите sql_mode.

0 голосов
/ 02 марта 2020

Это можно сделать с помощью оконных функций (доступно в 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

В это БД Fiddle , оба запроса возвращают:

item  | cnt_consecutive
:---- | --------------:
Bird  |               1
Cat   |               2
Dog   |               0
Horse |               3
Rat   |               0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...