SQLite3 Как объединить оператор IN с оператором LIKE, чтобы получить частичное совпадение текста с подзапросом? - PullRequest
0 голосов
/ 11 октября 2018

Я пытаюсь обнаружить дубликаты в базе данных.Я придумал MWE того, что я пытаюсь сделать.Не каждый столбец идентификатора имеет значение, но идентификатор может быть в имени файла.Я пытаюсь найти все строки с идентификатором, который появляется в имени файла, которое находится в другой строке.

enter image description here

Этот запрос является своего рода чемЯ ищу, но проблема в том, что делать точные совпадения

SELECT * FROM items WHERE id IN (
    SELECT filename FROM items
);

Оператор IN является сокращением для:

name IN ("Bob Walters", "Alice Reed") ==> name == "Bob Walters" OR name = "Alice Reed"

Однако мне нужен оператор, который делает это:

_________ ==> "%" || name || "%" LIKE "Bob Walters" OR "%" || name || "%" LIKE "Alice Reed"

Я ищу то, что я могу поместить в пустое пространство для выполнения этой операции.В моей таблице чуть более 10000 строк (поэтому решение не обязательно должно быть оптимизировано для миллионов строк).

Ответы [ 3 ]

0 голосов
/ 11 октября 2018

Вы хотите СУЩЕСТВУЕТ, а не IN.Попробуйте:

SELECT *
FROM items AS i
WHERE EXISTS (SELECT *
              FROM items AS i2
              WHERE i.filename LIKE '%' || i2.id || '%' AND i.filename <> i2.filename)
0 голосов
/ 12 октября 2018

Вещи, которые я пробовал

  1. Использование оператора WHERE EXISTS в сочетании с оператором LIKE (~ 50 сек в базе данных из 10000 элементов)

    SELECT * FROM items AS i1 WHERE EXISTS (
        SELECT * FROM items AS i2 WHERE i1.id != i2.id AND i2.filename LIKE '%' || i1.id || '%'
    );
    
  2. Использование оператора WHERE EXISTS в сочетании с функцией instr (~ 50 сек в базе данных из 10000 элементов)

    SELECT * FROM items AS i1 WHERE EXISTS (
        SELECT * FROM items AS i2 WHERE i1.id != i2.id AND instr(i2.filename, i1.id) != 0
    );
    
  3. Использование оператора WHERE EXISTS в сочетании с оператором LIKE наряду с просмотром только строк с нулевым идентификатором (~ 30 сек в базе данных из 10000 элементов)

  4. Использование оператора WHERE EXISTSв сочетании с функцией instr и просмотром только строк с нулевым идентификатором (~ 30 сек в базе данных из 10000 элементов)
  5. В Python получите список идентификаторов, затем длякаждый идентификатор вызывает базу данных в поисках совпадений (~ 17 сек в базе данных на 10000 элементов)
  6. В Python получите список идентификаторов и имен файлов из базы данных, а затем выполните поиск в Python (~ 10 сек на элементе 10000)база данных) <-- решение, которое я использовал

Другие возможные решения

  1. Использование оператора WHERE EXISTS в сочетании с оператором REGEXP
  2. Включение расширения полнотекстового поиска FTS4 и создание виртуальной таблицы, поэтому использование оператора WHERE EXISTS в сочетании сMATCH оператор (или другой оператор FTS4)
  3. Использование SQLAlchemy в Python
  4. Реструктуризация базы данных (например, извлечение идентичных идентификаторов из имен файлов в отдельный столбец идентификатора, поэтому мы можем сделать точныйсовпадения столбцов вместо поиска по шаблону)

Код для моего решения

statement_id = 'SELECT * FROM items WHERE id IS NOT NULL and id != ""'
cursor.execute(statement_id)
ids = cursor.fetchall()

statement_title = 'SELECT * FROM items WHERE title IS NOT NULL AND title != ""'
cursor.execute(statement_title)
titles = cursor.fetchall()

matches = []
for id in ids:
    for title in titles:
        if id['id'] in title['title']:
            matches.append([id, title])

Заключение

В заключение, решение уродливо, но я думаю, учитывая обстоятельства (это личный проект, я не буду часто выполнять эту операцию дедупликации данных и т. д.).Я думаю, что полнотекстовые расширения или реструктуризация базы данных могут быть более хорошими решениями.

0 голосов
/ 11 октября 2018

Если вы используете SQLite 3+, вы можете попробовать использовать оператор REGEXP:

SELECT *
FROM items i1
WHERE EXISTS (SELECT 1 FROM items i2
              WHERE i2.filename REGEXP '\b' || i1.id || '\b' AND
              i1.id <> i2.id);

Если ваша версия SQLite не поддерживает REGEXP, то вы можете используйте LIKE вместо него:

SELECT *
FROM items i1
WHERE EXISTS (SELECT 1 FROM items i2
              WHERE i2.filename LIKE '%' || i1.id || '%' AND
              i1.id <> i2.id);

Я выделил can выше, потому что проблема с LIKE и подстановочными знаками заключается в том, что он не только соответствует точным совпадениям, но иподстроки, например, если id=34983 появляется как подстрока другого идентификатора в имени файла в другой записи, будет ложное срабатывание.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...