【问题标题】:SQL query: Select max value of subselectSQL查询:选择子选择的最大值
【发布时间】:2016-01-05 10:18:49
【问题描述】:

我有一个sql语句已经可以了,但我认为一定有比我更好的解决方案。

我正在尝试获取最高价从未售出文章

通过此选择,我将获得所有尚未售出的文章(数量 + 价格):

select anr, price
from article a 
where not exists(
    select 1 from OrderItems o
    where o.artnr = a.anr 
  )

货号+价格结果的样子

| Anr | Price |
| 1   | 300.0 |
| 4   | 340.0 |
| 5   | 340.0 |
| 3   | 200.0 |

我获得最高价格文章的临时解决方案是:

select anr, price 
from article 
where anr in(
  select anr
  from article a 
  where not exists(
    select 1 from OrderItems o
    where o.artnr = a.anr 
  )
)
and price = (
  select max(price)
  from article a 
  where not exists(
    select 1 from OrderItems o
    where o.artnr = a.anr 
  )
)

正确的解决方法是:

| Anr | Price |
| 4   | 340.0 |
| 5   | 340.0 |

有没有办法避免两次相同的子选择?

这里的测试是带有我插入值的缩短的 Create Table 脚本:

CREATE TABLE Article
(
  Anr Int Primary Key,
  Price Numeric(9,2) Not Null
);

CREATE TABLE Orders
(
  OrderNr Int Primary Key
)

CREATE TABLE OrderItems
(
  OrderNr Int References Orders On Delete Cascade, 
  ItemNr  Int,
  Artnr   Int References Article Not Null,
  Amount  Int Not Null Check(Amount >= 0),
  Primary Key(OrderNr, ItemNr)
)

-- articles without an order
Insert into Article (Anr, Price) values(1,300.0);
Insert into Article (Anr, Price) values(4,340.0);
Insert into Article (Anr, Price) values(5,340.0);
Insert into Article (Anr, Price) values(3,200.0);

-- articles for order with orderNr '1'
Insert into Article (Anr, Price) values(2,340.0);
Insert into Article (Anr, Price) values(6,620.0);

-- insert test order that contains the two articles
Insert into Orders (OrderNr) values (1);
Insert into OrderItems(OrderNr, ItemNr, Artnr, Amount) values(1,1,2,4);
Insert into OrderItems(OrderNr, ItemNr, Artnr, Amount) values(1,2,6,2);

我也看了题目Select max value in subquery SQL 但我认为在我的情况下必须有一种更短的方式来进行选择。

【问题讨论】:

  • SQL Server/MySQL/Oracle/Postgresql/Firebird/SQLite?
  • 它应该适用于每个数据库,这就是我不想要特定数据库的原因。我想通过使用标准 sql 来解决这个问题。但我正在 Oracle 12c 上进行测试 :)
  • 如果你添加脚本来创建表和记录..我可以帮助你

标签: sql oracle subquery


【解决方案1】:

这是一种避免您拥有的相关子查询之一的解决方案,而是将其替换为 LEFT JOIN

SELECT a.*
FROM article a LEFT JOIN OrderItems o ON a.anr = o.artnr
WHERE o.artnr IS NULL AND
    a.price = (SELECT MAX(a.price)
               FROM article a LEFT JOIN OrderItems o ON a.anr = o.artnr
               WHERE o.artnr IS NULL)

此解决方案应符合 ANSI-92,这意味着它应与 MySQL、Oracle、SQL Server 以及您可能遇到的任何其他类型的快餐兼容。

【讨论】:

  • 它并没有真正“避免”相关的子查询。外连接几乎是一样的东西,任何现代优化器很可能会使用相同的执行计划。
  • @a_horse_with_no_name 优化器是否也能够顺利处理WHERE 子句中的子查询,认识到它独立于外部查询?
  • 不确定你所说的“顺利处理”是什么意思——但这个问题基本上只能通过查看执行计划来回答。
【解决方案2】:
SELECT a.*
FROM article a LEFT JOIN OrderItems o ON a.anr = o.artnr
WHERE o.artnr IS NULL 
AND a.price = (SELECT TOP 1 a.price
           FROM article a LEFT JOIN OrderItems o ON a.anr = o.artnr
           WHERE o.artnr IS NULL
           Order By a.price Desc
           )

试试这个....

【讨论】:

  • TOP 1 在 Oracle 中无效
【解决方案3】:

您可以将原始查询与窗口函数一起使用以获得您想要的:

select anr, price
from (
    select anr, price, max(price) over () as max_price
    from article a 
    where not exists (select 1 
                      from OrderItems o
                      where o.artnr = a.anr)
) t
where price = max_price;

可能会更快,因为只需要对article 表进行一次扫描。

或者,您可以使用左连接解决方​​案来查找从未订购过的文章,但如果 Oracle 会为此使用不同的执行计划,我会感到惊讶:

select anr, price
from (
    select anr, price, max(price) over () as max_price
    from article a 
      left join OrderItems o ON o.artnr = a.anr
    where o.artnr is null
) t
where price = max_price;

SQLFiddle 示例:http://sqlfiddle.com/#!15/1eb69/1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-07
    • 2013-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多