次のSQL Serverクエリがあります
select
(select top 1 b2 from BB b where b.b1 = a.a1 order by b2) calc,
a1,
a2
from AA a
where a2 = 2;
分析関数を使用して書き換えることができます
select
(select b2 from
(select
row_number() over (order by b2) lfd,
b2 from BB b where b.b1 = a.a1
) as t where lfd = 1
) calc,
a1,
a2
from AA a
where a2 = 2;
これをOracleに変換すると
create table AA ( a1 NUMBER(10), a2 NUMBER(10) );
insert into AA values ( 1, 1);
insert into AA values ( 1, 2);
insert into AA values ( 1, 3);
insert into AA values ( 2, 2);
create table BB ( b1 NUMBER(10), b2 NUMBER(10) );
insert into BB values ( 1, 1);
insert into BB values ( 2, 4);
insert into BB values ( 2, 5);
select * from AA;
select * from BB;
select
(select b2 from
(select
row_number() over (order by b2) lfd,
b2 from BB b where b.b1 = a.a1
) where lfd = 1
) calc,
a1,
a2
from AA a
where a2 = 2;
次のエラーが表示されます
Error at line 5
ORA-00904: "A"."A1": invalid column name
最初にOracleで結合を実行します。
SELECT a1, a2, b2
FROM (SELECT a1, a2, b2,
row_number() over(PARTITION BY a.a1 ORDER BY b.b2) lfd
FROM AA a
LEFT JOIN BB b ON b.b1 = a.a1
WHERE a2 = 2)
WHERE lfd = 1
クエリの問題は、現在、Oracleのサブクエリが2レベルより深い親クエリの値にアクセスできないことです。
内部SELECTを含むPL/SQL関数を使用することもできます。
これらがあなたが探している結果である場合:
CODE QTY FRUIT_PRICE FRUIT_NAME DRINK_PRICE DRINK_NAME
---- ---- ------------ ----------- ------------- ------------
A 1 2.4 Apple 5.4 aperol
B 1 1.3 banana 4.3 bear
C 1
あなたはこれでそれを得ることができます:
SELECT o.code, o.qty, f.fruit_price, f.fruit_name, d.drink_price, d.drink_name
FROM want_to_eat o
LEFT JOIN (
SELECT Row_Number() OVER (PARTITION BY Fruit_Code ORDER BY f.datetime desc)
FruitRow
, fruit_price, fruit_name, fruit_code
FROM Fruit f
) f ON f.fruit_code = o.code AND f.FruitRow = 1
LEFT JOIN (
SELECT Row_Number() OVER (PARTITION BY Drink_Code ORDER BY d.datetime desc)
DrinkRow
, Drink_price, Drink_name, Drink_code
FROM Drink d
) d ON d.Drink_code = o.code AND d.DrinkRow = 1
WHERE qty = 1;
ここでは、Vincent Malgratのアプローチを使用したときに得られたいくつかの結果を示します。
最初に、さまざまなテーブルまたは順序に基づいてこのような上位1サブクエリを複数使用する場合、ROW_NUMBER()ではなくRANK()関数を使用する必要があることを学びました=関数。
2番目に、rank()を使用するときは、結合の問題があります。 SQL Serverの上位1は、rank = 1の行の1つを任意に選択しますが、rank()を使用すると、複数の行を返すことができます。
これらのケースでSQL Server top 1を使用するのは悪い設計だと思います。設計を修正するには、いくつかの一意の制約(一意のインデックスなど)を見つけて、このあいまいさを防ぐ必要があります。
自分で試してみたい場合のSQL Serverの例を次に示します。
以下の最後の2つのselectステートメントの実行プランを比較すると、Vincent Malgratのアプローチがトップ1のソリューションよりも優れていることがわかります。
SET NOCOUNT ON
begin try drop table fruit end try begin catch end catch;
begin try drop table want_to_eat end try begin catch end catch;
begin try drop table drink end try begin catch end catch;
create table fruit (
fruit_code char(1),
fruit_name varchar(20),
date datetime,
fruit_price money
);
go
create table drink (
drink_code char(1),
drink_name varchar(20),
date datetime,
drink_price money
);
go
create table want_to_eat (
code char(1),
qty integer,
);
go
insert into want_to_eat values ( 'A', 1);
insert into want_to_eat values ( 'B', 2);
insert into want_to_eat values ( 'B', 1);
insert into want_to_eat values ( 'C', 1);
insert into fruit values ( 'A', 'Apple', '20100101', '2.20');
insert into fruit values ( 'A', 'Apple', '20110101', '2.40');
insert into fruit values ( 'B', 'banana', '20100101', '1.40');
insert into fruit values ( 'B', 'banana', '20110101', '1.30');
insert into fruit values ( 'B', 'banana', '20110101', '1.35');
insert into drink values ( 'A', 'aperol', '20100101', '5.20');
insert into drink values ( 'A', 'aperol', '20110101', '5.40');
insert into drink values ( 'B', 'bear', '20100101', '4.40');
insert into drink values ( 'B', 'bear', '20110101', '4.30');
create unique index iu_drink on drink(drink_code, date);
-- create unique index iu_fruit on fruit(fruit_code, date); -- Error
Select top 1 fruit_price from fruit where fruit_code = 'A' order by date desc;
Select top 1 fruit_price from fruit where fruit_code = 'B' order by date desc;
Select top 1 fruit_price from fruit where fruit_code = 'C' order by date desc;
SELECT
qty,
(Select top 1 fruit_price from fruit where fruit_code = code order by date desc) fruit_price,
(Select top 1 fruit_name from fruit where fruit_code = code order by date desc) fruit_name,
(Select top 1 drink_price from drink where drink_code = code order by date desc) drink_price,
(Select top 1 drink_name from drink where drink_code = code order by date desc) fruit_name
FROM want_to_eat
WHERE qty = 1;
SELECT qty, fruit_price, fruit_name , drink_price,drink_name from (
-- SELECT * FROM (
SELECT
RANK() OVER (PARTITION BY o.CODE ORDER BY f.date desc) f_lfd,
RANK() OVER (PARTITION BY o.CODE ORDER BY d.date desc) d_lfd,
f.date f_date,
code,
qty,
fruit_price,
fruit_name,
drink_price,
drink_name
from want_to_eat o
left join fruit f on o.code = fruit_code
left join drink d on o.code = drink_code
WHERE qty = 1
) t
where f_lfd = 1
and d_lfd = 1;
リー・リフェルへの回答:
次のIDが1と2の行では、どちらもrank()とdensity_rankが1です。
create table rank_test (
id int,
grp int,
val varchar(10)
);
insert into rank_test values (1, 1, 'a');
insert into rank_test values (2, 1, 'a');
insert into rank_test values (3, 1, 'b');
insert into rank_test values (4, 2, 'b');
select * from rank_test;
select r.*,
RANK() OVER (PARTITION BY grp ORDER BY val) rank,
DENSE_RANK() OVER (PARTITION BY grp ORDER BY val) d_rank
from rank_test r
order by id;
結果:
id grp val rank d_rank
----------- ----------- ---------- -------------------- --------------------
1 1 a 1 1
2 1 a 1 1
3 1 b 3 2
4 2 b 1 1
Leigh Riffelへの新しい回答:
ここの例では、私はあなたのパターンで変形することはできません。記事と価格の表と、価格変更の履歴のある2番目の表があります。一部の当局は、価格が変更されたときに古い価格を知りたいと考えています。
create table artikel (id int, price int);
create table price_history (id int,price int,v_date datetime);
insert into artikel values (1, 11), (2, 22);
insert into price_history values (1, 11, '20110101'), (2, 20,'20110101');
insert into price_history values (1, 11, '20110201'), (2, 20,'20110201'); -- the true table has more columns, which values might change while price stays the same
insert into price_history values (1, 11, '20110301'), (2, 22,'20110301');
Select id,
(SELECT TOP 1 price_history.price FROM price_history WHERE price_history.id = artikel.id AND artikel.price <> price_history.price ORDER by v_date DESC ) priceOld
from artikel
ORDER BY id;
Select artikel.id, y.price priceOld
from artikel
LEFT JOIN (
SELECT Row_Number() OVER (PARTITION BY id ORDER BY v_date desc)
yRow
, id, price
FROM price_history
) y ON y.id = artikel.id AND y.yRow = 1 and artikel.price <> y.price
ORDER BY id;
私は手に入れたい
id priceOld
----------- -----------
1 NULL
2 20
しかし、2番目は
id priceOld
----------- -----------
1 NULL
2 NULL
ビンセント・マルグラットのアプローチは正しい結果をもたらします
Select id, priceOld from (
Select
Row_Number() OVER (PARTITION BY a.id ORDER BY v_date desc) vRow,
a.id, h.price priceOld
from artikel a
LEFT JOIN
price_history h ON a.id = h.id and a.price <> h.price
) t
where vRow = 1
ORDER BY id;