Нужна помощь с оператором SELECT - PullRequest
1 голос
/ 09 апреля 2010

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

TABLE RECORDS
id
name

TABLE SEARCHTAGS 
id
recordid
name

Я хочу иметь возможность ВЫБРАТЬ записи на основе имеющихся у них тегов поиска. Например, я хочу иметь возможность ВЫБРАТЬ все записи, которые имеют теги поиска:

(1 OR 2 OR 5) AND (6 OR 7) AND (10)

Используя вышеупомянутую структуру данных, я не уверен, как структурировать SQL для достижения этой цели.

Есть предложения?

Спасибо!

Ответы [ 6 ]

2 голосов
/ 09 апреля 2010

Вы можете попробовать следующее:

SELECT    r.id, r.name
FROM      records r
WHERE     EXISTS (SELECT NULL FROM searchtags WHERE recordid = r.id AND id IN (1, 2, 5)) AND 
          EXISTS (SELECT NULL FROM searchtags WHERE recordid = r.id AND id IN (6, 7)) AND 
          EXISTS (SELECT NULL FROM searchtags WHERE recordid = r.id AND id IN (10));

Контрольный пример: обратите внимание, что только записи 1 и 4 будут удовлетворять критериям запроса.

CREATE TABLE records (id int, name varchar(10));
CREATE TABLE searchtags (id int, recordid int);

INSERT INTO records VALUES (1, 'a');
INSERT INTO records VALUES (2, 'b');
INSERT INTO records VALUES (3, 'c');
INSERT INTO records VALUES (4, 'd');

INSERT INTO searchtags VALUES (1,  1);
INSERT INTO searchtags VALUES (2,  1);
INSERT INTO searchtags VALUES (6,  1);
INSERT INTO searchtags VALUES (10, 1);
INSERT INTO searchtags VALUES (1,  2);
INSERT INTO searchtags VALUES (2,  2);
INSERT INTO searchtags VALUES (3,  2);
INSERT INTO searchtags VALUES (1,  3);
INSERT INTO searchtags VALUES (10, 3);
INSERT INTO searchtags VALUES (5,  4);
INSERT INTO searchtags VALUES (7,  4);
INSERT INTO searchtags VALUES (10, 4);

Результат:

+------+------+
| id   | name |
+------+------+
|    1 | a    |
|    4 | d    |
+------+------+
2 rows in set (0.01 sec)
1 голос
/ 09 апреля 2010
SELECT
  id, name
FROM
  records
WHERE
  EXISTS (
    SELECT 1 FROM searchtags WHERE recordid = records.id AND id IN (1, 2, 5)
  )
  AND EXISTS (
    SELECT 1 FROM searchtags WHERE recordid = records.id AND id IN (6, 7)
  )
  AND EXISTS (
    SELECT 1 FROM searchtags WHERE recordid = records.id AND id IN (10)
  )
0 голосов
/ 09 апреля 2010

Я впервые неправильно прочитал вопрос и подумал, что он спрашивает

(1 AND 2 AND 5) OR (6 AND 7) OR (10)

вместо правильного

(1 OR 2 OR 5) AND (6 OR 7) AND (10)

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

Актуальный вопрос

Я не могу сделать намного лучше, чем выбранный ответ на фактический вопрос ( Запрос 1 ):

SELECT r.id, r.name
  FROM Records AS r
 WHERE EXISTS(SELECT * FROM SearchTags AS s
               WHERE r.id = s.recordid AND s.id IN (1, 2, 5))
   AND EXISTS(SELECT * FROM SearchTags AS s
               WHERE r.id = s.recordid AND s.id IN (6, 7))
   AND EXISTS(SELECT * FROM SearchTags AS s
               WHERE r.id = s.recordid AND s.id IN (10));

Это можно записать как объединение трех псевдонимов для таблицы SearchTags.

Альтернативный вопрос

Есть несколько способов ответить на альтернативу - я думаю, что это наиболее аккуратный и расширяемый вариант. Очевидно, что один элемент (10) прост ( Запрос 2 ):

SELECT r.id, r.name
  FROM records AS r JOIN searchtags AS t ON r.id = t.recordid
 WHERE t.id IN (10)  -- or '= 10' but IN is consistent with what follows

Два элемента (6 или 7) можно выполнить с помощью ( Запрос 3 ):

SELECT r.id, r.name
  FROM records AS r JOIN searchtags AS t ON r.id = t.recordid
 WHERE t.id IN (6, 7)
 GROUP BY r.id, r.name
HAVING COUNT(*) = 2

Три элемента (1, 2, 5) можно выполнить с помощью ( Запрос 4 ):

SELECT r.id, r.name
  FROM records AS r JOIN searchtags AS t ON r.id = t.recordid
 WHERE t.id IN (1, 2, 5)
 GROUP BY r.id, r.name
HAVING COUNT(*) = 3

И вся коллекция может быть СОЮЗОМ из трех терминов.

Обобщение решений

Недостатком этого решения является то, что SQL должен создаваться вручную для каждого набора элементов. Если вы хотите автоматизировать «генерацию SQL», вам нужны контрольные данные - наборы интересных поисковых тегов - в таблице:

CREATE TABLE InterestingTags(GroupID INTEGER, TagID INTEGER);
INSERT INTO InterestingTags(1, 1);
INSERT INTO InterestingTags(1, 2);
INSERT INTO InterestingTags(1, 5);
INSERT INTO InterestingTags(2, 6);
INSERT INTO InterestingTags(2, 7);
INSERT INTO InterestingTags(3, 10);

