Ищем суррогат для массивов в SQLLite, через SQLAlchemy - PullRequest
0 голосов
/ 24 января 2020

Я использую SQLlite с SQLAlchemy, и у меня есть значения в базе данных, такие как:

id name    value
 1  ionic   "alpha, beta , gama, teta"

Мне нужно сделать запрос на выборку по имени и значениям, используя в качестве параметров:

name : ionic
value: "beta, gama"   

Таким образом, если имя соответствует, любое из значений, переданных в качестве параметров, найденных в значении, возвращает идентификатор. Но если значения «альфа, ронда» потерпят неудачу.

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

1 Ответ

2 голосов
/ 24 января 2020

В традиционных SQL списки выполняются как таблицы объединения. В этом случае отношения один ко многим. Одна вещь может иметь много значений.

create table things (
  id serial primary key,
  name text not null
);

create table thing_values (
  thing_id references things(id),
  value text not null,
  unique(value, thing_id)
);

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

select *
from things t
join thing_values tv on t.id = tv.thing_id;

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

select thing_id as id
from thing_values
where value in ('beta', 'gamma')
group by thing_id
having count(*) = 2

В вашем случае вы искали 2 значения, поэтому вам нужны только те вещи, которые совпадают дважды. Поскольку у нас есть уникальное предложение для thing_values, дубликатов быть не может. Если бы они были, вам бы пришлось использовать having count(distinct value) = 2.

См. Этот ответ для получения более подробной информации.

Наконец, это может быть объединено с CTE , чтобы получить имена соответствующих вещей.

with thing_matches as (
  select thing_id as id
  from thing_values
  where value in ('beta', 'gamma')
  group by thing_id
  having count(*) = 2
)
select t.name
from things t
join thing_matches tm on tm.id = t.id

dbfiddle для демонстрации.


Это может показаться намного более сложной работой, чем просто сохранение, скажем, поля, разделенного запятыми. И это так, но только в краткосрочной перспективе. В долгосрочной перспективе, когда вы поймете, что SQL представляет собой реляционный взгляд на мир, становится более естественным организовать вещи таким образом, а также намного эффективнее.

Поиск по списку через запятую требует база данных для сканирования каждой строки для каждого запроса, который хочет найти вещь с заданным значением. Это полное сканирование таблиц, и они являются причиной снижения производительности базы данных. (Другие базы данных имеют более продвинутые индексы, которые могут индексировать этот вид поиска, но AFAIK SQLite - нет.)

Вместо этого where value in ('beta', 'gamma') - это простая проверка на равенство, которую можно выполнить для индекса, который мы установили с помощью unique(value, thing_id). Ограничения уникальности выполняются с помощью индекса, и, помещая value, сначала SQLite может использовать этот уникальный индекс для поиска по значению.

Таким образом, вышеупомянутый запрос будет очень быстро выполняться для большой таблицы. Принимая во внимание, что запрос like должен сканировать каждую строку таблицы.


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

Наивный подход будет выглядеть примерно так ...

1   ionic   alpha,beta,gamma,theta
2   doric   al,bet,gams,the

where value like '%beta%'  # it matches 1

where value like '%bet%'   # whoopsie, it matches both

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

1   ionic   ,alpha,beta,gamma,theta,
2   doric   ,al,bet,gams,the,

where value like '%,beta,%'  # it matches 1

where value like '%,bet,%'     # it matches 2

Подобные крайние случаи являются еще одной причиной, по которой объединяющая таблица стоит проблем.

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