Выберите все записи, где последние n символов в столбце не являются уникальными - PullRequest
0 голосов
/ 17 апреля 2019

У меня немного странное требование в MySQL.Я должен выбрать все записи из таблицы, где последние 6 символов не являются уникальными.

, например, если у меня есть таблица:

enter image description here

Iследует выбрать строки 1 и 3, поскольку последние 6 букв этих значений не являются уникальными.

У вас есть идеи, как это реализовать?Спасибо за помощь.

Ответы [ 6 ]

1 голос
/ 17 апреля 2019

Может быть быстрым кодом, так как отсчет не требуется.

Тест в реальном времени: https://www.db -fiddle.com / f / dBdH9tZd4W6Eac1TCRXZ8U / 0

select *
from tbl outr
where not exists
(
    select 1 / 0 -- just a proof that this is not evaluated. won't cause division by zero
    from tbl inr
    where 
        inr.id <> outr.id
        and right(inr.value, 6) = right(outr.value, 6)  
)

Выход:

| id  | value           |
| --- | --------------- |
| 2   | aaaaaaaaaaaaaa  |
| 4   | aaaaaaaaaaaaaaB |
| 5   | Hello           |

Логика состоит в том, чтобы проверять другие строки, которые не равны тому же id внешней строки. Если эти другие строки имеют те же правые 6 символов, что и внешняя строка, не отображайте эту внешнюю строку.

UPDATE

Я неправильно понял намерение ОП. Это обратное. В любом случае, просто поменяйте логику. Используйте EXISTS вместо NOT EXISTS

Тест в реальном времени: https://www.db -fiddle.com / f / dBdH9tZd4W6Eac1TCRXZ8U / 3

select *
from tbl outr
where exists
(
    select 1 / 0 -- just a proof that this is not evaluated. won't cause division by zero
    from tbl inr
    where 
        inr.id <> outr.id
        and right(inr.value, 6) = right(outr.value, 6)  
)

Выход:

| id  | value       |
| --- | ----------- |
| 1   | abcdePuzzle |
| 3   | abcPuzzle   |

UPDATE

Протестировал запрос. Производительность моего ответа (коррелированный EXISTS подход) не является оптимальной. Просто держу мой ответ, чтобы другие знали, какого подхода избегать:)

GhostGambler ответ быстрее, чем correlated EXISTS подход. Для 5 миллионов строк его ответ занимает всего 2,762 секунды:

explain analyze                                   
SELECT
    tbl.*
FROM
    (
        SELECT
            RIGHT(value, 6) AS ending
        FROM
            tbl
        GROUP BY
            ending
        HAVING
            COUNT(*) > 1
    ) grouped
    JOIN tbl ON grouped.ending = RIGHT(value, 6)                                                

enter image description here

Мой ответ (коррелированный EXISTS) занимает 4,08 секунды:

explain analyze
select *
from tbl outr
where exists
(
    select 1 / 0 -- just a proof that this is not evaluated. won't cause division by zero
    from tbl inr
    where 
        inr.id <> outr.id
        and right(inr.value, 6) = right(outr.value, 6)          
)

enter image description here

Простой запрос - самый быстрый, без объединения, просто простой запрос IN. 2,722 секунды. Он имеет практически такую ​​же производительность, что и подход JOIN, поскольку у них один и тот же план выполнения. Это ответ от kiks73 . Я просто не знаю, почему он сделал второй ответ излишне сложным.

Так что это просто дело вкуса или выбор кода, который будет более читабельным select from in против select from join

explain analyze
SELECT *
FROM tbl
where right(value, 6) in 
    (
        SELECT
            RIGHT(value, 6) AS ending
        FROM
            tbl
        GROUP BY
            ending
        HAVING
            COUNT(*) > 1
    ) 

Результат:

enter image description here

<Ч />

Используемые данные испытаний:

CREATE TABLE tbl (
  id INTEGER primary key,
  value VARCHAR(20)
);

INSERT INTO tbl
  (id, value)
VALUES
  ('1', 'abcdePuzzle'),
  ('2', 'aaaaaaaaaaaaaa'),
  ('3', 'abcPuzzle'),
  ('4', 'aaaaaaaaaaaaaaB'),
  ('5', 'Hello');


