选择 EAV 方案中价格的所有产品
Posted
技术标签:
【中文标题】选择 EAV 方案中价格的所有产品【英文标题】:Select all products with prices in EAV scheme 【发布时间】:2016-06-07 19:33:08 【问题描述】:这是一个通用的 SQL 问题。我有一个查询,它从 Products 中选择所有行以及来自其他表的额外信息。问题是它是 EAV 方案,最后一个关系以某种方式颠倒并加入中断。
要求是:
列出所有带有组的产品 如果值表中的“价格”可用,请添加此信息 明确:如果“价格”不可用,则应该有没有价格信息的产品行 产品不能重复 另外:DISTINCT 是不可能的我有一个使用子查询过滤值的有效查询(如下),但我需要摆脱它。我只能使用连接。
SQL Fiddle : http://sqlfiddle.com/#!15/0576b/8
create table Products (
id int primary key,
groupId int,
code varchar(100)
);
create table Groups (
id int primary key,
code varchar(100)
);
create table Values (
id int primary key,
productId int,
typeId int,
value varchar(100)
);
create table ValueTypes (
id int primary key,
name varchar(100)
);
insert into Products values (1, 1, 'P1');
insert into Products values (2, 2, 'P2');
insert into Groups values (1, 'C1');
insert into Groups values (2, 'C2');
insert into Values values (1, 1, 1, 'Aqua');
insert into Values values (2, 1, 2, '$5');
insert into ValueTypes values (1, 'Name');
insert into ValueTypes values (2, 'Price');
我的查询有效:
SELECT *
FROM Products p
INNER JOIN Groups g ON p.groupId = g.id
LEFT JOIN Values v ON v.productId = p.id AND v.typeId = (SELECT id FROM ValueTypes WHERE name = 'Price')
问题是,如何重写它以使用连接而不是子查询?
我试过了:
SELECT *
FROM Products p
INNER JOIN Groups g ON p.groupId = g.id
LEFT JOIN Values v ON v.productId = p.id
LEFT JOIN ValueTypes vt ON vt.id = v.typeId AND vt.name = 'Price'
但它返回重复的产品 P1。另一方面,INNER JOIN 省略了没有“价格”值的产品。
【问题讨论】:
这是 sql server 吗?数据加 1 PostgreSQL,但查询应该没关系。 【参考方案1】:明确定义 JOIN 顺序
SELECT *
FROM Products p
INNER JOIN Groups g ON p.groupId = g.id
LEFT JOIN ( --product price
SELECT productId, value
FROM Values v2
JOIN ValueTypes vt ON vt.id = v2.typeId AND vt.name = 'Price'
) v ON v.productId = p.id;
编辑 还有 2 个 JOIN 版本。与上述版本相比,优化器产生不同的计划
SELECT *
FROM ValueTypes vt
INNER JOIN Products p ON vt.name = 'Price'
INNER JOIN Groups g ON p.groupId = g.id
LEFT JOIN Values v ON v.productId = p.id AND v.typeId = vt.id;
或略有不同的 v3
SELECT *
FROM (SELECT id FROM ValueTypes WHERE name = 'Price') vt
CROSS JOIN Products p
INNER JOIN Groups g ON p.groupId = g.id
LEFT JOIN Values v ON v.productId = p.id AND v.typeId = vt.id
【讨论】:
你期望它比我的子查询更快吗?明天去检查;) 这似乎比我使用子查询的查询要慢一些。我的标称时间是 12,5 s +- 5%;您对子查询的 JOIN 给出 13s + 20%。但我想在某些情况下这种结构会更好。 我认为子查询在正确的地方。如果您仍然只需要 JOIN 查询,请查看更多选项,已编辑。 FROM 以 ValueTypes 开头的版本获胜 :)【参考方案2】:你可以这样做:
SELECT p.id, p.code, g.id, g.code,
max(case when vt.name='Price'
then v.value
else null end) as price
FROM Products p
LEFT JOIN Groups g ON p.groupId = g.id
LEFT JOIN Values v ON v.productId = p.id
LEFT join ValueTypes vt ON v.typeId = vt.id
group by p.id, p.code, g.id, g.code
在这里查看它的工作原理:http://sqlfiddle.com/#!15/0576b/36
【讨论】:
返回的行数没问题,但是 (1) 'Price' 不是数字,所以我只返回了 NULL; (2) 这个查询太慢了,大约慢了 180%(30 秒) 1 - 它不是价格我在查询中返回它是v.value
根据您的要求 explicitly: if 'Price' is not available, there should be Product row without Price information
2- 它不应该那么慢,分析这样的事情我想要查看查询的解释计划,因此我可以建议一些索引,或者您可以显示具有定义索引的表结构。如果速度那么慢,则意味着您的表上没有正确的索引设置。
再次,v.value 不是数字 :) 由于分组,它很慢,我的查询使用相同的表和连接。与 Serg 的回答相同。
现在我明白你为什么说这个`v.value不是数字`聚合函数max
不需要传递给它的值是一个数字,它只是一种技术当其中一个为空时取出重复的结果,这是您的情况。像这样的简单连接不应该像你说的那样太慢。正如我在之前的 cmets 中提到的,这将取决于您拥有的索引。但是没问题。您已经接受了另一个答案:)
"max 不需要传递给它的值是一个数字" - 我不知道。但是,当我运行您编写的查询时,我只收到价格的空值。以上是关于选择 EAV 方案中价格的所有产品的主要内容,如果未能解决你的问题,请参考以下文章