SQL oracle error on select inside select - sql

This return single-row query subquery returns more than one row
select E.NO_ENCAN, E.NOM_ENC, TE.DESC_TYPE_ENC as TYPE_ENC,
(select sum(ITEM.MNT_VALEUR_ITE) from ENCAN left join ITEM on ITEM.NO_ENCAN = ENCAN.NO_ENCAN group by ENCAN.NO_ENCAN) as SOMME_ITEMS,
count(distinct INV.NOM_UTILISATEUR_INVITE) as NOMBRE_INVITES
from ENCAN E
left join TYPE_ENCAN TE on TE.CODE_TYPE_ENC = E.CODE_TYPE_ENC
left join INVITE INV on INV.NO_ENCAN = E.NO_ENCAN
group by E.NO_ENCAN, E.NOM_ENC, TE.DESC_TYPE_ENC
order by E.NO_ENCAN;
And if I add order by in the subquery, it returns a missing right parenthesis.
Anyone can give me any clues on what's going on?
By the way, I know that keyword/word are inversed uppercase/lowercase

You want a correlated subquery rather than a group by in the subselect. This also means that the subquery is not needed. So, this is probably what you are trying to write:
select E.NO_ENCAN, E.NOM_ENC, TE.DESC_TYPE_ENC as TYPE_ENC,
(select sum(ITEM.MNT_VALEUR_ITE)
from ITEM
where ITEM.NO_ENCAN = ENCAN.NO_ENCAN
) as SOMME_ITEMS,
count(distinct INV.NOM_UTILISATEUR_INVITE) as NOMBRE_INVITES
from ENCAN E left join
TYPE_ENCAN TE
on TE.CODE_TYPE_ENC = E.CODE_TYPE_ENC left join
INVITE INV
on INV.NO_ENCAN = E.NO_ENCAN
group by E.NO_ENCAN, E.NOM_ENC, TE.DESC_TYPE_ENC
order by E.NO_ENCAN;

Without knowing more about your schema and data, it looks to me like the issue is the 'group by ENCAN.NO_ENCAN'
I don't think you need the group by, or it is causing you issue.

If I am correctly understanding what you are trying to accomplish, I believe the subquery is unnecessary. You should just put an analytic on the SUM() call.
SELECT e.no_encan
,e.nom_enc
,te.desc_type_enc AS type_enc
,SUM(item.mnt_valeur_ite) OVER (PARTITION BY e.no_encan) somme_items
,COUNT(DISTINCT inv.nom_utilisateur_invite) AS nombre_invites
FROM encan e
LEFT JOIN type_encan te ON te.code_type_enc = e.code_type_enc
LEFT JOIN invite INV ON inv.no_encan = e.no_encan
GROUP BY e.no_encan, e.nom_enc, te.desc_type_enc
ORDER BY e.no_encan;
Details can be found here, although I would really suggest reading more about Analytic Functions in Oracle.

Your inner select statement is returning more than one row. Try adding a WHERE clause to limit your select to return one row.
select sum(ITEM.MNT_VALEUR_ITE) from ENCAN left join ITEM on ITEM.NO_ENCAN = ENCAN.NO_ENCAN
**WHERE ENCAN.NO_ENCAN = '1234'**
group by ENCAN.NO_ENCAN

I would do it like this:
with INVITE_ROLLUP as
(
select ENCAN.NO_ENCAN, sum(ITEM.MNT_VALEUR_ITE) as NOMBRE_INVITES
from ENCAN
left join ITEM on ITEM.NO_ENCAN = ENCAN.NO_ENCAN
group by ENCAN.NO_ENCAN
)
select
E.NO_ENCAN,
E.NOM_ENC,
TE.DESC_TYPE_ENC as TYPE_ENC,
INVITE_ROLLUP.NOMBRE_INVITES AS NOMBRE_INVITES
from ENCAN E
left join TYPE_ENCAN TE on TE.CODE_TYPE_ENC = E.CODE_TYPE_ENC
left join INVITE INV on INV.NO_ENCAN = E.NO_ENCAN
left join INVITE_ROLLUP ON E.NO_ENCAN = INVITE_ROLLUP.NO_ENCAN
group by E.NO_ENCAN, E.NOM_ENC, TE.DESC_TYPE_ENC
order by E.NO_ENCAN;

