Запрос postgreSQL - поиск наличия идентификатора в любом поле записи - PullRequest
1 голос
/ 02 февраля 2012

У меня есть две таблицы, которые выглядят следующим образом

инструменты

id | part name
---------------
 0 | hammer
 1 | sickle
 2 | axe

человек

 personID | ownedTool1 | ownedTool2 | ownedTool3 ..... ownedTool20
 ------------------------------------------------------------------
    0     |    2       |     1      |     3     ... ...    0

Я пытаюсь выяснить, сколько людей владеют определенным инструментом. Человек не может владеть несколькими копиями одного и того же инструмента.

Единственный способ, которым я могу думать об этом, это что-то вроде

SELECT COUNT(*)
FROM tools JOIN people ON tools.id = people.ownedTool1.id OR tools.id = people.ownedTool2 ... and so on
WHERE tools.id = 0

чтобы узнать количество людей, которым принадлежат молотки. Я полагаю, что это сработает, однако это подразумевает наличие 20 OR операторов в запросе. Конечно, есть более подходящий способ формирования такого запроса, и мне интересно узнать, как это сделать.

Ответы [ 3 ]

2 голосов
/ 02 февраля 2012

Если вы не можете изменить модель (и я уверен, что вы нам это скажете), то единственный разумный способ обойти эту поврежденную модель данных - это создать представление, которое даст вам нормализованное представление (предназначенное для каламбура) наданные:

create view normalized_people
as
select personid, 
       ownedTool1 as toolid
from people
union all
select personid, 
       ownedTool2 as toolid
from people
select personid, 
       ownedTool3 as toolid
from people
... you get the picture ...

Тогда ваш запрос так же прост, как

select count(personid)
from normalized_people
where toolid = 0;
2 голосов
/ 02 февраля 2012

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

SELECT count(*) AS person_ct
FROM   tbl t
WHERE  translate((t)::text, '()', ',,')
       ~~ ('%,' || @desired_tool_id::text || ',%')

Или, если первый столбец - person_id, и вы хотите исключить его из поиска:

SELECT count(*) AS person_ct
FROM   tbl t
WHERE  replace((t)::text, ')', ',')
       ~~ ('%,' || @desired_tool_id::text || ',%')

Объяснение

  • Каждая таблица сопровождается соответствующим составным типом в PostgreSQL. Таким образом, вы можете запросить любую таблицу следующим образом:

    SELECT (tbl) FROM tbl;
    

    Возвращает один столбец на строку, содержащий всю строку.

  • PostgreSQL может привести такой тип строки к тексту одним махом: (tbl)::text

  • Я заменяю оба символа () запятой ,, поэтому каждые значение строки отделяется запятыми ,.

    Мой второй запрос не переводит открывающую скобку, поэтому первый столбец (person_id) исключен из поиска.

  • Теперь я могу искать во всех столбцах с помощью простого выражения LIKE (~~), используя желаемое число, разделенное запятыми ~~ %,17,%

Вуаля: все сделано одной простой командой. Это надежно, если в вашей таблице нет столбцов типа text или int[], которые также могут содержать ,17, в пределах своих значений, или дополнительных столбцов с числами, которые могут привести к ложным срабатываниям.

Он не даст чудес производительности, поскольку не может использовать стандартные индексы. (Вы могли бы создать индекс GiST или GIN для выражения, используя модуль tgrm в pg 9.1, но это уже другая история.)

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

2 голосов
/ 02 февраля 2012

Во-первых, у вас не должно быть 20 столбцов, каждый из которых может содержать идентификатор.Вы должны правильно установить нормализованную схему.Если инструмент может принадлежать только одному пользователю, но у пользователя может быть несколько инструментов, необходимо установить отношение Один ко многим .Каждый инструмент будет иметь идентификатор пользователя в своей строке, который сопоставляется с пользователем, которому он принадлежит.Если инструмент может принадлежать одному или нескольким пользователям, вам необходимо установить отношение Многие ко многим .Для этого потребуется промежуточная таблица, содержащая строки сопоставлений user_id и tool_id.Правильно подобранная схема сделает запрос, который вы хотите выполнить, тривиальным.

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

Примерно так:

SELECT COUNT(ID) FROM UserTools Where ToolID = @desired_tool_id

Поиск в Googleтермины, которые я выделил, должны указать вам правильное направление.Если вы застряли с этой схемой, то способ, который вы указали, - единственный способ сделать это.

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