为了比较,在没有 IN 条件的情况下重写 Oracle 查询/更新

Posted

技术标签:

【中文标题】为了比较,在没有 IN 条件的情况下重写 Oracle 查询/更新【英文标题】:Rewriting Oracle query/update without IN condition for comparison's sake 【发布时间】:2019-07-18 13:29:33 【问题描述】:

这里是甲骨文。我有以下表格:

[orders]
===
order_id : integer constraint pk_orders primary key using index
order_name : varchar2(40 char)
order_ordered_by : integer constraint fk_shoppers references accounts
order_total : number(10,2) not null
order_status : char not null

[line_items]
===
line_item_id : integer constraint pk_line_items primary key using index
order_id : integer not null constraint fk_line_items_orders references orders on delete cascade
product_id : integer not null constraint fk_line_items_products references products
line_item_quantity : integer not null

[products]
===
product_id : integer constraint pk_products primary key using index
product_name : varchar2(40 char)
product_category : varchar2(10 char)
product_available_on : date

我正在尝试编写一个更新订单并将其状态设置为“已订购”的查询,其中:

orders.order_status 当前处于“待处理”状态;和 products.product_category 当前是“COFFEE”;和 products.product_available_on 当前小于或等于当前时间(现在)

迄今为止我最好的尝试确实工作并完成了工作:

UPDATE orders
SET status = 'ORDERED'
WHERE order_id IN (
    SELECT DISTINCT orders.order_id
    FROM orders
    INNER JOIN line_items ON line_items.orderId = orders.order_id
    INNER JOIN products ON line_items.product_id = products.product_id
    WHERE
        orders.status = 'PENDING' AND
        products.product_category = 'COFFEE' AND
        products.product_available_on <= CURRENT_DATE
);

再一次,这个确实工作,但是它很慢所以我想看看是否有一种方法可以在我不使用IN的情况下重写它以提高效率条件(我在几个地方读到IN 可能导致Oracle 中的性能问题)。有没有什么方法可以在没有IN 的情况下完成重写此查询,以便我可以比较性能?

请注意:在我的特定用例中,更改表格(调整其字段、添加约束/索引/任何内容)是不可能的!

【问题讨论】:

警惕那些告诉您避免使用 SQL 语言的基本元素的来源,例如 IN 运算符。它们充其量只是过度概括。 【参考方案1】:

我会开始这样:

UPDATE orders o
SET    o.status = 'ORDERED'
WHERE  o.status = 'PENDING'
AND    EXISTS ( SELECT 'line for available coffee'
                FROM   line_items li
                INNER JOIN products p ON p.product_id = li.product_id
                WHERE  li.order_id = o.order_id
                AND    p.product_category = 'COFFEE'
                AND    p.product_available_on <= SYSDATE );

这比您发布的查询要好,原因有几个。

    它只查看ORDERS 表一次。 EXISTS 允许 Oracle 停止在相关子查询中查找行 找到一个。 它将order_status = 'PENDING' 条件移动到 主UPDATE,让优化器更容易实现 它可以使用order_status 上的索引。

如果您没有关于订单状态的索引,请考虑使用一个。确保您使用直方图收集统计信息(如今这主要是自动发生的,具体取决于您拥有的 Oracle 版本)。

为什么直方图很重要?因为您的 order_status 值很可能会出现偏差(即分布不均)。也就是说,人们会期望很多很多订单的状态为“已关闭”或“已订购”或“暂停”(构成这些......),因此其中相对较小的百分比具有“待处理”状态。如果没有直方图,Oracle 将看到的只是一个包含 1,000,000 个值和 4 个不同值的索引——Oracle 不太可能使用这样的索引。直方图为它提供了额外的信息,让您知道,如果您想要“CLOSED”订单,它一个错误的索引;但如果您想要“待处理”订单,这是一个不错的选择。

【讨论】:

以上是关于为了比较,在没有 IN 条件的情况下重写 Oracle 查询/更新的主要内容,如果未能解决你的问题,请参考以下文章

Django model 遇到查询条件组合比较多的情况下怎么写

为啥 Go 没有三元条件运算符 [关闭]

为什么重写equals()就必须重写hashCode(),什么情况下可以不重写hashCode()

如何在一组行之后或有条件地在没有 PL/SQL 块的情况下增加 oracle 序列?

Django model 遇到查询条件组合比较多的情况下怎么写

浅谈equals重写