The Group by ENCAN.NO_ENCAN is causing the issue. Since you are not selecting that column, you don't need to group by that.

Related

Why am I getting ORA-00979: not a GROUP BY expression error?

Just a disclaimer, I'm very new to SQL... so please go easy on me. This is the script I have so far, and for the most part it's working, I'm just having a problem trying to only get the most recent DTTM record. What's wrong with my group by expression?
select
i.item_name,
i.description,
MAX(w.LAST_UPDATED_DTTM)as MOST_RECENT_DTTM,
w.last_updated_source,
w.tc_lpn_id,
lh1.dsp_locn as from_locn,
l.dsp_locn,
lh2.dsp_locn as dest_locn,
lp.inventory_lock_code
from wm_inventory W
left join Locn_hdr l
on w.location_id = L.locn_id
left join item_cbo i
on w.item_id = i.item_id
left join lpn_lock lp
on lp.tc_lpn_id = w.tc_lpn_id
left join task_dtl td
on td.cntr_nbr = w.tc_lpn_id
left join locn_hdr lh2
on td.dest_locn_id = lh2.locn_id
left join locn_hdr lh1
on td.PULL_LOCN_ID = lh1.locn_id
where i.color_desc = '00607'
and l.DSP_LOCN like '%SRT-0%'
and w.tc_lpn_id not like '%J%'
group by i.item_name, i.description, w.last_updated_source, w.tc_lpn_id, l.dsp_locn, lp.inventory_lock_code, lh1.dsp_locn, lh2.dsp_locn
order by w.last_updated_dttm asc;
To order by a column that's aggregated, you have to specify the aggregation:
order by MAX(w.last_updated_dttm) asc
Some implementations (including Oracle, you gave an Oracle error code) allow you to order by the aggregated column's alias:
order by MOST_RECENT_DTTM asc

SQL many to many select people with multiple vacancies

I am working with sql server through SSMS right now. How can i choose all people with multiple(>2)vacancies?
I am trying something like that, but i dont understand how to make part with "more than 2 vacancies"?
SELECT dbo.applicants.FirstName, dbo.vacancy.Name
FROM dbo.applicants INNER JOIN
dbo.VacancyApplicant ON dbo.applicants.id = dbo.VacancyApplicant.ApplicantId INNER JOIN
dbo.vacancy ON dbo.VacancyApplicant.VacancyId = dbo.vacancy.id WHERE dbo.vacancy.Name='third vacancy'
SELECT dbo.applicants.FirstName, dbo.vacancy.Name
FROM dbo.applicants A INNER JOIN
dbo.VacancyApplicant V ON A.id = V.ApplicantId
WHERE EXIST(
SELECT 1
FROM dbo.applicants INNER JOIN
dbo.VacancyApplicant ON dbo.applicants.id =
dbo.VacancyApplicant.ApplicantId INNER JOIN
dbo.vacancy ON dbo.VacancyApplicant.VacancyId = dbo.vacancy.id
WHERE A.id=dbo.applicants.id
GROUP BY dbo.applicants.id,dbo.vacancy.id
HAVING COUNT(1)>2
)
Group By and Having are you basic answer. Below is a simple solution, might not be ideal, but can give you the idea.
I am finding target "applicants" ids in subquery, that uses GROUP BY and HAVING then outer query joins to that to output FirstName and LastName of applicant
SELECT dbo.applicants.FirstName, dbo.applicants.LastName FROM
dbo.applicants a INNER JOIN
(
SELECT dbo.applicants.id
FROM dbo.applicants INNER JOIN
dbo.VacancyApplicant ON dbo.applicants.id = dbo.VacancyApplicant.ApplicantId INNER JOIN
dbo.vacancy ON dbo.VacancyApplicant.VacancyId = dbo.vacancy.id AND dbo.vacancy.Name='third vacancy'
GROUP BY dbo.applications.id
HAVING COUNT(dbo.vacancy.id) > 2
) targetIds ON a.id = targetIds.id
"more than 2 vacancies"?
Your question only mentions vacancies but your query is filtering for a particular name. I assume you really want more than two of that name.
If I understand correctly, you want aggregation:
SELECT a.FirstName, a.Name
FROM dbo.applicants a INNER JOIN
dbo.VacancyApplicant va
ON a.id = va.ApplicantId INNER JOIN
dbo.vacancy v
ON va.VacancyId = v.id
WHERE v.Name = 'third vacancy'
GROUP BY a.FirstName, v.Name
HAVING COUNT(*) > 2;
Note the use of table aliases. They make the query easier to write and to read.
WITH TempCTE AS (
SELECT DISTINCT ap.FirstName
,vc.Name
,COUNT (va.VacancyId) OVER (PARTITION BY ap.id) AS NoOfVacancies
FROM dbo.applicants ap
JOIN dbo.VacancyApplicant va
ON ap.id = va.ApplicantId
JOIN dbo.vacancy vc
ON va.VacancyId = vc.id
)
SELECT FirstName,[Name], NoOfVacancies FROM TempCTE
WHERE NoOfVacancies > 2

