SQL print subqueries' selected value - sql

The query below works talking about its functionality, but I wanted to select the result of the multiplication too, as you can see:
SELECT
I.Nome, I.Cognome, I.Stipendio, GP
FROM
Impiegati I
WHERE
I.Stipendio >= ALL(SELECT (progetti.Budget * partecipazioni.Percentuale / 100) AS GP /*HERE*/
FROM Partecipazioni, Progetti
WHERE Partecipazioni.CodProgetto = Progetti.CodProgetto
AND I.ID = partecipazioni.ID)
ORDER BY
I.Nome ASC;
I get this error:
Unknown column 'GP' in 'field list'

You need to move the logic to the from clause in order to return the value. The equivalent logic is basically:
select I.Nome, I.Cognome, I.Stipendio, pp.GP
from Impiegati I join
(select id, max(pr.Budget * pa.Percentuale / 100) as GP
from Partecipazioni pa join
Progetti pr
on pa.CodProgetto = pr.CodProgetto
group by pa.id
) pp
on I.ID = pp.ID
where I.Stipendio >= pp.gp
The only difference is if there are no matches in the subquery (the query can be tweaked for this case if necessary).
Note the use of proper, explicit, standard, readable JOIN syntax. Never use commas in the FROM clause.
Edit: Fixed the displayed error.

Related

How to fix SQL query to Left Join a subquery with Where clause?