insert into tbl(id, value)
select x.y, 'Puzzle'
from generate_series(6, 5000000) as x(y);

create index ix_tbl__right on tbl(right(value, 6));
<Ч />

Спектакли без указателя и с указателем на tbl(right(value, 6)):

JOIN подход:

Без индекса: 3,805 секунды

С индексом: 2,762 секунды

enter image description here

IN подход:

Без индекса: 3,719 секунд

С индексом: 2,722 секунды

enter image description here

1 голос
/ 17 апреля 2019

Это то, что вам нужно: подзапрос, чтобы получить правильное дублирование (значение 6), и основной запрос, чтобы получить строки в соответствии с этим условием.

SELECT t.* FROM t WHERE RIGHT(`value`,6) IN (
    SELECT RIGHT(`value`,6)
    FROM t
    GROUP BY RIGHT(`value`,6) HAVING COUNT(*) > 1);

ОБНОВЛЕНИЕ

Это решение, позволяющее избежать ошибки mysql в случае, если у вас sql_mode=only_full_group_by

SELECT t.* FROM t WHERE RIGHT(`value`,6) IN (
    SELECT DISTINCT right_value FROM (
        SELECT RIGHT(`value`,6) AS right_value, 
               COUNT(*) AS TOT
        FROM t
        GROUP BY RIGHT(`value`,6) HAVING COUNT(*) > 1)  t2
        ) 

Скрипка здесь

1 голос
/ 17 апреля 2019

Я использую JOIN против подзапроса, в котором подсчитываю вхождения каждой уникальной комбинации n (в моем примере 2) последних символов

SELECT t.*
FROM t
JOIN (SELECT RIGHT(value, 2) r, COUNT(RIGHT(value, 2)) rc 
      FROM t 
      GROUP BY r) c ON c.r = RIGHT(value, 2) AND c.rc > 1
1 голос
/ 17 апреля 2019

РЕДАКТИРОВАНИЕ: Ранее я неправильно понял вопрос, и я действительно не хочу ничего менять в своем первоначальном ответе. Но если мой предыдущий ответ неприемлем в какой-то среде и может ввести в заблуждение людей, я все равно должен его исправить.

SELECT GROUP_CONCAT(id),RIGHT(VALUE,6)
FROM table1
GROUP BY RIGHT(VALUE,6) HAVING COUNT(RIGHT(VALUE,6)) > 1;

Поскольку на этот вопрос уже есть хорошие ответы, я сделал свой запрос немного по-другому. И я проверил с sql_mode=ONLY_FULL_GROUP_BY. ;)

1 голос
/ 17 апреля 2019

Что-то подобное должно работать:

SELECT `mytable`.*
FROM (SELECT RIGHT(`value`, 6) AS `ending` FROM `mytable` GROUP BY `ending` HAVING COUNT(*) > 1) `grouped`
INNER JOIN `mytable` ON `grouped`.`ending` = RIGHT(`value`, 6)

но это не быстро. Это требует полного сканирования таблицы. Может быть, вам следует переосмыслить свою проблему.

0 голосов
/ 17 апреля 2019

Немного аккуратный код (если используется MySQL 8.0). Хотя не могу гарантировать производительность

Тест в реальном времени: https://www.db -fiddle.com / f / dBdH9tZd4W6Eac1TCRXZ8U / 1

select x.*
from 
(
    select  
        *, 
        count(*) over(partition by right(value, 6)) as unique_count
    from tbl
 ) as x
 where x.unique_count = 1                 

Выход:

| id  | value           | unique_count |
| --- | --------------- | ------------ |
| 2   | aaaaaaaaaaaaaa  | 1            |
| 4   | aaaaaaaaaaaaaaB | 1            |
| 5   | Hello           | 1            |

UPDATE

Я неправильно понял намерение ОП. Это обратное. Просто измените счет:

select x.*
from 
(
    select  
        *, 
        count(*) over(partition by right(value, 6)) as unique_count
    from tbl
 ) as x
 where x.unique_count > 1                 

Выход:

| id  | value       | unique_count |
| --- | ----------- | ------------ |
| 1   | abcdePuzzle | 2            |
| 3   | abcPuzzle   | 2            |
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...