Related
I am looking to populate a table with some data and normally I would run some insert scripts or upload via a csv file. The requirements I have is that the data can only be populated in the 2nd table as long as the order_id is within the orders table.
From what I know I probably need to write some PL/SQL code to check if the order_id exists in the orders table before running the insert scripts but not sure how to write this. I would appreciate it if somebody could get me started.
This is the create statement for the Orders Table:
CREATE TABLE ORDERS (
ORDER_ID NUMBER NOT NULL,
STATUS VARCHAR2(9) NOT NULL,
ORDER_DATE DATE NOT NULL,
PRIMARY KEY(ORDER_ID),
CONSTRAINT CHK_STATUS CHECK (STATUS = 'OPEN' OR STATUS = 'CLOSED')
);
The create statement for the 2nd table is:
CREATE TABLE ORDER2
(
ORDER_ID NUMBER NOT NULL,
PRODUCT_ID NUMBER NOT NULL,
ORDER_DATE DATE NOT NULL,
PRIMARY KEY(PRODUCT_ID)
);
Thanks.
Well yes, you could check it manually. As HoneyBadger commented, exists is a way to do that, for example:
SQL> insert into orders (order_id, status, order_date)
2 select 1, 'OPEN' , trunc(sysdate - 2) from dual union all
3 select 2, 'CLOSED', trunc(sysdate - 1) from dual;
2 rows created.
SQL> select * from orders;
ORDER_ID STATUS ORDER_DATE
---------- ------ ----------
1 OPEN 03.06.2022
2 CLOSED 04.06.2022
Let's try to insert order_id = 1 into order2:
SQL> insert into order2 (product_id, order_id, order_date)
2 select 100, 1, trunc(sysdate - 2) from dual
3 where exists (select null
4 from orders
5 where order_id = 1);
1 row created.
It succeeded as order_id = 1 exists in orders table. What about order_id = 3 which doesn't exist there?
SQL> insert into order2 (product_id, order_id, order_date)
2 select 300, 3, trunc(sysdate) from dual
3 where exists (select null
4 from orders
5 where order_id = 3);
0 rows created.
SQL>
Right, nothing was inserted.
But, why wouldn't you let the database do it for you? Create a foreign key constraint which won't let any rows to be inserted into the order2 table unless that order_id exists in the orders table:
SQL> create table orders (
2 order_id number constraint pk_ord primary key,
3 status varchar2(6) constraint chk_ord_stat check (status in ('OPEN', 'CLOSED'))
4 not null,
5 order_date date not null
6 );
Table created.
SQL> create table order2 (
2 product_id number constraint pk_ord2 primary key,
3 order_id number constraint fk_ord2_ord references orders (order_id)
4 not null,
5 order_date date not null
6 );
Table created.
SQL>
Testing:
SQL> insert into order2 (product_id, order_id, order_date)
2 values (300, 3, trunc(sysdate));
insert into order2 (product_id, order_id, order_date)
*
ERROR at line 1:
ORA-02291: integrity constraint (SCOTT.FK_ORD2_ORD) violated - parent key not
found
SQL>
See? Oracle won't let you do that, you don't have to check anything.
On the other hand, why two tables? Most columns are common (I presume they also share common data), so perhaps you could just add product_id into orders (I don't know whether order_id and product_id make the primary key, though):
SQL> create table orders (
2 order_id number,
3 product_id number,
4 status varchar2(6) constraint chk_ord_stat check (status in ('OPEN', 'CLOSED'))
5 not null,
6 order_date date not null,
7 --
8 constraint pk_ord primary key (order_id, product_id)
9 );
Table created.
SQL>
I will create table where I will insert multiple values for different companies. Basically I have all values that are in the table below but I want to add a column IndicatorID which is linked to IndicatorName so that every indicator has a unique id. This will obviously not be a PrimaryKey.
I will insert the data with multiple selects:
CREATE TABLE abc
INSERT INTO abc
SELECT company_id, 'roe', roevalue, metricdate
FROM TABLE1
INSERT INTO abc
SELECT company_id, 'd/e', devalue, metricdate
FROM TABLE1
So, I don't know how to add the IndicatorID I mentioned above.
EDIT:
Here is how I populate my new table:
INSERT INTO table(IndicatorID, Indicator, Company, Value, Date)
SELECT [the ID that I need], 'NI_3y' as 'Indicator', t.Company, avg(t.ni) over (partition by t.Company order by t.reportdate rows between 2 preceding and current row) as 'ni_3y',
t.reportdate
FROM table t
LEFT JOIN IndicatorIDs i
ON i.Indicator = roe3 -- the part that is not working if I have separate indicatorID table
I am going to insert different indicators for the same companies. And I want indicatorID.
Your "indicator" is a proper entity in its own right. Create a table with all indicators:
create table indicators (
indicator_id int identity(1, 1) primary key,
indicator varchar(255)
);
Then, use the id only in this table. You can look up the value in the reference table.
Your inserts are then a little more complicated:
INSERT INTO indicators (indicator)
SELECT DISTINCT roevalue
FROM table1 t1
WHERE NOT EXISTS (SELECT 1 FROM indicators i2 WHERE i2.indicator = t1.roevalue);
Then:
INSERT INTO ABC (indicatorId, companyid, value, date)
SELECT i.indicatorId, t1.company, v.value, t1.metricdate
FROM table1 t1 CROSS APPLY
(VALUES ('roe', t1.roevalue), ('d/e', t1.devalue)
) v(indicator, value) JOIN
indicators i
ON i.indicator = v.indicator;
This process is called normalization and it is the typical way to store data in a database.
DDL and INSERT statement to create an indicators table with a unique constraint on indicator. Because the ind_id is intended to be a foreign key in the abc table it's created as a non-decomposable surrogate integer primary key using the IDENTITY property.
drop table if exists test_indicators;
go
create table test_indicators (
ind_id int identity(1, 1) primary key not null,
indicator varchar(20) unique not null);
go
insert into test_indicators(indicator) values
('NI'),
('ROE'),
('D/E');
The abc table depends on the ind_id column from indicators table as a foreign key reference. To populate the abc table company_id's are associated with ind_id's.
drop table if exists test_abc
go
create table test_abc(
a_id int identity(1, 1) primary key not null,
ind_id int not null references test_indicators(ind_id),
company_id int not null,
val varchar(20) null);
go
insert into test_abc(ind_id, company_id)
select ind_id, 102 from test_indicators where indicator='NI'
union all
select ind_id, 103 from test_indicators where indicator='ROE'
union all
select ind_id, 104 from test_indicators where indicator='D/E'
union all
select ind_id, 103 from test_indicators where indicator='NI'
union all
select ind_id, 105 from test_indicators where indicator='ROE'
union all
select ind_id, 102 from test_indicators where indicator='NI';
Query to get result
select i.ind_id, a.company_id, i.indicator, a.val
from test_abc a
join test_indicators i on a.ind_id=i.ind_id;
Output
ind_id company_id indicator val
1 102 NI NULL
2 103 ROE NULL
3 104 D/E NULL
1 103 NI NULL
2 105 ROE NULL
1 102 NI NULL
I was finally able to find the solution for my problem which seems to me very simple, although it took time and asking different people about it.
First I create my indicators table where I assign primary key for all indicators I have:
CREATE TABLE indicators (
indicator_id int identity(1, 1) primary key,
indicator varchar(255)
);
Then I populate easy without using any JOINs or CROSS APPLY. I don't know if this is optimal but it seems as the simplest choice:
INSERT INTO table(IndicatorID, Indicator, Company, Value, Date)
SELECT
(SELECT indicator_id from indicators i where i.indicator = 'NI_3y) as IndicatorID,
'NI_3y' as 'Indicator',
Company,
avg(ni) over (partition by Company order by reportdate rows between 2 preceding and current row) as ni_3y,
reportdate
FROM TABLE1
I am an absolute beginner when it comes to SQL, and I'm trying to figure out how to do a simple integrity check. I am modelling a hotel registration system with four tables: Hotel, Room, Booking and Guest. What I'm interested in is in the booking table, which has attributes hotelNo, guestNo, dateFrom, dateTo, roomNo, where the first three are a composite primary key. Now then, the issue at hand is that under the current system, two people might have the same room booked at the same time, which would obviously be a problem in real life. I imagine that a solution might start to look something like
CREATE TABLE Booking(
-- All the attribute definitions go here...
CONSTRAINT OneGuestAtATime
CHECK (NOT EXISTS(SELECT(dateFrom FROM Booking ...))) -- I become unsure of what to do around here
);
Bear in mind that while I am a computer engineering student, I've never done SQL before, so a hand-holdy walk-through would be appreciated :)
Edit: I think my problem would be solved by a constraint to the effect of
CASE 1 (Overlap): "If the dateFrom or dateTo attributes of the record I am trying to insert falls between the dateFrom and dateTo attributes of a given record previously in the table, reject this insertion since there is some overlap between the two bookings."
CASE 2 (Superset): "If I am attempting to insert record X and there is a record already in the table named Y such that its Y.dateFrom > X.dateFrom and Y.dateTo < X.dateTo, then X is a superset of Y and should be rejected."
I'm not sure of how to translate that to SQL, though.
Edit 2: The tables
CREATE TABLE Hotel (
hotelNo NUMBER NOT NULL,
hotelName VARCHAR2(1024) NOT NULL,
city VARCHAR2(1024) NOT NULL,
--
PRIMARY KEY (hotelNo)
);
CREATE TABLE Room (
roomNo NUMBER(4,0) NOT NULL,
hotelNo NUMBER(5,0) NOT NULL,
type VARCHAR2(1024),
price NUMBER(6,2) NOT NULL,
--
PRIMARY KEY (roomNo, hotelNo),
FOREIGN KEY (hotelNo) REFERENCES Hotel
);
CREATE TABLE Guest(
guestNo NUMBER(8,0) NOT NULL,
guestName VARCHAR(1024) NOT NULL,
guestAddress VARCHAR(1024) NOT NULL,
--
PRIMARY KEY (guestNo)
);
CREATE TABLE Booking(
hotelNo NUMBER(8,0) NOT NULL,
guestNo NUMBER(8,0) NOT NULL,
dateFrom DATE NOT NULL,
dateTo DATE NOT NULL,
roomNo NUMBER(4,0) NOT NULL,
--
PRIMARY KEY (hotelNo, guestNo, dateFrom),
FOREIGN KEY (hotelNo) REFERENCES Hotel,
FOREIGN KEY (guestNo) REFERENCES Guest,
FOREIGN KEY (hotelNo, roomNo) REFERENCES Room(hotelNo, roomNo),
--
CONSTRAINT DateIntegrity
CHECK (dateFrom < dateTo)
In your model, the HOTEL and GUEST tables are okay (they may need some more columns later, but that's not a problem). For ROOM, you have decided to use a composite PK. However, a single column as ID would suffice. In BOOKING, the foreign key referencing HOTEL is redundant. Guests are booking rooms (which have a unique ID, and are tied to a HOTEL already) at certain days.
It may help (your learning) to auto-generate the IDs, and define different "start" values for them - when querying the tables at a later stage, you will recognise the IDs immediately eg HOTELs
could have 1000+, rooms could have 2000+ etc (see DDL code below).
When following the link provided by #Abra, you have seen that triggers etc can be used for solving the problem. The solution below is inspired by this answer (also mentioned as "option 4" here), and uses the idea of breaking down bookings into days ("slots"), which can then be used for unique (or PK) constraints. Please read the comments, as they contain more explanations.
DDL code
create table hotels (
id number generated always as identity start with 1000 primary key
, name_ varchar2( 100 )
) ;
create table rooms (
id number generated always as identity start with 2000 primary key
, name_ varchar2( 100 )
, hotelid number references hotels( id )
) ;
create table guests (
id number generated always as identity start with 3000 primary key
, last_name varchar2( 100 )
) ;
-- additional table, populated 500 days "into the future"
-- (no bookings _before_ the sysdate) due to FK constraint in bookings
create table days ( slot primary key )
as
select trunc( sysdate ) + level
from dual
connect by level <= 500 ; -- Oracle only!
create table bookings (
roomid number references rooms( id )
, slot date references days( slot ) not null
, guestid number references guests( id ) not null
, constraint bookings_pk primary key( roomid, slot )
) ;
Model
Populate HOTELS, ROOMS, GUESTS
-- For populating HOTELS, ROOMS, and GUESTS, we are just using a little PL/SQL script.
-- You can also use single INSERTs.
begin
-- insert one hotel
insert into hotels ( name_ ) values ( 'Tiny Hotel' ) ;
-- insert 8 rooms
for r in 1 .. 8
loop
insert into rooms( name_, hotelid ) values ( 'room_' || to_char( r ), 1000 ) ;
end loop ;
-- insert 9 guests
for g in 1 .. 9
loop
insert into guests( last_name ) values ( 'guest_' || to_char( g ) ) ;
end loop ;
commit ;
end ;
/
Data in HOTELS, ROOMS, GUESTS
SQL> select * from hotels ;
ID NAME_
1000 Tiny Hotel
SQL> select * from rooms ;
ID NAME_ HOTELID
2000 room_1 1000
2001 room_2 1000
2002 room_3 1000
2003 room_4 1000
2004 room_5 1000
2005 room_6 1000
2006 room_7 1000
2007 room_8 1000
SQL> select * from guests ;
ID LAST_NAME
3000 guest_1
3001 guest_2
3002 guest_3
3003 guest_4
3004 guest_5
3005 guest_6
3006 guest_7
3007 guest_8
3008 guest_9
Testing
-- tests for bookings - unique (roomid, slot)
-- guest 3000 books room 2000, 2 days
-- these 2 inserts must succeed
insert into bookings ( roomid, guestid, slot )
values ( 2000, 3000, date '2020-10-10' ) ;
insert into bookings ( roomid, guestid, slot )
values ( 2000, 3000, date '2020-10-10' + 1 ) ; -- + 1 here could be + i in a loop ...
-- INSERT must fail - guest 3000 cannot book room 2000 twice (on the same day)
insert into bookings ( roomid, guestid, slot )
values ( 2000, 3000, date '2020-10-10' ) ;
--ERROR at line 1:
--ORA-00001: unique constraint (...BOOKINGS_PK) violated
-- this INSERT must fail
-- guest 3001 cannot have room 2000 on the same day as guest 3000
insert into bookings ( roomid, guestid, slot )
values ( 2000, 3001, date '2020-10-10' + 1 ) ;
--ERROR at line 1:
--ORA-00001: unique constraint (...BOOKINGS_PK) violated
-- guest 3001 can have a different room at the same date, though
-- this insert must succeed
insert into bookings ( roomid, guestid, slot )
values ( 2001, 3001, date '2020-10-10' + 1 ) ;
-- 1 row created.
You can insert more dates for testing by using PL/SQL and coding a loop: see dbfiddle.
Simple query
-- all current bookings
select
H.name_
, R.name_
, G.last_name
, B.slot
from hotels H
join rooms R on H.id = R.hotelid
join bookings B on R.id = B.roomid
join guests G on G.id = B.guestid
;
-- result
NAME_ NAME_ LAST_NAME SLOT
Tiny Hotel room_1 guest_1 10-OCT-20
Tiny Hotel room_1 guest_1 11-OCT-20
Tiny Hotel room_1 guest_4 01-DEC-20
Tiny Hotel room_1 guest_4 02-DEC-20
Tiny Hotel room_1 guest_4 03-DEC-20
...
Query with more test data (INSERTs: see dbfiddle)
-- query that returns
-- all current bookings with nights_booked etc
-- CAUTION: for recurring bookings (same ROOM and GUEST but different slots)
-- this query will give us misleading results
select
H.name_
, R.name_
, G.last_name
, count( B.slot) nights_booked
, min( B.slot ) arrival_date
, max( B.slot ) + 1 departure_date
from hotels H
join rooms R on H.id = R.hotelid
join bookings B on R.id = B.roomid
join guests G on G.id = B.guestid
group by H.name_, R.name_, G.last_name
;
-- result
NAME_ NAME_ LAST_NAME NIGHTS_BOOKED ARRIVAL_DATE DEPARTURE_DATE
Tiny Hotel room_1 guest_1 2 10-OCT-20 12-OCT-20
Tiny Hotel room_1 guest_4 21 01-DEC-20 22-DEC-20
Tiny Hotel room_2 guest_2 1 11-OCT-20 12-OCT-20
As your question was more about modelling and constraints, the queries will probably need more work.
I have 4 different tables, bommodule, bomitem and mapbomitemmodule, mapbomparentsubmodule.
bommodule-table:
CREATE TABLE "BOMMODULE"
(
"MODULEID" NUMBER(10,0) NOT NULL ENABLE,
"ISROOTMODULE" NUMBER(1,0) NOT NULL ENABLE,
"MODULENAME" VARCHAR2(255 CHAR),
.....
)
bomitem-table:
CREATE TABLE "BOMITEM"
(
"ITEMID" NUMBER(10,0) NOT NULL ENABLE,
....
)
mapbomitemmodule-table: (This table maps the items to one or more modules).
CREATE TABLE "SSIS2"."MAPBOMITEMMODULE"
(
"ITEMID" NUMBER(10,0) NOT NULL ENABLE,
"MODULEID" NUMBER(10,0) NOT NULL ENABLE,
CONSTRAINT "MAP_ITEM_MODULE_FK" FOREIGN KEY ("ITEMID") REFERENCES "BOMITEM" ("ITEMID") ENABLE,
CONSTRAINT "MAP_MODULE_ITEM_FK" FOREIGN KEY ("MODULEID") REFERENCES "BOMMODULE" ("MODULEID") ENABLE
)
mapbomparentsubmodule-table: (This table maps the module to submodules)
CREATE TABLE "MAPBOMPARENTSUBMODULE"
(
"PARENTMODULEID" NUMBER(10,0) NOT NULL ENABLE,
"SUBMODULEID" NUMBER(10,0) NOT NULL ENABLE,
CONSTRAINT "PARENTMODULE_SUBMODULE_FK" FOREIGN KEY ("SUBMODULEID") REFERENCES "BOMMODULE" ("MODULEID") ENABLE,
CONSTRAINT "SUBMODULE_PARENTMODULE_FK" FOREIGN KEY ("PARENTMODULEID") REFERENCES "BOMMODULE" ("MODULEID") ENABLE
)
So imagine a structure something like this.
root module
submodule 1
submodule 2
submodule 3
submodule 4
submodule 5
item 5
item 6
item 7
item 2
item 3
item 4
item 1
I need to find out all items that belong to a specific moduleId. All items on all submodule levels should be listed.
How can I do this? I am using Oracle 11 as database.
Thanks a lot for your help, much appreciate it!
Thanks for the interesting question
1 step. Prepare table for canonical oracle hierarhy.
select PARENTMODULEID, SUBMODULEID, 1 HTYPE
from MAPBOMPARENTSUBMODULE
union all
select null, MODULEID, 1 --link root to null parent
from BOMMODULE B
where ISROOTMODULE = 1
union all
select MODULEID, ITEMID, 2
from MAPBOMITEMMODULE
2 step. Expand the hierarchy by using connect by
select PARENTMODULEID
,SUBMODULEID
,HTYPE
,level
,lpad('|', level*3, '|')
from
(
select PARENTMODULEID, SUBMODULEID, 1 HTYPE
from MAPBOMPARENTSUBMODULE
union all
select null, MODULEID, 1
from BOMMODULE B
where ISROOTMODULE = 1
union all
select MODULEID, ITEMID, 2
from MAPBOMITEMMODULE
) ALL_HIER
connect by prior SUBMODULEID = PARENTMODULEID
and prior HTYPE = 1 --parent is always module
start with ALL_HIER.PARENTMODULEID is null
order siblings by HTYPE
3 step. Last. Join with value tables.
select PARENTMODULEID
,SUBMODULEID
,HTYPE
,ALL_VAL.VAL
,level
,rpad('|', level * 3, ' ') || ALL_VAL.VAL
from
(
select PARENTMODULEID, SUBMODULEID, 1 HTYPE
from MAPBOMPARENTSUBMODULE
union all
select null, MODULEID, 1
from BOMMODULE B
where ISROOTMODULE = 1
union all
select MODULEID, ITEMID, 2
from MAPBOMITEMMODULE
) ALL_HIER
,(
select MODULEID VAL_ID, MODULENAME VAL, 1 VTYPE
from BOMMODULE
union all
select ITEMID, 'item '||ITEMID, 2
from BOMITEM
) ALL_VAL
where ALL_VAL.VAL_ID = ALL_HIER.SUBMODULEID
and ALL_VAL.VTYPE = ALL_HIER.HTYPE
connect by prior SUBMODULEID = PARENTMODULEID
and prior HTYPE = 1
start with ALL_HIER.PARENTMODULEID is null
order siblings by HTYPE
Start by investigating the use of a hierarchical query, using the CONNECT BY clause. It is designed for exactly this type of model.
The principle for multiple tables is the same as that for a single table:
i. You identify "starting rows".
ii. You define the logic by which you identify the next level of rows based on the "current" set.
In some cases it can help to define the hierarchical query in an inline view, particularly if a single table holds all the data required for identifying both the starting rows and the connection between levels.
These queries are always tricky, and as with many SQL issues it often helps to start off simple and build up the complexity.
So i'm having this problem.
I have two tables (Oracle), one is called Destination and the other one Reserve. Reserve has a foreign key to the id of Destination (Because on reserve has one destination). And reserve tuples means all the reserves that all the users have done. I need a way to check the top 3 most visited Destinations (based on the foreign key in the table Reserve).
How can I do that with SQL in Oracle. I know that I need to search within the Reserve tables for the 3 most repeated Destinations ID's, and later join that with the Destination table to get the details of the top 3 destinations.
Any help is valid. Thank you very much.
SCHEMA:
--------------------------------------------------------
-- File created - martes-septiembre-15-2009
--------------------------------------------------------
--------------------------------------------------------
-- DDL for Table DESTINO
--------------------------------------------------------
CREATE TABLE "S2501A29"."DESTINO"
( "PK_ID_DESTINO" NUMBER(10,0),
"FK_COD_UBICACION_GEOGRAFICA" NUMBER(10,0),
"NOMBRE" VARCHAR2(10),
"FOTO" VARCHAR2(30),
"DESCRIPCION" VARCHAR2(50)
) ;
--------------------------------------------------------
-- DDL for Table LUGAR_ESTADIA
--------------------------------------------------------
CREATE TABLE "S2501A29"."LUGAR_ESTADIA"
( "PK_ID_ESTADIA" NUMBER(10,0),
"NOMBRE" VARCHAR2(10),
"TIPO" VARCHAR2(10),
"DESCRIPCION" VARCHAR2(50),
"COSTO_SERVICIOS" NUMBER,
"DESCRIPCION_ALOJAMIENTO" VARCHAR2(100),
"DESCRIPCION_ALIMENTACION" VARCHAR2(100)
) ;
--------------------------------------------------------
-- DDL for Table OPCION_TRANSPORTE
--------------------------------------------------------
CREATE TABLE "S2501A29"."OPCION_TRANSPORTE"
( "PK_ID_VIAJE" NUMBER(10,0),
"MEDIO_TRANSPORTE" VARCHAR2(10),
"RESPONSABLE" VARCHAR2(10),
"CIUDAD_ORIGEN" VARCHAR2(10),
"CIUDAD_DESTINO" VARCHAR2(10),
"COSTO" NUMBER
) ;
--------------------------------------------------------
-- DDL for Table RESERVA
--------------------------------------------------------
CREATE TABLE "S2501A29"."RESERVA"
( "PK_ID_RESERVA" NUMBER(10,0),
"FK_COD_DESTINO" NUMBER(10,0),
"FK_COD_ESTADIA" NUMBER(10,0),
"FK_COD_VIAJE" NUMBER(10,0),
"TARJETA_CREDITO" VARCHAR2(12),
"FECHA_SALIDA" DATE,
"FECHA_REGRESO" DATE,
"NOMBRE_USUARIO" VARCHAR2(50)
) ;
--------------------------------------------------------
-- DDL for Table UBICACION_GEOGRAFICA
--------------------------------------------------------
CREATE TABLE "S2501A29"."UBICACION_GEOGRAFICA"
( "PK_ID_UBICACION" NUMBER(10,0),
"CIUDAD" VARCHAR2(10),
"PAIS" VARCHAR2(10),
"CONTINENTE" VARCHAR2(10)
) ;
Just a note:
RESERVE is Reserva
DESTINATION is Destino
Because the DB is in spanish. Thanks!
Maybe I'm missing something, but what about grouping by foreign key and sorting by the resulting values?
Edit: Something like:
select FK_COD_DESTINO, count(*) as qty from RESERVA group by FK_COD_DESTINO order by qty desc limit 3
Oracle doesn't support limit. This should work for you.
select A.FK_COD_DESTINO
, A.COUNT
, A.RANK
, B.*
from (select FK_COD_DESTINO
, count(*) as COUNT
, rank() over (order by count(*) desc) as RANK
from RESERVA
group by FK_COD_DESTINO) A
join DESTINO B on B.PK_ID_DESTINO = A.FK_COD_DESTINO
where A.RANK <= 3
you can try HAVING rownum <=3
How about:
WITH CountsByDestination
AS
(
SELECT FK_COD_DESTINO,
COUNT(*) CNT
FROM Reservations
GROUP BY DestinationId
),
RankingByDestination
AS
(
SELECT FK_COD_DESTINO,
CNT,
RANK() OVER (ORDER BY CNT) RNK
FROM CountsByDestination
)
SELECT *
FROM RankingByDestination R
INNER JOIN Destinations D ON D.PK_ID_DESTINO = R.FK_COD_DESTINO
WHERE R.RNK BETWEEN 1 AND 3
ORDER BY R.RNK
This retains ties, so if two or more destinations share the same counts they should appear as part of the result set.