Subquery Where clause invalid in select list

I am trying to create a numerator(num) and denominator(den) column that I will later use to create a metric value. In my numerator column, I need to have a criteria that my denominator column does not have. When I add the where clause to my sub query, I am getting the error below. I do not want to add INRInRange to my Group By clause.
Column 'dbo.PersonDetailB.INRInRange' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause."
SELECT
dbo.PersonDetailSpecialty.PracticeAbbrevName,
(SELECT COUNT(DISTINCT dbo.Problem.PID) WHERE PersonDetailB.INRInRange='True') AS num,
COUNT(DISTINCT dbo.Problem.PID) AS den
FROM
dbo.PersonDetailB
RIGHT OUTER JOIN
dbo.PersonDetailSpecialty ON dbo.PersonDetailB.PID = dbo.PersonDetailSpecialty.PID
LEFT OUTER JOIN
dbo.Problem ON dbo.PersonDetailSpecialty.PID = dbo.Problem.PID
GROUP BY
practiceabbrevname
Create a sub-query that counts PersonDetailB.INRInRange and LEFT OUTER JOIN it with the original query.
SELECT Main.PracticeAbbrevName, InRange.Num AS num, Main.den
FROM
(SELECT
dbo.PersonDetailSpecialty.PracticeAbbrevName,
COUNT(DISTINCT dbo.Problem.PID) AS den
FROM
dbo.PersonDetailB
RIGHT OUTER JOIN
dbo.PersonDetailSpecialty ON dbo.PersonDetailB.PID = dbo.PersonDetailSpecialty.PID
LEFT OUTER JOIN
dbo.Problem ON dbo.PersonDetailSpecialty.PID = dbo.Problem.PID
GROUP BY
practiceabbrevname) Main
LEFT OUTER JOIN
(SELECT practiceabbrevname, COUNT(DISTINCT dbo.Problem.PID) Num WHERE PersonDetailB.INRInRange='True' GROUP BY practiceabbrevname) InRange ON Main.practiceabbrevname = InRange.practiceabbrevname
The problem with this statement:
SELECT dbo.PersonDetailSpecialty.PracticeAbbrevName,
(SELECT COUNT(DISTINCT dbo.Problem.PID) WHERE PersonDetailB.INRInRange = 'True') AS num,
COUNT(DISTINCT dbo.Problem.PID) AS den
is that PersonDetailB.INRInRange1 doesn't have a unique value in each group. It is possible that it does. One method is to add it to the GROUP BY:
GROUP BY practiceabbrevname, PersonDetailB.INRInRange
Another method would use an aggregation function in the subquery:
SELECT dbo.PersonDetailSpecialty.PracticeAbbrevName,
(SELECT COUNT(DISTINCT dbo.Problem.PID) WHERE MAX(PersonDetailB.INRInRange) = 'True') AS num,
COUNT(DISTINCT dbo.Problem.PID) AS den
Join a separate table as a different name on the where criteria that was in the sub query.
SELECT PersonDetailSpecialty.PracticeAbbrevName,
COUNT(PDTRUE.PID) as num,
COUNT(dbo.Problem.PID) AS den
FROM dbo.PersonDetailSpecialty LEFT OUTER JOIN
dbo.PersonDetailB as PDTRUE ON dbo.PersonDetailSpecialty.PID = PDTRUE.PID and PDTRUE.INRInRange='True' LEFT OUTER JOIN
dbo.PersonDetailB ON dbo.PersonDetailSpecialty.PID = PersonDetailB.PID LEFT OUTER JOIN
dbo.Medicate ON dbo.PersonDetailSpecialty.PID = dbo.Medicate.PID LEFT OUTER JOIN
dbo.Problem ON dbo.PersonDetailSpecialty.PID = dbo.Problem.PID
GROUP BY PersonDetailSpecialty.PracticeAbbrevName
This is the relevant code needed in the FROM section
dbo.PersonDetailSpecialty LEFT OUTER JOIN dbo.PersonDetailB as PDTRUE
ON dbo.PersonDetailSpecialty.PID = PDTRUE.PID and PDTRUE.INRInRange='True'
This lets you add
COUNT(PDTRUE.PID) as num,
as a simple part of the overall select query