I'm new to SQL and I'm not certain why I am getting this error. I am trying to left join a sub-query to another query in sql developer.
This is the first query,
SELECT DISTINCT
tl.species,
ag.age
FROM
age_list ag,
tree_list tl
WHERE
ag.tree_id = tl.tree_id
And then the sub-query I would like to left join where the tree_id = tree_number is,
SELECT DISTINCT
sl.tree_spon,
sl.tree_number
FROM spon_list sl
WHERE
sl.tree_spon < 10
When trying to do this I've tried to use,
SELECT DISTINCT
tl.species,
ag.age,
q1.tree_spon
FROM
age_list ag,
tree_list tl
LEFT OUTER JOIN (SELECT DISTINCT
sl.tree_spon,
sl.tree_number
FROM spon_list sl
WHERE sl.tree_spon < 10) q1 on q1.tree_number = tree_list.tree_id
WHERE
ag.tree_id = tl.tree_id
Whatever I change in terms of the alias' for the columns and tables I always get the error, "ORA-00904: invalid identifier error", and that "tree_list.tree_id is invalid identifier", though separately the queries run fine.
Can anyone help, is it an issue with both queries joining on the tl.tree_id?
You can use the ANSI join syntax throughout (rather than mixing in legacy comma joins), joining on ag.tree_id = sl.tree_number (or tl.tree_id = sl.tree_number but they're both equal given the previous join) and putting the filter on sl.tree_spon < 10 into the ON clause as well:
SELECT DISTINCT
tl.species,
ag.age,
sl.tree_spon,
sl.tree_number
FROM age_list ag
INNER JOIN tree_list tl
ON (ag.tree_id = tl.tree_id)
LEFT OUTER JOIN spon_list sl
ON (ag.tree_id = sl.tree_number AND sl.tree_spon < 10)
Change tree_list.tree_id to tl.tree_id

SQL subquery with join to main query

I have this:
SELECT
SU.FullName as Salesperson,
COUNT(DS.new_dealsheetid) as Units,
SUM(SO.New_profits_sales_totaldealprofit) as TDP,
SUM(SO.New_profits_sales_totaldealprofit) / COUNT(DS.new_dealsheetid) as PPU,
-- opportunities subquery
(SELECT COUNT(*) FROM Opportunity O
LEFT JOIN Account A ON O.AccountId = A.AccountId
WHERE A.OwnerId = SU.SystemUserId AND
YEAR(O.CreatedOn) = 2022)
-- /opportunities subquery
FROM New_dealsheet DS
LEFT JOIN SalesOrder SO ON DS.New_DSheetId = SO.SalesOrderId
LEFT JOIN New_salespeople SP ON DS.New_SalespersonId = SP.New_salespeopleId
LEFT JOIN SystemUser SU ON SP.New_SystemUserId = SU.SystemUserId
WHERE
YEAR(SO.New_purchaseordersenddate) = 2022 AND
SP.New_SalesGroupIdName = 'LO'
GROUP BY SU.FullName
I'm getting an error from the subquery:
Column 'SystemUser.SystemUserId' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Is it possible to use the SystemUser table join from the main query in this way?
As has been mentioned extensively in the comments, the error is actually telling you the problem; SU.SystemUserId isn't in the GROUP BY nor in an aggregate function, and it appears in the SELECT of the query (albeit in the WHERE of a correlated subquery). Any columns in the SELECT must be either aggregated or in the GROUP BY when using one of the other for a query scope. As the column in question isn't aggregated nor in the GROUP BY, the error occurs.
There are, however, other problems. Like mentioned ikn the comments too, your LEFT JOINs make little sense, as many of the tables you LEFT JOIN to require a column in that table to have a non-NULL value; it is impossible for a column to have a non-NULL value if a row was not found.
You also use syntax like YEAR(<Column Name>) = <int Value> in the WHERE; this is not SARGable, and thus should be avoided. Use explicit date boundaries instead.
I assume here that SU.SystemUserId is a primary key, and so should be in the GROUP BY. This is probably a good thing anyway, as a person's full name isn't something that can be used to determine who a person is on their own (take this from someone who shared their name, date of birth and post code with another person in their youth; it caused many problems on the rudimentary IT systems of the time). This results in a query like this:
SELECT SU.FullName AS Salesperson,
COUNT(DS.new_dealsheetid) AS Units,
SUM(SO.New_profits_sales_totaldealprofit) AS TDP,
SUM(SO.New_profits_sales_totaldealprofit) / COUNT(DS.new_dealsheetid) AS PPU,
(SELECT COUNT(*)
FROM dbo.Opportunity O
JOIN dbo.Account A ON O.AccountId = A.AccountId --A.OwnerID must have a non-NULL value, so why was this a LEFT JOIN?
WHERE A.OwnerId = SU.SystemUserId
AND O.CreatedOn >= '20220101' --Don't use YEAR(<Column Name>) = <int Value> syntax, it isn't SARGable
AND O.CreatedOn < '20230101') AS SomeColumnAlias
FROM dbo.New_dealsheet DS
JOIN dbo.SalesOrder SO ON DS.New_DSheetId = SO.SalesOrderId --SO.New_purchaseordersenddate must have a non-NULL value, so why was this a LEFT JOIN?
JOIN dbo.New_salespeople SP ON DS.New_SalespersonId = SP.New_salespeopleId --SP.New_SalesGroupIdName must have a non-NULL value, so why was this a LEFT JOIN?
LEFT JOIN dbo.SystemUser SU ON SP.New_SystemUserId = SU.SystemUserId --This actually looks like it can be a LEFT JOIN.
WHERE SO.New_purchaseordersenddate >= '20220101' --Don't use YEAR(<Column Name>) = <int Value> syntax, it isn't SARGable
AND SO.New_purchaseordersenddate < '20230101'
AND SP.New_SalesGroupIdName = 'LO'
GROUP BY SU.FullName,
SU.SystemUserId;
Doing such a sub-query is bad on a performance-wise point of view
better do it like this:
SELECT
SU.FullName as Salesperson,
COUNT(DS.new_dealsheetid) as Units,
SUM(SO.New_profits_sales_totaldealprofit) as TDP,
SUM(SO.New_profits_sales_totaldealprofit) / COUNT(DS.new_dealsheetid) as PPU,
SUM(csq.cnt) as Count
FROM New_dealsheet DS
LEFT JOIN SalesOrder SO ON DS.New_DSheetId = SO.SalesOrderId
LEFT JOIN New_salespeople SP ON DS.New_SalespersonId = SP.New_salespeopleId
LEFT JOIN SystemUser SU ON SP.New_SystemUserId = SU.SystemUserId
-- Moved subquery as sub-join
LEFT JOIN (SELECT a.OwnerId, YEAR(o.CreatedOn) as year, COUNT(*) cnt FROM Opportunity O
LEFT JOIN Account A ON O.AccountId = A.AccountId
GROUP BY a.OwnerId, YEAR(o.CreatedOn) as csq ON csq.OwnerId = su.SystemUserId and csqn.Year = 2022
WHERE
YEAR(SO.New_purchaseordersenddate) = 2022 AND
SP.New_SalesGroupIdName = 'LO'
GROUP BY SU.FullName
So you have a nice join and a clean result
The query above is untested

How to do operations between a column and a subquery

I would like to know how I can do operations between a column and a subquery, what I want to do is add to the field Subtotal what was obtained in the subquery Impuestos, the following is the query that I am using for this case.
Select
RC.PURCHID;
LRC.VALUEMST as 'Subtotal',
isnull((
select sum((CONVERT(float, TD1.taxvalue)/100)*LRC1.VALUEMST ) as a
FROM TAXONITEM TOI1
inner join TAXDATA TD1 ON (TD1.TAXCODE = TOI1.TAXCODE and RC.DATAAREAID = TD1.DATAAREAID)
inner join TRANS LRC1 on (LRC1.VEND = RC.RECID)
WHERE TOI1.TAXITEMGROUP = PL.TAXITEMGROUP and RC.DATAAREAID = TOI1.DATAAREAID
), 0) Impuestos
from VEND RC
inner join VENDTABLE VTB on VTB.ACCOUNTNUM = RC.INVOICEACCOUNT
inner join TRANS LRC on (LRC.VEND = RC.RECID)
inner join PURCHLINE PL on (PL.LINENUMBER =LRC.LINENUM and PL.PURCHID =RC.PURCHID)
where year (RC.DELIVERYDATE) =2021 and RC.PURCHASETYPE =3 order by RC.PURCHID;
Hope someone can give me some guidance when doing operations with subqueries.
A few disjointed facts that may help:
When a SELECT statement returns only one row with one column, you can enclose that statement in parenthesis and use it as a plain value. In your case, let's say that select sum(......= TOI1.DATAAREAID returns 500. Then, your outer select's second column is equivalent to isnull(500,0)
You mention in your question "subquery Impuestos". Keep in mind that, although you indeed used a subquery as we mentioned earlier, by the time it was enclosed in parentheses it is not treated as a subquery (more accurately: derived table), but as a value. Thus, the "Impuestos" is only a column alias at this point
I dislike and avoid subqueries before the from, makes things much harder to read. Here is a solution with apply which will keep your code mostly intact:
Select
RC.PURCHID,
LRC.VALUEMST as 'Subtotal',
isnull(subquery1.a, 0) as Impuestos
from VEND RC
inner join VENDTABLE VTB on VTB.ACCOUNTNUM = RC.INVOICEACCOUNT
inner join TRANS LRC on (LRC.VEND = RC.RECID)
inner join PURCHLINE PL on (PL.LINENUMBER =LRC.LINENUM and PL.PURCHID =RC.PURCHID)
outer apply
(
select sum((CONVERT(float, TD1.taxvalue)/100)*LRC1.VALUEMST ) as a
FROM TAXONITEM TOI1
inner join TAXDATA TD1 ON (TD1.TAXCODE = TOI1.TAXCODE and RC.DATAAREAID = TD1.DATAAREAID)
inner join TRANS LRC1 on (LRC1.VEND = RC.RECID)
WHERE TOI1.TAXITEMGROUP = PL.TAXITEMGROUP and RC.DATAAREAID = TOI1.DATAAREAID
) as subquery1
where year (RC.DELIVERYDATE) =2021 and RC.PURCHASETYPE =3 order by RC.PURCHID;

ORA-00920: invalid relational operator in select clause with multiple joins and using max value

I trying to use a select statement to return rows when the following condition are met:
First condition:
The Employee's EMPLOYEE_HOME_ORG IS NOT EQUAL TO 396000 OR 396010 AND DEGREE_HIERARCHY is less than 40 AND FACULTY_DEGREE_CODE is not equal to 'MS' or 'MA'
Second Condition:
The employee's DEGREE_HIERARCHY is less than 40 and the employee's FACULTY_SUBJECT_CODE and FACULTY_DEGREE_CODE is not in the table SUBJ_CODE_CROSSWALK.
I get the following error:
ORA-00920: invalid relational operator
Please see the code below, any help is greatly appreciated!
SELECT V.FACULTY_DEGREE_CODE,
V.FACULTY_SUBJECT_CODE,
I.EMPLOYEE_DEPARTMENT_HOME_ORG,
MAX(D.DEGREE_HIERARCHY)
FROM V_DEGRESS V
JOIN DEGREE_CROSSWALK D
ON V.FACULTY_DEGREE_CODE =
D.DEGREE_CODE
JOIN I_REPORT_DETAILS I
ON V.PIDM = I.HR_PIDM
LEFT JOIN SUBJ_CODE_CROSSWALK S
ON V.FACULTY_DEGREE_CODE =
S.FACULTY_DEGREE_CODE
LEFT JOIN SUBJ_CODE_CROSSWALK S
ON S.FACULTY_SUBJECT_CODE =
V.FACULTY_SUBJECT_CODE
WHERE V.PERSON_SKEY = 12345
AND
(
(I.EMPLOYEE_DEPARTMENT_HOME_ORG != 396000
and I.EMPLOYEE_DEPARTMENT_HOME_ORG != 396010)
and (d.DEGREE_HIERARCHY < 40 )
and
(V.FACULTY_DEGREE_CODE != 'MS' and
V.FACULTY_DEGREE_CODE != 'MA')
)
--and NOT ONE SUBJ CODE CROSSWALK
AND
(
(D.DEGREE_HIERARCHY < 40)
AND
(V.FACULTY_SUBJECT_CODE,
V.FACULTY_DEGREE_CODE NOT IN
(SELECT S.FACULTY_SUBJECT_CODE,
S.FACULTY_DEGREE_CODE
FROM SUBJ_CODE_CROSSWALK
S
WHERE S.FACULTY_SUBJECT_CODE =
V.FACULTY_SUBJECT_CODE
AND V.FACULTY_DEGREE_CODE =
S.FACULTY_DEGREE_CODE
)
)
)
GROUP BY V.FACULTY_DEGREE_CODE,
V.FACULTY_SUBJECT_CODE,
I.EMPLOYEE_DEPARTMENT_HOME_ORG
Aside the fact that there's probably a typo since you are joining the table twice with the same alias, there's a syntax error in your query.
The second condition includes a tuple but is incorrectly typed. It should be in parenthesis as in (V.FACULTY_SUBJECT_CODE, V.FACULTY_DEGREE_CODE). Change:
AND
(V.FACULTY_SUBJECT_CODE,
V.FACULTY_DEGREE_CODE NOT IN
(SELECT S.FACULTY_SUBJECT_CODE,
S.FACULTY_DEGREE_CODE
FROM ...
For:
AND
(V.FACULTY_SUBJECT_CODE,
V.FACULTY_DEGREE_CODE) NOT IN
(SELECT S.FACULTY_SUBJECT_CODE,
S.FACULTY_DEGREE_CODE
FROM ...
Also, you'll need to remove one parenthesis at then end to ensure they are correctly balanced.
NOTE: You need to make sure the columns S.FACULTY_SUBJECT_CODE and S.FACULTY_DEGREE_CODE do not include nulls. If they do, this syntax won't produce the results you want. You'll need to rephrase this section as an "anti-join".

Run WHERE over an Average aggregation result - postgresql

I have the following query that gets the papers grade listing.
SELECT DISTINCT papers.paper_id, papers.paper_title, AVG(paper_judge_participations.paper_judge_participation_score) AS final_grade,
(SELECT array_agg(paper_author_name) FROM paper_authors as authors WHERE authors.paper_id=papers.paper_id )::varchar as paper_author_name
FROM papers
FULL JOIN paper_categories ON paper_categories.paper_category_id=papers.paper_category_id
LEFT JOIN paper_judge_participations ON papers.paper_id = paper_judge_participations.paper_id
WHERE ((papers.paper_note IS NULL AND final_grade >= 7) OR (papers.paper_note IS NOT NULL AND papers.paper_note >= 7)) AND papers.paper_category_id = 1
GROUP BY papers.paper_id ORDER BY final_grade, papers.paper_note;
I want to see if the final_grade variable from the the averae result is more than 7 but I get the error:
ERROR: column "final_grade" does not exist
LINE 6: WHERE ((papers.paper_note IS NULL AND final_grade >= 7) OR (...
How can I use the average result on my WHERE condition?
The distinct is unnecessary in the SELECT. Also you need to move the logic to the HAVING clause:
SELECT p.paper_id, p.paper_title,
AVG(pjp.paper_judge_participation_score) AS final_grade,
(SELECT string_agg(paper_author_name)
FROM paper_authors pa
WHERE pa.paper_id = p.paper_id
) as paper_author_name
FROM papers p LEFT JOIN
paper_categories pc
ON pc.paper_category_id = p.paper_category_id LEFT JOIN
paper_judge_participations pjp
ON p.paper_id = pjp.paper_id
HAVING p.paper_category_id = 1
GROUP BY p.paper_id
HAVING (p.paper_note IS NULL AND final_grade >= 7) OR
(p.paper_note IS NOT NULL AND p.paper_note >= 7) AND
ORDER BY final_grade, p.paper_note;
Comments:
The FULL OUTER JOIN is being turned into a LEFT OUTER JOIN by the WHERE clause. So, you might as well be explicit.
Instead of doing array_agg() and converting the results to a string, how about just using string_agg()?
Table aliases make the query easier to write and read.
And of course, the conditions on the final grade have been moved to the HAVING clause. The condition on the group stays in the WHERE.
Personally, I find it strange that you are using a correlated subquery for one aggregation and explicit aggregation for the other. I suppose that is a matter of preference. Under the circumstance, you might consider using a correlated subquery for both.