我的数据库等同于下表:
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特定解决方案将不胜感激。
答案 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上的条件相同。