SQL using Min()

I need to display the Youngest Athlete in my database. MIN(Athlete.Age) works fine, but I need the name as well obviously. When I add the Name to select I get about 5 results. How do I display just the MIN(Age) and her name?
select MIN(Athlete.Age), Athlete.Name
from (((Country INNER JOIN Athlete ON Country.Country_Code = Athlete.Country_Code)
INNER JOIN Athlete_event
ON Athlete.Athlete_ID = Athlete_event.Athlete_ID2)
INNER JOIN Event
ON Event.Event_ID = Athlete_event.Event_ID2)
Where Athlete.Athlete_ID = Event.Award_Gold
GROUP BY Athlete.Name;
You may have 5 athletes with the same age. The query will not know which one you want.
If you have the birth-dates in the database, try using that.
[edit] Storing age is generally not a good idea because in one year, they will all be wrong.
[edit2] Note it is still possible for people to share the same birthday.
select *
from
(
select Athlete.Age, Athlete.Name
from (((Country INNER JOIN Athlete ON Country.Country_Code = Athlete.Country_Code)
INNER JOIN Athlete_event
ON Athlete.Athlete_ID = Athlete_event.Athlete_ID2)
INNER JOIN Event
ON Event.Event_ID = Athlete_event.Event_ID2)
Where Athlete.Athlete_ID = Event.Award_Gold
order by Age
) v
where rownum=1;
add this line on your query
HAVING MIN(Athlete.Age) = (SELECT MIN(AGE) from Athlete)
so your final query will look like
SELECT MIN(Athlete.Age),
Athlete.NAME
FROM (((Country INNER JOIN Athlete
ON Country.Country_Code = Athlete.Country_Code
) INNER JOIN Athlete_event
ON Athlete.Athlete_ID = Athlete_event.Athlete_ID2
) INNER JOIN Event
ON Event.Event_ID = Athlete_event.Event_ID2
)
WHERE Athlete.Athlete_ID = Event.Award_Gold
GROUP BY Athlete.NAME
HAVING MIN(Athlete.Age) = ( SELECT MIN(AGE) FROM Athlete )
if you use the GROUP BY in your query, you are searching the min age of each athlete.
You should try with a subquery. Adding it to your where clause. Something like: (Not tested)
select Athlete.Age, Athlete.Name
from Athlete
AND Athlete.Age = (select MIN(Athlete.Age)
from (((Country INNER JOIN Athlete ON Country.Country_Code = Athlete.Country_Code)
INNER JOIN Athlete_event
ON Athlete.Athlete_ID = Athlete_event.Athlete_ID2)
INNER JOIN Event
ON Event.Event_ID = Athlete_event.Event_ID2)
Where Athlete.Athlete_ID = Event.Award_Gold);

Combine three SQL queries

