2009-10-20 48 views
3

最后为了在PostgreSQL: 我有一个有3列一个表:PostgreSQL的选择每个客户的每日期范围

CustomerNum, OrderNum, OrderDate

对于每个日期范围内的每个客户,可能(或可能不)有很多订单。我需要的是位于提供的日期范围内的每个客户的最后一个OrderNum。 我一直在做的是获得客户的ResultSet并分别查询每个客户,但这需要花费太多时间。

是否有任何方式使用子选择选择客户,然后获取每个客户的最后一个OrderNum?

回答

7
select customernum, max(ordernum) 
from table 
where orderdate between '...' and '...' 
group by customernum 

仅此而已。

+0

这就是我要说的。它只需要一个小组。 – 2009-10-20 20:50:21

+0

“OrderNum”是一个顺序值的假设可能不成立,或者简单地说创建顺序可能与日期顺序不匹配(例如,OrderDate可能会标记订单已完成的时间,而不是创建时间,并且你关心最终的时间)。 – 2017-06-21 15:07:39

4
SELECT t1.CustomerNum, t1.OrderNum As LastOrderNum, t1.LastOrderDate 
    FROM table1 As t1 
WHERE t1.OrderDate = (SELECT MAX(t2.OrderDate) 
         FROM table1 t2 
         WHERE t1.CustomerNum = t2.CustomerNum 
          AND t2.OrderDate BETWEEN date1 AND date2) 
    AND t1.OrderDate BETWEEN date1 AND date2 
+0

这将导致一个使用外部全表扫描和内部子查询嵌套循环,因为Postgresql无法通过子查询中的聚合提升OrderDate范围约束。添加AND t1.OrderDate BETWEEN date1和date2将允许它在OrderDate上使用索引来限制结果集。 – 2009-10-20 20:50:24

+0

@Ants:Postgre真的不够聪明,使用子查询作为嵌套循环连接中的外部表吗? – erikkallen 2009-10-20 21:08:27

0

不知道你的客户表的结构或关系,但这应该工作:

SELECT Customer.Num, (
    SELECT OrderNum FROM Orders WHERE CustomerNum = Customer.Num AND OrderDate BETWEEN :start AND :end ORDER BY OrderNum DESC LIMIT 1 
) AS LastOrderNum 
FROM Customer 
0

如果最后一个订单号码,你的意思是最大的订单号码,然后你可以用你的选择作谓语,为客户NUM ,组结果和选择最大:

SELECT CustomerNum, MAX(OrderNum) AS LastOrderNum 
    FROM Orders 
    WHERE 
     CustomerNum IN (SELECT CustomerNum FROM ...) 
      AND 
     OrderDate BETWEEN :first_date AND :last_date 
    GROUP BY CustomerNum 

如果最后一个订单号不一定是最大的订单数,那么你要么需要为每个客户找到最大的订单日期,并与一起加入吧其余的命令找到t他对应的号码:

SELECT O.CustomerNum, O.OrderNum AS LastOrderNum 
    FROM 
     (SELECT CustomerNum, MAX(OrderDate) AS OrderDate 
      FROM Orders 
      WHERE 
       OrderDate BETWEEN :first_date AND :last_date 
        AND 
       CustomerNum IN (SELECT CustomerNum FROM ...) 
      GROUP BY CustomerNum 
     ) AS CustLatest 
      INNER JOIN 
     Orders AS O USING (CustomerNum, OrderDate); 
10

在Postgres的,你也可以使用非标DISTINCT ON条款:

SELECT DISTINCT ON (CustomerNum) CustomerNum, OrderNum, OrderDate 
    FROM Orders 
    WHERE OrderDate BETWEEN 'yesterday' AND 'today' 
    ORDER BY CustomerNum, OrderDate DESC; 

http://www.postgresql.org/docs/current/static/sql-select.html#SQL-DISTINCT

+0

即使它不是标准的SQL,它也是这个问题的最佳答案。谢谢 – jlandercy 2015-10-07 09:01:14

+0

比其他解决方案更快,它在26万行的表格上给了我35秒的结果。也可以使用不在分组中的字段,如: 'SELECT DISTINCT ON(field(s))* FROM ...' – 2017-04-06 19:39:04

0
-- generate some data 
DROP TABLE tmp.orders; 
CREATE TABLE tmp.orders 
    (id INTEGER NOT NULL 
    , odate DATE NOT NULL 
    , payload VARCHAR 
    ) 
    ; 
ALTER TABLE tmp.orders ADD PRIMARY KEY (id,odate); 

INSERT INTO tmp.orders(id,odate,payload) VALUES 
    (1, '2011-10-04' , 'one') 
, (1, '2011-10-24' , 'two') 
, (1, '2011-10-25' , 'three') 
, (1, '2011-10-26' , 'four') 
, (2, '2011-10-23' , 'five') 
, (2, '2011-10-24' , 'six') 
    ; 

-- CTE to the rescue ... 
WITH sel AS (
    SELECT * FROM tmp.orders 
    WHERE odate BETWEEN '2011-10-23' AND '2011-10-24' 
    ) 
SELECT * FROM sel s0 
WHERE NOT EXISTS (
    SELECT * FROM sel sx 
    WHERE sx.id = s0.id 
    AND sx.odate > s0.odate 
    ) 
    ; 

结果:

DROP TABLE 
CREATE TABLE 
NOTICE: ALTER TABLE/ADD PRIMARY KEY will create implicit index "orders_pkey" for table "orders" 
ALTER TABLE 
INSERT 0 6 
id | odate | payload 
----+------------+--------- 
    1 | 2011-10-24 | two 
    2 | 2011-10-24 | six 
(2 rows) 
相关问题