Выберите из подзапроса, где несколько строк - PullRequest
0 голосов
/ 15 января 2020

У меня возникли проблемы с динамическим c SQL поисковым запросом, где я хотел бы найти все объекты, в которых есть какие-либо поля, которые я ищу. Ниже приведена структура данных.

objectregister                      fieldvalue
| id |  name   |         | id |  objid  | fieldid | value  (illustration)  |
+----+---------+         +----+---------+---------+------------------------+
| 1  |  CUBE   |         | 1  |    1    |    12   |   4    (BLUE)          |
| 2  |  SQUARE |         | 2  |    2    |    12   |   4    (BLUE)          |
                         | 3  |    1    |    22   |   27   (SMALL)         |
                         | 4  |    2    |    22   |   9    (BIG)           |

Испытательный стенд со структурой БД:

CREATE TABLE IF NOT EXISTS `objectregister` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(1024) COLLATE utf8_swedish_ci NOT NULL,
  PRIMARY KEY (`id`)
);

INSERT INTO `objectregister` (`id`, `name` ) VALUES
(1, 'CUBE'),
(2, 'SQUARE');

CREATE TABLE IF NOT EXISTS `fieldvalue` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `objid` int(11) NOT NULL,
  `fieldid` int(11) NOT NULL,
  `value` varchar(2048) COLLATE utf8_swedish_ci NOT NULL,
  UNIQUE KEY `id` (`id`)
);

INSERT INTO `fieldvalue` (`id`, `objid`, `fieldid`, `value`) VALUES
(1, 1, 12, 4),
(2, 2, 12, 4),
(3, 1, 22, 27),
(4, 2, 22, 9);


SELECT `id`, `name` FROM `objectregister` WHERE id IN 
(
    SELECT * FROM 
    (
        SELECT `objid` 
        FROM `fieldvalue` 
        WHERE 1  AND ( (fieldvalue.fieldid = '12' AND fieldvalue.value = '4')  OR (fieldvalue.fieldid = '22' AND fieldvalue.value = '27')  ) 
        GROUP BY objid 
    ) as subquery
  )

  +----+--------+
  | id | name   |
  +----+--------+
  |  1 | CUBE   |
  |  2 | SQUARE |
  +----+--------+

https://rextester.com/XTR72354

Пример:

Я хотел бы найти все объекты, которые синие . В этом случае: CUBE, SQUARE

Я хотел бы найти все объекты, которые синие И большие . В этом случае: SQUARE

Поэтому, я полагаю, мне сначала нужно будет выбрать все возможные objid , которые соответствуют любому поиску в подзапросе. Получите их в одну строку, чтобы я позже мог выбрать, ГДЕ они оба совпадают? Но как мне это сделать? Нужно ли мне несколько SUB запросов союзов? GROUP_CONCAT? Таблица TEMP?

Это текущий этап моего запроса на момент написания (который будет возвращать обе строки в подзапросе, но для внешнего запроса потребуется WHERE):

SELECT `id`, `name` FROM `objectregister` WHERE id IN 
(
    SELECT * FROM 
    (
        SELECT `objid` 
        FROM `fieldvalue` 
        WHERE 1  AND ( (fieldvalue.fieldid = '12' AND fieldvalue.value = '4')  OR (fieldvalue.fieldid = '22' AND fieldvalue.value = '27')  ) 
        GROUP BY objid 
    ) as subquery
)

Ответы [ 3 ]

1 голос
/ 15 января 2020

Один из вариантов - использовать exists:

select r.*
from objectregister r
where 
    exists (
        select 1 from fieldvalue f where f.objid = r.id and f.fieldid = 12 and f.value = 4
    ) and exists (
        select 1 from fieldvalue f where f.objid = r.id and f.fieldid = 22 and f.value = 27
    )

При индексе fieldvalue(objid, fieldid, value) это должно быть эффективным решением.

Вы можете объединять, объединять и фильтровать с having предложение:

select r.id, r.name
from objectregister r
inner join fieldvalue f on f.objid = r.id
group by r.id, r.name
having max(f.fieldid = 12 and f.value = 4) = 1 and max(f.fieldid = 22 and f.value = 27) = 1
0 голосов
/ 04 февраля 2020
  • Схема EAV имеет много проблем.

  • Для fieldvalue, избавьтесь от id, вместо этого наберите PRIMARY KEY(objid, fieldid).

  • Также для fieldvalue, есть INDEX(fieldid). (Вероятно, не стоит включать value, поскольку он слишком длинный.)

  • Избегать IN ( SELECT ... ); изменить на JOIN .. ON или EXISTS( SELECT 1 ... )

Я хотел бы найти все объекты синего цвета. В этом случае: CUBE, SQUARE

SELECT obj.name
    FROM ( SELECT objid
               FROM fieldvalue
               WHERE fieldid = '12'
                 AND `value` = '4'
         ) AS x
    JOIN objectregister AS obj ON x.objid = obj.id;

В качестве альтернативы измените первую строку на

SELECT GROUP_CONCAT(obj.name)

Я хотел бы найти все объекты, синие И большие , В этом случае: КВАДРАТ

SELECT obj.name
    FROM ( SELECT objid FROM fieldvalue WHERE fieldid = '12' AND `value` =  '4' ) AS x
    JOIN ( SELECT objid FROM fieldvalue WHERE fieldid = '22' AND `value` = '27' ) AS y
                               USING(objid)
    JOIN objectregister AS obj ON x.objid = obj.id;

Философия здесь может быть «Начните с того, что вы знаете; работайте в направлении того, что вам нужно знать» вместо «Давайте проверим каждый объект, чтобы увидеть, какие из них применимы» .

0 голосов
/ 15 января 2020

Аналогично ...

SELECT o.*
  FROM objectregister o 
  JOIN fieldvalue v 
    ON v.objid = o.id 
 WHERE value IN (4,9) 
 GROUP 
    BY o.id 
HAVING COUNT(DISTINCT value) = 2;

Если значения имеют разные значения в разных контекстах, то вам также понадобятся полевые идентификаторы. Краткий (но неэффективный способ написания WHERE (fieldid,value) IN((12,4),(etc))

...