«Лучший» метод для сопоставления наборов ID в операторе MySQL - PullRequest
0 голосов
/ 10 мая 2009

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

Редактировать: Идентификаторы, которые я сопоставляю, являются частью отношения многих ко многим. Структура такая:

Статьи ArticleKeywords Ключевые слова

Статья может иметь несколько ключевых слов, связанных с ней таблицей ArticleKeywords. В настоящее время я использую выражение IN, чтобы сопоставить любой из идентификаторов ключевых слов с записями. Однако я бы хотел сопоставить определенные записи с небольшими группами ключевых слов, т. Е. Для сопоставления записи должно появиться ключевое слово с другим ключевым словом.

Ток: ... И ID IN ('25', '566', '156', '166', '7345')

Более конкретно: ... И ((id = '25' И id = '566') ИЛИ (id = '156' И id = '166') ИЛИ (id = '7345'))

Хотя второй вариант может сработать, я думаю, что он, вероятно, не очень подкован с точки зрения производительности. Есть ли другой способ, которым это может быть сделано, или я должен идти об этом по-другому?

Спасибо за вашу помощь.

Ответы [ 3 ]

3 голосов
/ 10 мая 2009

Отредактировано на основе комментариев. Допустим, вы ищете статьи, которые:

  • Имейте ключевое слово с именем 'a1'
  • Или, пусть обе клавиатуры имеют названия 'b1' и 'b2'
  • Или используйте ключевое слово "c1", "c2" или "c3"

Вы можете запросить как:

select a.id
from Articles a 
inner join ArticleKeywords ak on ak.articleid = a.id
inner join Keywords k on k.id = ak.keywordid
group by a.id
having 
    sum(case when k.name in ('a1') then 1 else 0 end) = 1
    or sum(case when k.name in ('b1','b2') then 1 else 0 end) = 2 
    or sum(case when k.name in ('c1','c2,'c3') then 1 else 0 end) > 0

Согласно комментарию SquareCog, вы можете значительно повысить производительность с помощью раннего предложения WHERE. Предложение ограничит группировку только соответствующими ключевыми словами. В вышеприведенном запросе добавьте WHERE непосредственно перед HAVING:

...
inner join Keywords k on k.id = ak.keywordid
where k.name in ('a1','b1','b2','c1','c2','c3')
group by a.id
...

Вы можете получить другие сведения о статье (статьях), например:

select *
from Articles
where id in (
    ...query from above here...
)

Допустим, у вас есть таблица, содержащая группы для поиска, определенные как:

groupid - keywordid
1 - 1
1 - 2
2 - 3

Значение статьи должно совпадать ((ключевое слово 1 и ключевое слово2) или ключевое слово3). Тогда вы можете запросить так:

select ak.articleid
from ArticleKeywords ak
inner join Search s on ak.keywordid = s.keywordid
group by s.searchgroup, ak.articleid
having count(*) = (
    select count(*) from #Search s2 where s2.Searchgroup = s.SearchGroup
)
2 голосов
/ 10 мая 2009

Ну, ваш второй вариант никогда не сработает ...

((id = '25' AND id = '566') --For this to return the column `id` would have to = both 25 & 566 which it obviously can't
OR 
(id = '156' AND id = '166') --For this to return the column `id` would have to = both 156 & 166 which it obviously can't
OR 
(id = '7345'))

Чего именно вы пытаетесь достичь ... Что вы подразумеваете под "сопоставлять определенные идентификаторы с другими, чтобы они отображались вместе" Вы имеете в виду в последовательных строках?

1 голос
/ 10 мая 2009

Ответ , данный Andomar с использованием GROUP BY и HAVING, является распространенным способом решения такого рода проблем, но он не очень хорошо работает. GROUP BY часто вызывает временную таблицу.

Вернуться к вашему примеру:

... И ((id = '25' И id = '566') ИЛИ (id = '156' И id = '166') ИЛИ (id = '7345'))

Это никогда не может быть правдой. Условия в предложении WHERE применяются к одной строке за раз. Столбец id никогда не может иметь двух значений в данной строке. Я концептуально понимаю, на что вы собираетесь тестировать, но SQL так не работает.

Когда вам нужно написать условие, которое включает значения, встречающиеся в нескольких строках, другое решение - использовать self-join .

SELECT *
FROM ArticleKeywords k1
 LEFT OUTER JOIN ArticleKeywords k2 ON (k1.article_id = k2.article_id)
WHERE k1.keyword_id = '7345'
 OR (k1.keyword_id = '25' AND k2.keyword_id = '566')
 OR (k1.keyword_id = '156' AND k2.keyword_id = '166');

Большинство людей назвали бы k1 и k2 "псевдонимами таблиц". Но если вместо этого вы воспринимаете эти псевдонимы как указывающие на отдельные строки в таблице, то становится понятнее, как писать условия с помощью самостоятельных соединений.

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

Возможно, это решение не сможет эффективно использовать индексы, но оно не будет использовать временную таблицу, как это делает решение GROUP BY. Попробуйте оба решения, используйте EXPLAIN для их анализа, а затем измерьте их производительность для сравнения.

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