简体   繁体   中英

Merging SQL Result rows together

I am working on an EAV storage system for storing schemaless meta data about an object, and am now working on a way to have it searchable, but have most of the processing done by the Database Server.

The table of the EAV is:

`entityID` - INT(11)
`entity` - VARCHAR(128)
`attribute` - VARCHAR(128)
`value` - BLOB

This is my statement:

SELECT 
    `entity`,
    (CASE WHEN `attribute` = 'bob' THEN `value` ELSE NULL END) `bob`,
    (CASE WHEN `attribute` = 'dean' THEN `value` ELSE NULL END) `dean`
FROM `eav`

which returns a nice set of rows (as expected) like this:

+----------+------+------+
|  entity  | bob  | dean |
+----------+------+------+
| testEnt  | foo  | NULL | // attribute = bob
+----------+------+------+
| testEnt  | NULL | NULL | // another test attribute
+----------+------+------+
| testEnt  | NULL | NULL | // another test attribute
+----------+------+------+
| testEnt2 | foo  | NULL | // attribute = bob
+----------+------+------+
| testEnt2 | NULL | foo  | // attribute = dean
+----------+------+------+

but when I attach GROUP BY (Entity) , the result turns into this:

+----------+------+------+
|  entity  | bob  | dean |
+----------+------+------+
| testEnt  | foo  | NULL |
+----------+------+------+
| testEnt2 | foo  | NULL |
+----------+------+------+

so using the HAVING syntax after that stops working. Is there a way so that the result returned is:

+----------+------+------+
|  entity  | bob  | dean |
+----------+------+------+
| testEnt  | foo  | NULL |
+----------+------+------+
| testEnt2 | foo  | foo  |
+----------+------+------+

The only problem with is that you did GROUP your record but you haven't aggregate the fields. Try it by aggregating the field using MAX .

SELECT 
    `entity`,
    MAX(CASE WHEN `attribute` = 'bob' THEN `value` ELSE NULL END) `bob`,
    MAX(CASE WHEN `attribute` = 'dean' THEN `value` ELSE NULL END) `dean`
FROM `eav`
GROUP BY `entity`

Apply an aggregate function with the CASE and the GROUP BY . Since the values are strings you can use either a MAX() or MIN() to return the result:

SELECT 
    `entity`,
    Max(CASE WHEN `attribute` = 'bob' THEN `value` ELSE NULL END) `bob`,
    Max(CASE WHEN `attribute` = 'dean' THEN `value` ELSE NULL END) `dean`
FROM `eav`
group by `entity`

See SQL Fiddle with Demo

In the event, you have an unknown number of attribute values to return, you can use a prepared statement to get the result:

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'max(case when attribute = ''',
      attribute,
      ''' then value end) AS ',
      attribute
    )
  ) INTO @sql
FROM eav;

SET @sql = CONCAT('SELECT entity, ', @sql, ' 
                  FROM eav 
                   GROUP BY entity');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

See SQL Fiddle with Demo

The result for both will be:

|   ENTITY | BOB |   DEAN |
---------------------------
|  testEnt | foo | (null) |
| testEnt2 | foo |    foo |

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM