SQL:检索每个客户端的最新订单

时间:2014-11-13 22:44:38

标签: sql sql-server postgresql greatest-n-per-group

我有两个具有一对多关系的表(例如,客户表和订单表;每个客户可能已经下了多个订单)。对于每个客户,我只需要检索一个订单,由特定标准(例如最近的订单)定义。

我需要在PostgreSQL 9.1和Microsoft SQL Server 2014中执行此操作(两个系统中的不同数据,但问题非常相同)。这些表约有250万条记录(客户)和1000万条记录(订单)。

我在考虑在SQL服务器中使用外部应用,并在PostgreSQL中对分区或横向连接进行排名,但对语法不太确定。

一个简单的max(order_date) ... group by clientid就不够了,因为我需要订单表中的所有字段。例如,我需要像

这样的输出
c.clientid, c.client_name, o.order_id, o.order_date, o.order_amount, o.product_ordered
where c = the clients table and o = the orders table.

有什么建议吗?

3 个答案:

答案 0 :(得分:1)

我没看到如何进行横向连接。这是一个很好的旧“最大n组”问题,可以通过普通连接和窗口函数来解决:

select *
from clients c
  join (
     select clientid, order_id, order_date, order_amount, product_ordered 
            row_number() over (partition by client_id order by order_date desc) as rn
     from orders
  ) o on o.clientid = c.clientid and o.rn = 1;

对于Postgres,使用distinct on代替窗口函数可以提高效率。如果你有client_id, order_date的索引,这应该表现得相当不错。

答案 1 :(得分:0)

这是你可以做到的另一种方式。为SQL 2005编写/测试。对于大量的记录,不太确定它的性能。

SELECT 
    c.clientid, 
    c.client_name, 
    o.order_id, 
    o.order_date, 
    o.order_amount, 
    o.product_ordered
FROM c JOIN o ON c.clientid = o.clientid
WHERE order_id IN (
    SELECT TOP 1 order_id
    FROM o AS o2
    WHERE o.clientid = o2.clientid
    ORDER BY order_date DESC)

答案 2 :(得分:0)

对于SQL Server,我会使用OUTER APPLY

SELECT
    clients.clientid
    ,clients.client_name
    ,LastOrder.order_id
    ,LastOrder.order_date
    ,LastOrder.order_amount
    ,LastOrder.product_ordered
FROM
    clients
    OUTER APPLY
    (
        SELECT TOP(1)
            orders.order_id
            ,orders.order_date
            ,orders.order_amount
            ,orders.product_ordered
        FROM orders
        WHERE orders.clientid = clients.clientid
        ORDER BY order_date DESC
    ) AS LastOrder
;

如果你有一个索引(orders.clientid,orders.order_date)并将其他列添加到索引中,那么OUTER APPLY应该是对该索引的搜索。 因此,对于clients表中的每一行,应该对订单索引进行单一搜索。 如果索引不包含所有其他列,则会有搜索和查找。

如果您不想让没有任何订单的客户使用CROSS APPLY而不是OUTER APPLY

我认为这种OUTER APPLY比为所有订单生成ROW_NUMBERS()更有效,然后丢弃大部分结果并仅使用第一行。

当然,您应该使用您的系统测量不同变体的实际性能。

不幸的是,我不知道Postgres。