SQL - 查询缺少属性的实体 - 属性 - 值(EAV)

时间:2016-03-16 20:22:16

标签: mysql sql entity-attribute-value

我的数据库等同于下表:

id |  foo |  bar
---+------+-----
 1 |    5 |    6
 2 |    7 | NULL

但不幸的是,实现为Entity-Attribute-Value:

CREATE TABLE obj(id INTEGER NOT NULL PRIMARY KEY);
CREATE TABLE attrdef(id INTEGER NOT NULL PRIMARY KEY, name VARCHAR(4));
CREATE TABLE attr(obj_id INTEGER NOT NULL, attrdef_id INTEGER NOT NULL, value INTEGER NOT NULL);

INSERT INTO obj VALUES(1);
INSERT INTO obj VALUES(2);

INSERT INTO attrdef VALUES(3, 'foo');
INSERT INTO attrdef VALUES(4, 'bar');

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

我需要查询该数据库以“正确”的形式获取数据 - 就像在示例表中一样。我试过了:

SELECT obj.id, foo.value, bar.value
FROM obj 
LEFT JOIN attr foo ON (obj.id = foo.obj_id)
LEFT JOIN attrdef foo_def ON (foo.attrdef_id = foo_def.id)
LEFT JOIN attr bar ON (obj.id = bar.obj_id)
LEFT JOIN attrdef bar_def ON (bar.attrdef_id = bar_def.id)
WHERE foo_def.name = 'foo' AND bar_def.name = 'bar';

但缺少第二行:

id |  foo |  bar
---+------+-----
 1 |    5 |    6

SELECT obj.id,
    MAX(CASE WHEN name='foo' THEN value ELSE NULL END) foo,
    MAX(CASE WHEN name='bar' THEN value ELSE NULL END) bar
FROM obj LEFT JOIN attr ON (obj.id = attr.obj_id)
LEFT JOIN attrdef ON (attr.attrdef_id = attrdef.id)
GROUP BY obj.id;

给出了正确的结果:

id |  foo |  bar
---+------+-----
 1 |    5 |    6
 2 |    7 | NULL

但此查询的执行情况是不可接受的。

我想要标准的SQL查询,但是特定的MySQL特定解决方案将不胜感激。

2 个答案:

答案 0 :(得分:3)

您只需将条件移至on子句:

SELECT obj.id, foo.value, bar.value
FROM obj LEFT JOIN
     attr foo
     ON obj.id = foo.obj_id LEFT JOIN
     attrdef foo_def
     ON foo.attrdef_id = foo_def.id AND foo_def.name = 'foo' LEFT JOIN
     attr bar
     ON obj.id = bar.obj_id LEFT JOIN
     attrdef bar_def
     ON bar.attrdef_id = bar_def.id AND bar_def.name = 'bar';

对于聚合方法,我会选择:

SELECT obj.id,
       MAX(CASE WHEN name = 'foo' THEN value END) foo,
       MAX(CASE WHEN name = 'bar' THEN value END) bar
FROM obj LEFT JOIN
     attr
     ON obj.id = attr.obj_id LEFT JOIN
     attrdef
     ON attr.attrdef_id = attrdef.id
WHERE name IN ('foo', 'bar')
GROUP BY obj.id;

在这种情况下可能不需要left join(取决于缺失值的分布)。无论如何,如果您开始查看越来越多的属性,JOIN方法需要的时间越来越长。 GROUP BY方法具有大致相同的性能。

编辑:

正确的查询是:

SELECT obj.id, foo.value, bar.value
FROM obj LEFT JOIN
     (attr foo JOIN
      attrdef foo_def
      ON foo.attrdef_id = foo_def.id AND foo_def.name = 'foo'
     )
     ON obj.id = foo.obj_id LEFT JOIN
     (attr bar JOIN
      attrdef bar_def
      ON bar.attrdef_id = bar_def.id AND bar_def.name = 'bar'
     )
     ON obj.id = bar.obj_id ;

Here是SQL小提琴。

答案 1 :(得分:0)

当你在caluse:

的地方执行此操作时
AND bar_def.name = 'bar';

您将bar_def上的左连接转换为内连接。与你放在Foo_def上的条件相同。