从子查询中选择多行

时间:2020-01-15 08:21:52

标签: mysql sql group-by subquery

我在使用动态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

示例:

我想找到所有蓝色的对象。在这种情况下:立方体,正方形

我想找到所有 blue big 的对象。在这种情况下: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 个答案:

答案 0 :(得分:1)

一种选择是使用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

答案 1 :(得分:0)

类似地...

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;

如果值在不同的上下文中具有不同的含义,那么您还需要字段ID。一种简短(但效率低下的写作方式,WHERE (fieldid,value) IN((12,4),(etc))

答案 2 :(得分:0)

  • EAV模式有很多问题。

  • 对于fieldvalue,请摆脱id,而改为PRIMARY KEY(objid, fieldid)

  • 也为fieldvalue拥有INDEX(fieldid)。 (由于时间太长,可能不值得添加value。)

  • 避免使用IN ( SELECT ... );更改为JOIN .. ONEXISTS( SELECT 1 ... )

我想找到所有蓝色的对象。在这种情况下:立方体,正方形

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)

我想找到所有蓝色和大的物体。在这种情况下:SQUARE

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;

这里的哲学可能是“从您知道的开始;朝着您需要知道的方向努力”,而不是“让我们检查每个对象以查看哪些对象适用”。

相关问题