Why I get error missing right parenthesis ORA-00907 - sql

I am stuck in one problem and I have no idea where did I made mistake.
Since I check everything and every solution but I can not see what I made wrong.
SELECT
o.OrderID,
o.Order_date,
o.status,
o.OrderAcceptanceCommentsSaved,
o.OrderFileAttachment,
o.HasErrors,
o.ErrorsResolved,
(SELECT ou.Status FROM order_unload ou WHERE ou.OrderID = o.OrderID
AND rownum <= 1 ORDER BY ou.Id DESC) AS UnloadStatus
FROM
orders o
WHERE
ProjectID = 141
ORDER BY ou.Id DESC;
The problem here is second SELECT
(SELECT ou.Status FROM order_unload ou WHERE ou.OrderID = o.OrderID
AND rownum <= 1 ORDER BY ou.Id DESC) AS UnloadStatus)
However, when I want to execute only second SELECT I also get error
o.OrderID invalid identifier
Can someone guide me and tell me where I made mistake? What is wrong with this query?

You have several problems:
The ORDER BY clause is not allowed in a correlated sub-query so the SQL engine expects the query to end before the ORDER BY and there to be a closing brace at that point. Remove the ORDER BY clause in the inner select and that error would go away (and you would get a different error).
ROWNUM is applied before the ORDER BY is evaluated so, even if the query was syntactically valid, it would not do what you wanted as you would get a random row (the first the SQL engine happens to read) which would be given a ROWNUM of 1 and then the rest of the rows discarded and then that single (random) row would be ordered. You want to order first and then get the first row.
You are using ou.id to order the outer query but the ou alias is not visible in that outer select.
You can use:
SELECT o.OrderID,
o.Order_date,
o.status,
o.OrderAcceptanceCommentsSaved,
o.OrderFileAttachment,
o.HasErrors,
o.ErrorsResolved,
ou.status AS UnloadStatus
FROM orders o
LEFT OUTER JOIN (
SELECT status,
orderid,
id,
ROW_NUMBER() OVER ( PARTITION BY orderid ORDER BY id DESC ) AS rn
FROM order_unload
) ou
ON ( o.orderid = ou.OrderID AND ou.rn = 1 )
WHERE ProjectID = 141
ORDER BY ou.Id DESC;
db<>fiddle here

Related

SQL queries with slight modification giving different results

I am learning SQL and doing some exercise with analytics functions. I have following query to find out ship_name and order_value of the highest placed order. Following are my tables:
orders(id, ship_name, city_of_origination)
order_details(id, order_id, unit_price, quantity)
In order to solve this problem, I wrote following query:
select o.ship_name, od.quantity*od.unit_price, first_value(od.quantity*od.unit_price) over (order by od.quantity*od.unit_price desc)
from orders o
inner join order_details od
on o.order_id = od.order_id
limit 1
Here id the sample output after removing limit in above query:
Changing the problem statement slightly, I only want the ship_name. So I wrote this query:
select tmp.ship_name
from (select o.ship_name as ship_name, first_value(od.quantity*od.unit_price) over (order by od.quantity*od.unit_price desc) fv
from orders o
inner join order_details od
on o.order_id = od.order_id
limit 1
) tmp;
To my surprise, the result changed. Here is the result of above wuery without limit:
At the same time, if I execute following query:
select tmp.ship_name, tmp.fv
from (select o.ship_name as ship_name, first_value(od.quantity*od.unit_price) over (order by od.quantity*od.unit_price desc) fv
from orders o
inner join order_details od
on o.order_id = od.order_id
limit 1
) tmp;
I get the same result (and the expected one) as that of the first query. My question is: Why is there a difference of results in above queries?
limit without order by returns an arbitrary row. It might not even return the same row for the same query when executed subsequent times.
So, use order by to control which row is returned.
In Postgres, row order is returned based on the hidden column ctid order. Essentially, it's last-updated/last-inserted order--it just orders based on the order it finds it on-disk. Using LIMIT does not change that order, as it's still going to come out in the order that it's read out of the disk.
Using LIMIT 1 will only show you the first row it encounters off disk. To change the ordering behavior, you should use ORDER BY

Can we use order by in subquery? If not why sometime could use top(n) order by?

