选择所有EAV方案价格的产品

时间:2016-06-07 19:33:09

标签: sql postgresql join

这是一个通用的SQL问题。我有一个查询,从Products中选择所有行以及其他表中的额外信息。问题在于它的EAV方案和最后的关系以某种方式被逆转并且加入了中断。

要求是:

  • 列出所有带有群组的产品
  • 如果' Price'在值表中可以添加此信息
  • 明确地说:如果' Price'不可用,应该有没有价格信息的产品行
  • 产品无法重复
  • 此外: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省略了没有' Price'值。

2 个答案:

答案 0 :(得分:2)

明确定义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

答案 1 :(得分:1)

你可以这样做:

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