使用子查询和连接

时间:2012-11-04 23:17:03

标签: sql postgresql join

我有以下声明:

  

1997年购物的客户的姓名和地址清单。

所以我认为以下两种方法是正确的,但它们并非如此;为什么呢?

select contactname, address from customers
       inner join orders
       on customers.customerid = orders.customerid
       where date_part('year', orderdate) = 1997
       order by contactname

select contactname, address from customers
       where customerid in
             (select customerid from orders 
              where date_part('year', orderdate) = 1997)
       order by contactname

1 个答案:

答案 0 :(得分:4)

有很多正确的方法可以做到这一点。 EXISTS半连接可能是PostgreSQL中最快的,如果你只想要一个不同客户的列表,除了至少存在1个订单之外没有其他详细信息:

SELECT c.contactname, c.address
FROM   customers c
WHERE  EXISTS (
    SELECT 1
    FROM   orders o 
    WHERE  o.customerid = c.customerid
    AND    o.orderdate >= '1997-1-1'::date
    AND    o.orderdate <  '1998-1-1'::date
    )
ORDER BY contactname;

为什么要使用?

    WHERE  o.orderdate >= '1997-1-1'::date
    AND    o.orderdate <  '1998-1-1'::date

而不是:

    WHERE  date_part('year', orderdate) = 1997

使用您的表达式PostgreSQL必须在检查条件之前为每一行计算一个值。在替代形式中,列(按原样)与两个常数项匹配。这也可以更容易地使用索引。应该更快。

另请注意我如何使用table aliases来简化查询。

由于JOIN,您的第一个查询会遇到行的倍增。如果表customer中的行在表orders中有多个匹配的行,则每个订单都会得到一行。你可以用GROUP BY

解决这个问题
SELECT c.contactname, c.address
FROM   customers c
JOIN   orders o USING (customerid)
WHERE  o.orderdate >= '1997-1-1'::date
AND    o.orderdate <  '1998-1-1'::date
GROUP  BY c.customer_id   --- or whatever is the primary key of c 
ORDER  BY c.contactname

..这是另一种方式。但大多数可能更慢。如果您还想从表orders中检索其他(聚合)数据,则可以使用此表单。

DISTINCT可以替代GROUP BY,在这个简单的情况下大致相同。删除此GROUP BY子句,然后在DISTINCT之后添加SELECT

您还可以使用DISTINCT修复第二个查询,而是使用我的第一个示例。