I'm an entry level trying to learn more about SQL,
I have a question "can we use order by in subquery?" I did look for some article says no we could not use.
But on the other hand, I saw examples using top(n) with order by in subquery:
select c.CustomerId,
c.OrderId
from CustomerOrder c
inner join (
select top 2
with TIES CustomerId,
COUNT(distinct OrderId) as Count
from CustomerOrder
group by CustomerId
order by Count desc
) b on c.CustomerId = b.CustomerId
So now I'm bit confused.
Could anyone advise?
Thank you very much.
Yes, you are right we cannot use order by in a inner query. Because it is acting as a table. A table in itself needs to be sorted when queried for different purposes.
In your query itself the inner query is select some records using Top 2. Eventhough these are top 2 records only, they form a table with 2 records which is enough for it to recognized as a table and join it with another table
The right query will be:-
SELECT * FROM
(
SELECT c.CustomerId, c.OrderId, DENSE_RANK() OVER(ORDER BY b.count DESC) AS RANK
FROM CustomerOrder c
INNER JOIN
(SELECT CustomerId, COUNT(distinct OrderId) as Count
FROM CustomerOrder GROUP BY CustomerId) b
ON c.CustomerId = b.CustomerId
) a
WHERE RANK IN (1,2);
Hope I have answered your question.
Yes we can use order by clause in sub query, for example i have a table named as product (check the screen shot of table http://prntscr.com/f15j3z). Chek this query on your side and revert me in case of any doubt.
select p1.* from product as p1 where product_id = (select p2.product_id from product as p2 order by product_id limit 0,1)
yes we can use order by in subquery,but it is pointless to use it.
It is better to use it in the outer query.There is no use of ordering the result of subquery, because result of inner query will become the input for outer query and it does not have to do any thing with the order of the result of subquery.

Count number of rows that were returned in a join

This query successfully returns only the first row of a join that could potentially have more than one row.
WITH RevisionProducts AS (
SELECT
qr.LeadID,
p.Code,
ROW_NUMBER() OVER(PARTITION BY qr.LeadID ORDER BY qr.LeadID DESC) rownumber
FROM
QuoteRevisions qr
JOIN ...
)
SELECT
l.LeadID,
...
co.Name,
rp1.Code,
0 AS CodeCount
FROM
Leads l
JOIN Companies co on co.CompanyID = l.CompanyID
JOIN RevisionProducts rp1 ON rp1.LeadID = l.ID AND rp1.rownumber = 1
What I want to do now is replace...
0 AS CodeCount
...with the actual number of rows that would have been returned in the join, had we allowed them all. Can't figure out how to do this.
I'm not sure I need the CTE, but I figured it might be handy since I most likely need to reference the same query again for the count?
EDIT:
Ok it looks like I need to be more clear. So the query with in the CTE... let's run it with a WHERE clause:
SELECT
qr.LeadID,
p.Code,
ROW_NUMBER() OVER(PARTITION BY qr.LeadID ORDER BY qr.LeadID DESC) rownumber
FROM
QuoteRevisions qr
JOIN ...
WHERE
qr.LeadID = 151
And let's say that query returns 5 rows. So, in the first query, if we DID NOT limit the join to the first row only, then the join would have returned 5 rows when Lead.LeadID got to 151. So in the final dataset, there would have been 5 rows that were identical except for the rp1.Code column.
I have already limited the number of rows to 1, which is what I wanted. But now, I want to know how many rows would have been returned.
I hope that makes more sense.
How about something like this?
WITH RevisionProducts AS (
SELECT
qr.LeadID,
p.Code,
ROW_NUMBER() OVER(PARTITION BY qr.LeadID ORDER BY qr.LeadID DESC) rownumber
COUNT(*) OVER(PARTITION BY qr.LeadID ) rowcount
FROM
QuoteRevisions qr
JOIN ...
)
SELECT
l.ID,
...
co.Name,
rp1.Code,
rp1.rowcount AS CodeCount
FROM
Leads l
JOIN Companies co on co.CompanyID = l.CompanyID
JOIN RevisionProducts rp1 ON rp1.LeadID = l.ID AND rp1.rownumber = 1

Sub query in a inner join

Well, in order to get the last entry by date i've done that query :
select cle
from ( select cle, clepersonnel, datedebut, row_number()
over(partition by clepersonnel order by datedebut desc) as rn
from periodeoccupation) as T
where rn = 1
This one is working and give me the last entry by date, so far i'm ok.
But its the first time i work with subquery and i have to make my query way more complex with multiple joins. But i cannot figure out how to make a subquery in a inner join.
This is what i try :
select personnel.prenom, personnel.nom
from personnel
inner join
( select cle, clepersonnel, datedebut, row_number()
over(partition by clepersonnel order by datedebut desc) as rn
from periodeoccupation) as T
ON personnel.cle = periodeoccupation.clepersonnel
where rn = 1
but its not working !
If you have any idea or tips...Thank you !
Simply change
ON personnel.cle = periodeoccupation.clepersonnel
to
ON personnel.cle = T.clepersonnel
The query join has been aliased with T and you have reference the alias as the table in the aliased query is out of scope in your outer statement.

Invalid identifier in double-nested query with ORDER BY and ROWNUM

I'm on Oracle and I need to use both, ORDER BY and ROWNUM, in one request. I need to double-nest my inner query, because I want to apply ORDER BY first and select with ROWNUM=1 afterwards.
My data is filtered by O.ID on outmost level. However, I get an error in my inner query, because O.ID is an unknown identifier there.
What I want:
SELECT
O.INSERTDATE OrderCreateDate,
-- Determine delivery date
(SELECT INSERTDATE FROM (
SELECT OP2.FK_ORDER, DD.ID, DD.INSERTDATE FROM MY_DELIVERYDATE_TABLE DD
JOIN MY_ORDERPOS_TABLE OP2 ON DD.FK_ORDERPOS=OP2.ID
LEFT OUTER JOIN MY_ORDER_TABLE O2 ON OP2.FK_ORDER=O2.ID
WHERE OP2.FK_ORDER=O.ID AND -- This gives me "Invalid identifier O.ID"
DD.DELFLAG IS NULL AND OP2.DELFLAG IS NULL
ORDER BY DD.CLOSED ASC, ABS(TRUNC(CURRENT_DATE-TO_DATE(TO_CHAR(DD.INSERTDATE, 'DDMMYYYY'), 'DDMMYYYY'))) ASC
) WHERE ROWNUM=1) DeliveryDate
FROM MY_ORDER_TABLE O
WHERE O.ID = 620; -- ID goes here!
The only way I get this working, is when I filter in the WHERE clause of the intermediate SELECT query. But this is slow, of course, since the inner SQL returns the entire data without filtering.
SELECT
O.INSERTDATE OrderCreateDate,
-- Determine delivery date
(SELECT INSERTDATE FROM (
SELECT OP2.FK_ORDER, DD.ID, DD.INSERTDATE FROM MY_DELIVERYDATE_TABLE DD
JOIN MY_ORDERPOS_TABLE OP2 ON DD.FK_ORDERPOS=OP2.ID
LEFT OUTER JOIN MY_ORDER_TABLE O2 ON OP2.FK_ORDER=O2.ID
WHERE DD.DELFLAG IS NULL AND OP2.DELFLAG IS NULL
ORDER BY DD.CLOSED ASC, ABS(TRUNC(CURRENT_DATE-TO_DATE(TO_CHAR(DD.INSERTDATE, 'DDMMYYYY'), 'DDMMYYYY'))) ASC
) WHERE ROWNUM=1 AND FK_ORDER=O.ID) DeliveryDate -- Filtering here
FROM MY_ORDER_TABLE O
WHERE O.ID = 620;
How can I pass O.ID to the inner query or how can this query be redesigned, still keeping ORDER BY and ROWNUM work.
My final solution as suggested by Kim Berg Hansen and improved by rims:
(I had to use MIN() instead of MAX(), though)
SELECT
O.INSERTDATE OrderCreateDate,
-- Determine delivery date
(SELECT MIN(DD.INSERTDATE) KEEP (DENSE_RANK FIRST ORDER BY
DD.CLOSED ASC, ABS(TRUNC(CURRENT_DATE-TRUNC(DD.INSERTDATE))) ASC)
FROM MY_DELIVERYDATE_TABLE DD
JOIN MY_ORDERPOS_TABLE OP2 ON DD.FK_ORDERPOS=OP2.ID
LEFT OUTER JOIN MY_ORDER_TABLE O2 ON OP2.FK_ORDER=O2.ID
WHERE OP2.FK_ORDER=O.ID AND
DD.DELFLAG IS NULL AND OP2.DELFLAG IS NULL
) DeliveryDate
FROM MY_ORDER_TABLE O
WHERE O.ID = 620; -- ID goes here!
In the scalar subquery you are using, you can only reference the tables from the "main" query "one nested level down", not any further down, as you have seen. (I believe this restriction is lifted in version 12, so maybe you can just upgrade your database? ;-)
In the scalar subquery you are trying to get the value of INSERTDATE column of the first row according to your ordering. That can also be written without nesting as follows:
SELECT
O.INSERTDATE OrderCreateDate,
-- Determine delivery date
(SELECT MAX(DD.INSERTDATE) KEEP (
DENSE_RANK FIRST ORDER BY
DD.CLOSED ASC, ABS(TRUNC(CURRENT_DATE-TO_DATE(TO_CHAR(DD.INSERTDATE, 'DDMMYYYY'), 'DDMMYYYY'))) ASC
)
FROM MY_DELIVERYDATE_TABLE DD
JOIN MY_ORDERPOS_TABLE OP2 ON DD.FK_ORDERPOS=OP2.ID
LEFT OUTER JOIN MY_ORDER_TABLE O2 ON OP2.FK_ORDER=O2.ID
WHERE OP2.FK_ORDER=O.ID AND -- This will no longer give "Invalid identifier O.ID"
DD.DELFLAG IS NULL AND OP2.DELFLAG IS NULL
) DeliveryDate
FROM MY_ORDER_TABLE O
WHERE O.ID = 620; -- ID goes here!
KEEP (DENSE_RANK FIRST tells the MAX function, that it should calculate the MAX only of those rows that rank first in the ORDER BY clause. So if your ORDER BY is "unique", MAX will be applied only to one row. If your ORDER BY is not "unique" and can have duplicates, you might think about whether you want the MAX or the MIN (or add something to the ORDER BY to make it unique.)
(If you had been on Oracle version 12, an alternative to the KEEP (DENSE_RANK trick would be to use the FIRST 1 ROW ONLY clause of the SELECT statement.)