ORA-00907: Missing right parenthesis ,maybe problem with SubQuery - sql

This Query give an error
ORA-00907: Missing right parenthesis
I can't find any parenthesis problem
select
(select PRE_DESIG_ID FROM AUTHORIZATION
WHERE PROJECT_ID = 5 and PRE_DESIG_ID =48 and
ROWNUM=1 order by ID DESC) AS PERPARED_BY
,(Select PRE_LAST_DATE From (Select PRE_LAST_DATE From AUTHORIZATION
Where PROJECT_ID = 5 and PRE_DESIG_ID = 48 Order By ID Desc)
Where ROWNUM = 1) AS PRE_END_DT from AUTHORIZATION au
LEFT join PROJECT p on AU.PROJECT_ID =p.PROJECT_ID
LEFT join DESIGNATION d on au.AU_DESIG_ID=d.DESIGID;

Your problem is this:
ORDER BY id DESC
You can't use ORDER BY in a subquery that acts as a column expression. If you remove it the error you're facing will go away.
But, I would prefer you rewrite your query using a with clause, since both of your sub-query expression fetches the same row.
WITH auth AS ( SELECT *
FROM ( SELECT pre_desig_id AS perpared_by,
pre_last_date AS pre_end_dt
FROM authorization
WHERE project_id = 5
AND pre_desig_id = 48 ORDER BY id DESC )
WHERE ROWNUM = 1 )
SELECT a.perpared_by,a.pre_end_dt
FROM authorization au
LEFT JOIN project p ON au.project_id = p.project_id
LEFT JOIN designation d ON au.au_desig_id = d.desigid
CROSS JOIN auth a;

You need to convert the derived column's (prepared_by) subquery similar to pre_end_dt's subquery as below
Select
(Select pre_desig_id
From (Select pre_desig_id,
row_number() over (order by ID desc) as rn
From Authorization
Where project_id = 5
and pre_desig_id = 48
)
Where rn = 1) as prepared_by,
(Select pre_last_date
From (Select pre_last_date,
row_number() over (order by ID desc) as rn
From Authorization
Where project_id = 5
and pre_desig_id = 48
)
Where rn = 1) as pre_end_dt
From Authorization au
Left Join Project p
on au.project_id = p.project_id
Left Join Designation d
on au.au_desig_id = d.desigid;
where Order By ID Desc part produces the error, that should be wrapped within an extra subquery, otherwise compiler doesn't allow to use Order By directly as this. i.e. restriction (rownum=1) and using order by are not allowed to work at the same level, they need to exist within outer, and inner select statements respectively.
In fact, it's better to use row_number() window analytic function rather than rownum pseudocolumn, which is untrustable for most cases since it is generated before sorting.
Even if using Where rownum = 1 with order by ID desc for inner select statement is enough for you to use, rather make an habit using row_number() function.

Related

Join Table + Group by + Sum + Finding Nth highest sum value

I have 2 tables -
Broker Table ->Column names-->ID,Name,City,branch
Policy Table ->Column names-->Policy_num,Broker_id,Premium
I need to find the broker name having 4th highest sum of the premiums.
Please suggest the SQL query for this.
NOTE:I am looking for a query that can run on all platforms.So please don't use
TOP,LIMIT or ROWNUMBER functions.
I am looking for a query similar to mentioned below,which is just finding 4th highest salary from a table.
SELECT Salary
FROM EmployeeSalary Emp1
WHERE 3 = (
SELECT COUNT( DISTINCT ( Emp2.Salary ) )
FROM EmployeeSalary Emp2
WHERE Emp2.Salary > Emp1.Salary
)
First, you don't need a join, because all the information you need is in the policy table. You can join in broker at the end, if you want.
Second, the question is ambiguous, because it doesn't specify how to handle ties.
But basically, you can follow the same structure as the query in your question:
SELECT p.BrokerId
FROM Policy p
GROUP BY p.BrokerId
HAVING 4 = (SELECT COUNT(*)
FROM (SELECT SUM(p2.Premium) as sumpremium
FROM Policy p2
GROUP BY p2.BrokerId
)
WHERE sumpremium >= SUM(p.premium)
);
But ironically, this is a very, very poor way to approach the problem. Why? There is no guarantee that the SUM() on the two aggregation queries will be exactly the same.
This difference can occur in two ways. If premium is a floating point number, then the order of addition can make a difference. Adding values near zero to a large value loses precision. So check this out:
select ( (v1 + v2) + v3), (v1 + (v2 + v3))
from (select cast(100000000.0001 as float) as v1, cast(-100000001.0001 as float) as v2, cast(0.000002 as float) as v3) x;
Even with fixed point numbers you can still have problems, because adding all the positive numbers first can result in an overflow.
It is really better to do arithmetic calculations once rather than doing them multiple times and then comparing the values.
Try this-
SELECT *
FROM
(
SELECT B.Broker_ID,A.Name,
SUM(B.Premium) S_P,
ROW_NUMBER() OVER (ORDER BY SUM(B.Premium) DESC) RN
FROM Broker A
INNER JOIN Policy B ON A.ID = B.Broker_ID
GROUP BY B.Broker_ID,A.Name
)A
WHERE A.RN = 4
Another option you can use TOP twice to get your desired output as below-
SELECT TOP 1 * FROM
(
SELECT TOP 4
B.Broker_ID,A.Name,
SUM(B.Premium) S
FROM Broker A
INNER JOIN Policy B ON A.ID = B.Broker_ID
GROUP BY B.Broker_ID,A.Name
ORDER BY SUM(your_id_column) DESC
)A
ORDER BY S
Another option considering MSSQL-
SELECT * FROM
(
SELECT B.Broker_ID,A.Name,
SUM(B.Premium) S_P,
COUNT(*) OVER (ORDER BY SUM(B.Premium) DESC ROWS UNBOUNDED PRECEDING) AS RN
FROM Broker A
INNER JOIN Policy B ON A.ID = B.Broker_ID
GROUP BY B.Broker_ID,A.Name
)A
WHERE A.RN = 4
Another option considering your sample query-
SELECT * FROM
(
SELECT B.Broker_ID, A.Name, SUM(B.Premium) S
FROM Broker A INNER JOIN Policy B ON A.ID = B.Broker_ID
GROUP BY B.Broker_ID,A.Name
)A
WHERE 3 = (
SELECT COUNT(*)
FROM
(
SELECT B.Broker_ID, A.Name, SUM(B.Premium) S
FROM Broker A INNER JOIN Policy B ON A.ID = B.Broker_ID
GROUP BY B.Broker_ID,A.Name
) B
WHERE B.S > A.S
)