I'm trying to fetch data for my forum's index. Fetching a list of all the boards, the number of threads in that board, and the number of posts for each of those threads in that board.
SELECT
board.*,
IFNULL(a.thread_count, 0) AS thread_count,
b.post_count
FROM
(SELECT * FROM r_forum_boards ORDER BY position) board
LEFT OUTER JOIN
(SELECT r_forum_threads.board, r_forum_threads.id,
COUNT(r_forum_threads.id) AS thread_count
FROM r_forum_threads) a
ON board.id = a.board
LEFT OUTER JOIN
(SELECT r_forum_posts.thread_id, COUNT(*) AS post_count
FROM r_forum_posts) b
ON b.thread_id = a.id
The problem is that post_count is returning NULL. I've tried a few different variations of this, but none of them are working.
I'm guessing from the IFNULL that your SQL is MySQL-flavored. In that case, you can use COUNT DISTINCT to simplify things.
SELECT
board.id,
COUNT(DISTINCT r_forum_threads.id) AS thread_count,
COUNT(r_forum_posts.id) AS post_count
FROM board
LEFT OUTER JOIN r_forum_threads ON board.id = r_forum_threads.board
LEFT OUTER JOIN r_forum_posts ON r_forum_posts.thread_id = r_forum_threads.id
GROUP BY board.id
ORDER BY board.position
Depending on how much of board.* you actually need, either add columns to the SELECT and GROUP or use this as a subquery to join back to board.
Try putting in a GROUP clause:
LEFT OUTER JOIN
(SELECT r_forum_threads.board, r_forum_threads.id, COUNT(r_forum_threads.id) AS thread_count FROM r_forum_threads GROUP BY r_forum_threads.id) a
ON board.id = a.board
LEFT OUTER JOIN
(SELECT r_forum_posts.thread_id, COUNT(*) AS post_count FROM r_forum_posts GROUP BY r_forum_posts.thread_id) b ON b.thread_id = a.id
See if that does the trick.
Perhaps because you are missing the Group By clause in your subqueries? In addition, you do not need the first subquery.
Select board...
, Coalesce(a.thread_count, 0) AS thread_count
, b.post_count
From r_forum_boards
Left Join (
Select r_forum_threads.board
, r_forum_threads.id
, Count(r_forum_threads.id) AS thread_count
From r_forum_threads
Group By r_forum_threads.board
, r_forum_threads.id
) a
On a.board = board.id
Left Join (
Select r_forum_posts.thread_id
, Count(*) AS post_count
From r_forum_posts
Group By r_forum_posts.thread_id
) As b
On b.thread_id = a.id
Order By r_forum_boards.position
You might consider changing the query slightly to make it easier to test:
Select board...
, Coalesce(a.thread_count, 0) AS thread_count
, A.post_count
From r_forum_boards
Left Join (
Select r_forum_threads.board
, r_forum_threads.id
, Count(r_forum_threads.id) AS thread_count
, Posts.post_count
From r_forum_threads
Left Join (
Select r_forum_posts.thread_id
, Count(*) AS post_count
From r_forum_posts
Group By r_forum_posts.thread_id
) As Posts
On Posts.thread_id = r_forum_threads.Id
Group By r_forum_threads.board
, r_forum_threads.id
) As A
On A.board = board.id
Order By r_forum_boards.position
In this way, you can run the single inner query and ensure you A. get results and B. get values for post_count.
The problem I see is that you're trying to get 2 related but slightly conflicting pieces of data, and probably 2 queries will get you what you need.
You first need a query to get the board names and the number of threads in each board.
Select Board.*, GroupThread.threadCount
FROM r_forum_boards Board
INNER JOIN (Select board_id, count(*) as threadCount from r_forum_threads group by board_id) GroupThread ON Board.board_id = GroupThread.board_id
Second, for each thread, you need the posts, which are calculated in basically the same way:
Select Thread.*, GroupPosts.postCount
FROM r_forum_threads Thread
INNER JOIN (Select thread_id, count(*) as postCount from r_forum_posts group by thread_id) GroupPosts ON Thread.board_id = GroupPosts.thread_id
In each of these cases, you look at the parent object, and count the children.