Для запроса, запрашивающего '(1 ИЛИ ИЛИ 5) И (...)' (конъюнктивная нормальная форма), вы можете написать ( Запрос 5 ):

SELECT r.id, r.name
  FROM records AS r JOIN
       searchtags AS s ON r.id = s.recordID JOIN
       interestingtags AS t ON s.id = t.tagID
 GROUP BY r.id, r.name
HAVING COUNT(DISTINCT t.GroupID) = (SELECT COUNT(DISTINCT GroupID)
                                      FROM InterestingTags);

Это проверяет, что количество различных «интересных групп тегов» для данной записи равно общему количеству «интересных групп тегов».

Для запроса, запрашивающего '(1 И 2 ​​И 5) ИЛИ (...)' (дизъюнктивная нормальная форма), вы можете написать объединение с InterestingTags и проверить, что запись имеет столько записей, сколько и группа тегов. ( Запрос 6 ):

SELECT i.id, i.name
  FROM (SELECT q.id, q.name, c.GroupSize,
               COUNT(DISTINCT t.GroupID) AS GroupCount
          FROM records AS q JOIN
               searchtags AS s ON q.id = s.recordID JOIN
               interestingtags AS t ON s.id = t.tagID JOIN
               (SELECT GroupID, COUNT(*) AS GroupSize
                  FROM InterestingTags
                 GROUP BY GroupID) AS c ON c.GroupID = t.GroupID
         GROUP BY q.id, q.name, c.GroupSize
       ) AS i
 WHERE i.GroupCount = i.GroupSize;

Данные испытаний

Я взял тестовые данные из ответа Даниэля Вассало и дополнил их некоторыми дополнительными значениями:

CREATE TABLE records (id int, name varchar(10));
CREATE TABLE searchtags (id int, recordid int);

INSERT INTO records VALUES (1, 'a');
INSERT INTO records VALUES (2, 'b');
INSERT INTO records VALUES (3, 'c');
INSERT INTO records VALUES (4, 'd');

INSERT INTO records VALUES (11, 'A11');
INSERT INTO records VALUES (21, 'B12');
INSERT INTO records VALUES (31, 'C13');
INSERT INTO records VALUES (41, 'D14');
INSERT INTO records VALUES (51, 'E15');
INSERT INTO records VALUES (61, 'F16');

INSERT INTO searchtags VALUES (1,  1);
INSERT INTO searchtags VALUES (2,  1);
INSERT INTO searchtags VALUES (6,  1);
INSERT INTO searchtags VALUES (10, 1);
INSERT INTO searchtags VALUES (1,  2);
INSERT INTO searchtags VALUES (2,  2);
INSERT INTO searchtags VALUES (3,  2);
INSERT INTO searchtags VALUES (1,  3);
INSERT INTO searchtags VALUES (10, 3);
INSERT INTO searchtags VALUES (5,  4);
INSERT INTO searchtags VALUES (7,  4);
INSERT INTO searchtags VALUES (10, 4);

INSERT INTO searchtags VALUES (1,  11);
INSERT INTO searchtags VALUES (2,  11);
INSERT INTO searchtags VALUES (5,  11);
INSERT INTO searchtags VALUES (6,  21);
INSERT INTO searchtags VALUES (7,  21);
INSERT INTO searchtags VALUES (10, 31);
INSERT INTO searchtags VALUES (1,  41);
INSERT INTO searchtags VALUES (6,  41);
INSERT INTO searchtags VALUES (10, 41);
INSERT INTO searchtags VALUES (2,  51);
INSERT INTO searchtags VALUES (5,  51);
INSERT INTO searchtags VALUES (10, 51);
INSERT INTO searchtags VALUES (7,  61);
INSERT INTO searchtags VALUES (2,  61);
INSERT INTO searchtags VALUES (1,  61);

CREATE TABLE InterestingTags(GroupID INTEGER, TagID INTEGER);
INSERT INTO InterestingTags VALUES(1, 1);
INSERT INTO InterestingTags VALUES(1, 2);
INSERT INTO InterestingTags VALUES(1, 5);
INSERT INTO InterestingTags VALUES(2, 6);
INSERT INTO InterestingTags VALUES(2, 7);
INSERT INTO InterestingTags VALUES(3, 10);

Результаты теста

Выводы, которые я получил, были:

Запрос 1

1   a
4   d
41  D14

Запрос 2

1   a
3   c
4   d
31  C13
41  D14
51  E15

Запрос 3

21  B12

Запрос 4

11  A11

Запрос 5

1   a
41  D14
4   d

Запрос 6

4   d
31  C13
3   c
1   a
41  D14
51  E15

Очевидно, что если бы я хотел вывод в определенном порядке, я бы добавил к запросам предложение ORDER BY.

0 голосов
/ 09 апреля 2010
 select RECORDS.name
    from RECORDS join SEARCHTAGS 
    on RECORDS.id = SEARCHTAGS.recordid
    where RECORDS.id in (1,2,...)
0 голосов
/ 09 апреля 2010

Попробуйте:

SELECT R.*
FROM RECORDS R, SEARCHTAGS S
WHERE R.id == S.recordid
AND S.name in (1,2,5,6,7,10);

Не знаю, нужен ли вам S.name или S.id, но это пример.

0 голосов
/ 09 апреля 2010

не знаю, как это сделать в MySQL, но в T-SQL вы можете сделать что-то вроде:

SELECT id, name FROM RECORDS where id in (SELECT recordid from SEARCHTAGS where id in (1,2,5,6,7,10))

Возможно, я не совсем понимаю ваш вопрос ... но я приложил все усилия.

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