Removing duplicates *after* ordering in a SQL query

I have the following SQL query:
select
id,
name
from
project
inner join job on project.id = job.project_id
where
job.user_id = 'me'
order by
project.modified desc
limit 10
The idea is to get information about the 10 most recently used projects for a given user.
The problem is that this can return duplicates in the case where multiple jobs have the same project. Instead of having duplicates, I want to order all rows by modified desc, remove duplicates based on id and name, then limit to 10.
I've not been able to figure out how to achieve this. Can anyone point me in the right direction?
You are getting duplicates because of the join. As you only want columns from the project table (I assume id and name are from that table), not creating the duplicates in the first place would be better than removing them after the join:
select p.id,
p.name
from project p
where exists (select *
from job job
where job.project_id = p.id
and job.user_id = 'me')
order by p.modified desc
limit 10
try this using row_number - it will work on postgresql
select * from
(select
id,
name,row_number() over(partition by id,name order by modified desc) as rn
from
project inner join job on project.id = job.project_id
where
job.user_id = 'me')a where rn=1 order by modified desc limit 10
Did you try SELECT DISTINCT?
select distinct
id,
name
from
project
inner join job on project.id = job.project_id
where
job.user_id = 'me'
order by
project.modified desc
limit 10
I ended up with the following, which seems to work fine:
select
p.id,
p.name
from
project p
inner join
(
select
j.id,
max(j.modified) as max_modified
from
job j
where
t.user_id = 'me'
group by
j.id
order by
max_modified desc
limit 10
) ids on p.id = ids.id
order by
max_modified desc

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.

oracle select from 2 table restrict from 1 table and order by another

I have book and recipient table. I want to select maximum 20 rows order by recipient table's membershipdate column. After I got it, i want to order it by book table's id column. I wrote that sql. Is there any way to do this with less code?
SELECT *
FROM ( SELECT *
FROM ( SELECT b.*
FROM book b
JOIN recipient r ON r.id = b.recipient_id
WHERE b.bookno = 115
ORDER BY r.membershipdate DESC
)
WHERE ROWNUM <= 20
)
ORDER BY ID DESC
You can remove one layer of select:
SELECT *
FROM (
SELECT b.*
FROM book b
JOIN recipient r ON r.id = b.recipient_id
WHERE b.bookno = 115
ORDER BY r.membershipdate DESC
)
WHERE ROWNUM <= 20
ORDER BY id DESC;
Selecting b.* isn't normally a good, idea, it's better to specify the columns you actually want, even if you do really want them all - to make sure you get them in the order you expect.
You can also look at the row_number() analytic function in place of rownum, but that will give you slightly more code - not that it should matter, the effectiveness and efficiency of the query it rather more important that its length.
Doesn't this work?
SELECT * FROM
(SELECT b.*
FROM book b
JOIN recipient r ON r.id = b.recipient_id
WHERE b.bookno = 115 ORDER BY r.membershipdate DESC
) WHERE ROWNUM <= 20
ORDER BY ID DESC
In oracle you can use ROW_NUMBER() function to reduce the code as you want -
and you can even mix your Order columns also.
select * from (
select b.*,row_number() over (order by r.membershipdate desc) cnt
from book b
JOIN recipient r ON r.id = b.recipient_id
WHERE b.bookno = 115
order by cnt,b.id desc
) where cnt<=20;

MYSQL top N rows from multiple table join

Like, there is top keyword in sql server 2005, how to select top 1 row in mysql if i have join on multiple table & want to retrieve extreme of each ID/column. Limit restricts the no. of row returns so it can't solve my problem.
SELECT v.*
FROM document d
OUTER APPLY
(
SELECT TOP 1 *
FROM version v
WHERE v.document = d.id
ORDER BY
v.revision DESC
) v
or
SELECT v.*
FROM document d
LEFT JOIN
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY v.id ORDER BY revision DESC)
FROM version
) v
ON v.document = d.id
AND v.rn = 1
The latter is more efficient if your documents usually have few revisions and you need to select all or almost all documents; the former is more efficient if the documents have many revisions or you need to select just a small subset of documents.
Update:
Sorry, didn't notice the question is about MySQL.
In MySQL, you do it this way:
SELECT *
FROM document d
LEFT JOIN
version v
ON v.id =
(
SELECT id
FROM version vi
WHERE vi.document = d.document
ORDER BY
vi.document DESC, vi.revision DESC, vi.id DESC
LIMIT 1
)
Create a composite index on version (document, revision, id) for this to work fast.
If I understand you correctly, top doesn't solve your problem either. top is exactly equivalent to limit. What you are looking for is aggregate functions, like max() or min() if you want the extremes. for example:
select link_id, max(column_a), min(column_b) from table_a a, table_b b
where a.link_id = b.link_id group by link_id