【问题标题】:UPDATE table with FIRST matching row from another table without subquery?从另一个没有子查询的表中更新具有第一个匹配行的表?
【发布时间】:2021-07-23 14:48:32
【问题描述】:

我正在尝试调整路线管理。

我们有与邮政编码范围相关的路线代码。它们用于在交付订单时选择正确的运输路线。

一条路线将具有其有效的邮政编码范围,并且可能有一个客户端——如果该路线仅针对该客户端。

所以,路由表如下所示:

       route_no        | zipfrom | zipto |  client
-----------------------+---------+-------+----------
 ROUTE-CLIENTA-SPECIAL | 12330   | 12350 | CLIENT-A
 ROUTE-CLB1            | 33331   | 44445 | CLIENT-B
 ROUTE-SCENIC          | 11111   | 99999 |
(3 rows)

所以,根据这张表:

  • 每个订单都必须经过 ROUTE-SCENIC,包括客户 CLIENT-A 和 CLIENT-B。
  • 除非订单是针对 CLIENT-A 的,并且交货是针对邮政编码在 12330-12350 之间的地址 - 这些通过 ROUTE-CLIENTA-SPECIAL 进行。
  • 或者是 CLIENT-B 的订单,并且订单的邮政编码在 33331 和 44445 之间——然后它转到路由 ROUTE-CLB1

所以,对于今天的订单:

 order_id |  client  | route_no |  zip
----------+----------+----------+-------
 123      | CLIENT-A |          | 12345
 124      | CLIENT-A |          | 33333
 125      | CLIENT-A |          |
 N988     | CLIENT-B |          | 77777
 N987     | CLIENT-B |          | 33335
 N0981    | CLIENT-B |          | 44444
 N0983    | CLIENT-B |          | 12345

要获得CLIENT-B(邮政编码33333)订单N987的可能路线,我可以这样做:

SELECT client,route_no FROM routes 
WHERE (client='CLIENT-B' OR client IS NULL )  
   AND '33333' BETWEEN zipfrom AND zipto ORDER BY client;

  client  |   route_no
----------+--------------
 CLIENT-B | ROUTE-CLB1
          | ROUTE-SCENIC
(2 rows)

在这个 ROUTE-CLB1 之外是正确的路由(它比默认路由更具体到客户端)。

我可以通过 ORDER BY 和 LIMIT 1 始终选择正确的路线。

但我需要用正确的路线更新订单表。我可以用子查询来做到这一点:

UPDATE orders AS O 
   SET route_no=(SELECT R.route_no FROM routes R
              WHERE (O.client=R.client OR R.client IS NULL )
                    AND O.zip BETWEEN R.zipfrom AND R.zipto 
              ORDER BY R.client LIMIT 1);

这给出了正确答案:

  id   |  client  |         route_no      |  zip
-------+----------+-----------------------+-------
 123   | CLIENT-A | ROUTE-CLIENTA-SPECIAL | 12345
 124   | CLIENT-A | ROUTE-SCENIC          | 33333
 125   | CLIENT-A |                       |
 N988  | CLIENT-B | ROUTE-SCENIC          | 77777
 N987  | CLIENT-B | ROUTE-CLB1            | 33335
 N0981 | CLIENT-B | ROUTE-CLB1            | 44444
 N0983 | CLIENT-B | ROUTE-SCENIC          | 12345
(7 rows)

但这在较大的订单列表上往往会非常慢,因为子查询会针对每一行运行。

我该如何改进它?我尝试按照Select first row in each GROUP BY group? 中的说明使用 DISTINCT ON,但这似乎不起作用。

【问题讨论】:

  • @TimBiegeleisen 我知道这听起来可能很复杂,但我不知道该去哪里更容易。也许您对哪些不清楚的地方有一些评论可以帮助我改进我的问题?
  • @TimBiegeleisen 现在好点了吗?

标签: sql postgresql performance


【解决方案1】:

您可能会发现使用正确的索引会更快:

UPDATE orders o
   SET route_no = COALESCE( (SELECT R.route_no
                             FROM routes R
                             WHERE O.client = R.client AND
                                   O.zip BETWEEN R.zipfrom AND R.zipto 
                             LIMIT 1
                            ), 
                            (SELECT R.route_no
                             FROM routes R
                             WHERE R.client IS NULL AND
                                   O.zip BETWEEN R.zipfrom AND R.zipto 
                             LIMIT 1
                            )
                           );

那么你想要一个routes(client, zipfrom, zipto) 的索引。索引至少会在不扫描表的情况下快速找到routes 中的候选行。 Postgres 可能会通过 or 解决这个问题,但这值得一试。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多