J'ai la requête SQL Server suivante
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;
que je peux réécrire à l'aide de fonctions analytiques
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;
mais quand je convertit cela en 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;
Je reçois l'erreur suivante
Error at line 5
ORA-00904: "A"."A1": invalid column name
vous effectueriez la première jointure à 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
Le problème avec votre requête est que, actuellement une sous-requête à Oracle, vous ne pouvez pas accéder à une valeur d'une requête mère plus de deux niveaux de niveau.
Vous pouvez également utiliser une fonction PL/SQL qui contiendrait la sélection interne.
Si ce sont les résultats que vous recherchez:
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
Vous pouvez l'obtenir avec ceci:
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;
Ici, je vais montrer quelques résultats que j'ai obtenus lors de l'approche de Vincent Malgrat.
J'ai d'abord appris que lorsque vous utilisez plus d'une telle sous-requête top 1 sur différentes tables ou commandes, je dois utiliser la fonction rang () fonction et non le Row_Number () = fonction.
Deuxièmement, lors de l'utilisation d'un rang (), il y a le problème des liens. Le top 1 de SQL Server sélectionne arbitrairement l'une des lignes avec rang = 1 lors de l'utilisation de Rank () peut revenir plus d'une ligne.
Je pense que c'est une mauvaise conception d'utiliser SQL Server Top 1 dans ces cas. Pour que la conception correcte certaines contraintes uniques (par exemple, des index uniques) doivent être trouvés, qui empêchent cette ambiguïté.
Voici un exemple de serveur SQL si vous voulez l'essayer vous-même.
La comparaison des plans d'exécution des deux dernières déclarations de sélection ci-dessous montre que l'approche de Vincent Malgrat est meilleure que la solution top 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;
Réponse à Leigh Riffel :
Dans les lignes suivantes avec ID 1 et 2 ont les deux rang () et dense_rank de 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;
Résultat:
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
nouvelle réponse à Leigh Riffel :
Voici un exemple, je ne peux pas transformer avec votre motif. J'ai une table avec des articles un prix et une deuxième table avec l'historique des changements de prix. Une certaine autorité veut connaître l'ancien prix, lorsque le prix a changé.
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;
Je veux obtenir
id priceOld
----------- -----------
1 NULL
2 20
mais la seconde donne
id priceOld
----------- -----------
1 NULL
2 NULL
L'approche de Vincent Malgrat donne le résultat correct
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;