ORACLE Retrieving data from 4 tables - sql

I have the following tables:
Students(id, name, surname)
Courses(course id)
Course_Signup(id, student_id, course_id, year)
Grades(signup_id, mark)
I want to display all the students(id, name, surname) with their final grade (where final grade = avg of the grades of all courses), but only for the students that have passed all the courses for which they have sign-up in the current year.
This is what I tried:
SELECT s."id", s."name", s."surname", AVG(g."mark") AS "finalGrade"
FROM "STUDENT" s,
"course sign-up" csn
join "GRADES" g
on csn."id" = g."signup_id"
WHERE csn."year" >= '01-01-2022'
HAVING "finalGrade" >= 5.00
GROUP BY s."id"
However, after adding the last 2 lines, regarding the finalGrade condition, I get an invalid identifier error. Why is that?

Uh, oh. Did you really create tables using lower letter case names enclosed into double quotes? If so, get rid of them (the sooner, the better) because they only cause problems.
Apart from that, uniformly use joins - in your from clause there's the student table which isn't joined to any other table and results in cross join.
Don't compare dates to strings; use date literal (as I did), or to_date function with appropriate format model.
As of error you got: you can't reference expression's alias ("finalGrade") as is in the having clause - use the whole expression.
Also, group by should contain all non-aggregated columns from the select column list.
This "fixes" error you got, but - I suggest you consider everything I said:
SELECT s."id", s."name", s."surname", AVG(g."mark") AS "finalGrade"
FROM "STUDENT" s,
"course sign-up" csn
join "GRADES" g
on csn."id" = g."signup_id"
WHERE csn."year" >= date '2022-01-01'
GROUP BY s."id", s."name", s."surname"
HAVING AVG(g."mark") >= 5.00

Related

Oracle sql group function issues

Find Melbourne VIP level 4 customers’ first name, last name who have hired the vehicle model as “Ranger ” at least 2 times in database. You write three different queries: one is using operator EXISTS and the other one is using operator IN. The third query with the main filter criteria in FROM clause of the main query or filter criteria in the sub-query. Find one with the better performance.
I Have tried this query;
SELECT c_fname, c_fname FROM rental WHERE
EXISTS(SELECT c_id FROM customer WHERE c_city = 'Melbourne' AND customer.vip_level = '4')
AND EXISTS (SELECT vehicle_reg FROM vehicle WHERE v_model = 'Ranger')
HAVING COUNT(c_id)>=2 GROUP BY c_lname, c_fname;
I am getting error: SQL Error: ORA-00934: group function is not allowed here
00934. 00000 - "group function is not allowed here"
can anyone help me with this question. really struggled to get this done?
You are selecting from the wrong subject table as Rental does not have c_fname or c_lname columns.
You want to "Find Melbourne VIP level 4 customers’ first name, last name" which would be in the customer table:
SELECT c_fname,
c_lname
FROM customer
WHERE c_city = 'Melbourne'
AND vip_level = 4;
Then you want to add an additional filter "who have hired the vehicle model as “Ranger ” at least 2 times in database". That requires you to use EXISTS (for the answer for the first query) and you need to correlate between the outer-query and the sub-query; once you have done that then you do not need a GROUP BY clause and you are aggregating over the entire result set of the sub-query and can just use a HAVING clause.
SELECT c_fname,
c_lname
FROM customer c
WHERE c_city = 'Melbourne'
AND vip_level = 4
AND EXISTS(
SELECT 1
FROM rental r
INNER JOIN vehicle v
ON (r.vehicle_reg = v.vehicle_reg)
WHERE c.c_id = r.c_id
AND v.v_model = 'Ranger'
HAVING COUNT(*) >= 2
);
Then you need to write the same query using IN instead of EXISTS and the same query a third time using JOIN conditions instead of IN or EXISTS and, finally, you need to compare the performance of all three queries.

Need to find average and number of repetitions of column

I have an SQL sentence :
SELECT application.id,title,url,company.name AS company_name,package_name,ranking,date,platform,country.name AS country_name,collection.name AS collection_name,category.name AS category_name FROM application
JOIN application_history ON application_history.application_id = application.id
JOIN company ON application.company_id = company.id
JOIN country ON application_history.country_id = country.id
JOIN collection ON application_history.collection_id = collection.id
JOIN category ON application_history.category_id = category.id
WHERE application.platform=0
AND country.name ='CZ'
AND collection.name='topfreeapplications'
AND category.name='UTILITIES'
AND application_history.ranking <= 10
AND date::date BETWEEN date (CURRENT_DATE - INTERVAL '1 month') AND CURRENT_DATE
ORDER BY application_history.ranking ASC
It produces this result :
I'd like to add both a column average ranking for a given package, and a column number of appearances, which would count the number a package appears in the list. I'd also like to Group results by package_name, so that I don't have redundancies.
So far, I've tried to add a GROUP BY By clause before the ORDER BY :
GROUP BY package_name
But it returns me an error :
column "application.id" must appear in the GROUP BY clause or be used in an aggregate function
If I add each and every column it asks me for, it doesn't work.
I have also tried to count the number of package names, by adding after the SELECT :
COUNT(package_name) AS count
It produces a similar error.
How could I get the result I'm looking for ? Should I make two queries instead, or is it possible to get everything at once ?
I precise I have looked at other answers on S.O, but none of them tries to make the COUNT on a "produced" column.
Thank you for your help.
Edit :
Here is the result I expected at first :
Although Gordon's advice didn't give me the proper result it put me on the good track, when I read this :
From the docs : "Unlike regular aggregate functions, use of a window function does not cause rows to become grouped into a single output row."
So I came back to using COUNT and AVG alone. My problem was that I wanted to display the ranking column and date to check whether things were right. But putting these column into the Select prevented the GROUP BY to work as expected, as mentioned by Jarlh in the comments.
The working query :
SELECT application.id,title,url,company.name AS company_name,package_name,platform,country.name AS country_name,collection.name AS collection_name,category.name AS category_name,
COUNT(package_name) AS count, AVG(application_history.ranking) AS avg
FROM application
JOIN application_history ON application_history.application_id = application.id
JOIN company ON application.company_id = company.id
JOIN country ON application_history.country_id = country.id
JOIN collection ON application_history.collection_id = collection.id
JOIN category ON application_history.category_id = category.id
WHERE application.platform=0
AND country.name ='CZ'
AND collection.name='topfreeapplications'
AND category.name='UTILITIES'
AND application_history.ranking <= 10
AND date::date BETWEEN date (CURRENT_DATE - INTERVAL '1 month') AND CURRENT_DATE
GROUP BY package_name,application.id,company.name,country.name,collection.name,category.name
ORDER BY count DESC
I think you want window/analytic functions. The following adds two columns, one for the count of rows for each package and the other an average ranking for them:
SELECT application.id, title, url, company.name AS company_name, package_name,
ranking, date, platform, country.name AS country_name,
collection.name AS collection_name, category.name AS category_name,
count(*) over (partition by package_name) as count,
avg(ranking) over (partition by package_name) as avg_package_ranking
FROM application . . .

SQL Error - not a group by expression

With this SQL Query, I am attempting to list overdue book by patron. In addition, I want to group and order the books by patron. I understand you have to use some kind of aggregate function when doing a GROUP BY function. Even so, I am getting a "not a group by expression" error. Any help is appreciated.
Edit: updated code
What if I want to get the total fees per person and book? The bottom code is still resulting in the same error.
SELECT PATRON.LAST_NAME, PATRON.FIRST_NAME, PATRON.PHONE, BOOK.BOOK_TITLE, CHECKOUT.DUE_DATE, SUM((SYSDATE - CHECKOUT.DUE_DATE) * 1.00) AS FEE_BALANCE
FROM CHECKOUT
JOIN PATRON
ON CHECKOUT.PATRON_ID=PATRON.PATRON_ID
JOIN COPY
ON CHECKOUT.COPY_ID=COPY.COPY_ID
JOIN BOOK
ON COPY.BOOK_ID=BOOK.BOOK_ID
WHERE CHECKOUT.RETURN_DATE IS NULL
AND CHECKOUT.DUE_DATE > SYSDATE
GROUP BY PATRON.PATRON_ID
HAVING SUM((SYSDATE - CHECKOUT.DUE_DATE) > 0
ORDER BY PATRON.LAST_NAME, PATRON.FIRST_NAME;
You need to add these 3 columns to the GROUP BY clause...
PATRON.PHONE, BOOK.BOOK_TITLE, CHECKOUT.DUE_DATE
So it would be...
SELECT
PATRON.LAST_NAME,
PATRON.FIRST_NAME,
PATRON.PHONE,
BOOK.BOOK_TITLE,
CHECKOUT.DUE_DATE,
SUM((SYSDATE - CHECKOUT.DUE_DATE) * 1.00) AS BALANCE
FROM CHECKOUT
JOIN PATRON
ON CHECKOUT.PATRON_ID=PATRON.PATRON_ID
JOIN COPY
ON CHECKOUT.COPY_ID=COPY.COPY_ID
JOIN BOOK
ON COPY.BOOK_ID=BOOK.BOOK_ID
WHERE CHECKOUT.RETURN_DATE IS NULL
AND CHECKOUT.DUE_DATE > SYSDATE
GROUP BY PATRON.LAST_NAME, PATRON.FIRST_NAME, PATRON.PHONE, BOOK.BOOK_TITLE, CHECKOUT.DUE_DATE
ORDER BY PATRON.LAST_NAME, PATRON.FIRST_NAME
Bottom line is, if you have a GROUP BY clause, you can only select columns that are part of the GROUP BY unless you are using some kind of scalar value function on them, like COUNT, SUM, MAX, MIN, etc.
Otherwise if you aren't grouping or using a scalar-value function, multiple rows could have different values for that column, so what would the query results be?

string aggregation and joining with another table column

I have following tables:
weighment_tran
village_cd
farmer_id
registered_farmer_id
plot_no
out_date
net_wt
village_dir
village_cd
village_name
taluka_cd
district_cd
taluka_dir
taluka_cd
taluka_name
district_dir
district_cd
district_name
farmer_dir
farmer_id
first_name
middle_name
agreement_tran
farmer_id
registered_farmer_id
payment_farmer_id
plot_no
main_sy_no
payment_bank_cd
payment_account_no
bank_dir
bank_cd
bank_name
bank_branch
Mainly I have two transaction tables namely agreement_tran (agreements are stored) and weighment_tran (weighment of products stored only those present in agreement_tran) and others are directories those holds lookup for actual names for codes like bank_cd looks for actual bank_name in bank_dir. farmer_id, registered_farmer_id, payment_farmer_id are of same column values. What I need is as below with out_date range:
Sl.No farmer_name Sy_nos village taluka district payment_farmer_id payment_account_no bank_name bank_branch sum(net_wt)
Every plot_no has a sy_no, I need to concatenate all sy_nos as I am selecting sum(net_wt) as net_wt concerned to every plot_no.
I tried something like-
select row_number() over (order by a.payment_farmer_id),
a.payment_farmer_id,
(select f.first_name ||' '|| f.middle_name as name
from farmer_dir f
where a.payment_farmer_id=f.farmer_id),
(select wm_concat(main_sy_no) from agreement_tran a
where a.plot_no=w.plot_no),
(select sum(net_wt)
from weighment_tran w
where (w.plot_no = a.plot_no)
and (w.season_cd = 9) and trunc(w.out_date) between
to_date('22-12-2013','dd-mm-yyyy') and to_date('23-12-2013','dd-mm-yyyy')
group by a.payment_farmer_id)
from agreement_tran a
but didn't get what I want.
There seem to be some mistakes in your query:
First subquery looks okay.
Second subquery is wrong. You select agreement_tran a second time but use the same alias. Moreover you compare with w.plot_no, but there is no w table. (I suppose you meant to name the inner agreement_tran w?)
In your second subquery the group by clause makes no sense. Remove it.
Is it on purpose that you don't sort your results? Above you number your lines, but at the end of the query you have no order by, which may result in a random order.
Your query shows all agreements, their farmer's name, all sy nos of all agreements with the same plot number, and the sum of all net weights with the same plot number in a certain time slot. Please check if this is meets your expectations. Do you really want to select all Agreements or rather all plots for instance?
SELECT Sum(WEIGHMENT_TRAN.NET_WT),
AGREEMENT_TRAN.PAYMENT_FARMER_ID,
wm_concat(Distinct (agreement_tran.main_sy_no)) as Sy_no,
wm_concat(Distinct (village_dir.village_name)) as Village,
farmer_dir.first_name || ' ' || farmer_dir.middle_name as Farmer_name, taluka_dir.taluka_name, district_dir.district_name, bank_dir.bank_name, bank_dir.bank_branch, agreement_tran.payment_account_no, weighment_tran.registered_farmer_id
FROM AGRI.AGREEMENT_TRAN AGREEMENT_TRAN,
AGRI.village_dir village_dir,
AGRI.WEIGHMENT_TRAN WEIGHMENT_TRAN,
agri.farmer_dir farmer_dir, agri.taluka_dir taluka_dir,
agri.district_dir district_dir, agri.bank_dir bank_dir
WHERE AGREEMENT_TRAN.PLOT_NO = WEIGHMENT_TRAN.PLOT_NO AND
((AGREEMENT_TRAN.SEASON_CD=9) AND (WEIGHMENT_TRAN.SEASON_CD=9) AND
(trunc(weighment_tran.out_date) Between to_date('22-12-2013','dd-mm-yyyy')
And to_date('22-12-2013','dd-mm-yyyy')) AND
(AGREEMENT_TRAN.PLOT_NO=weighment_tran.plot_no) AND (WEIGHMENT_TRAN.VILLAGE_CD=village_dir.village_cd)) and agreement_tran.payment_farmer_id=farmer_dir.farmer_id and village_dir.taluka_cd=taluka_dir.taluka_cd and village_dir.district_cd=district_dir.district_cd and agreement_tran.payment_bank_cd=bank_dir.bank_cd
GROUP BY AGREEMENT_TRAN.PAYMENT_FARMER_ID, farmer_dir.first_name, farmer_dir.middle_name, taluka_dir.taluka_name, district_dir.district_name,bank_dir.bank_name, bank_dir.bank_branch, agreement_tran.payment_account_no, weighment_tran.registered_farmer_id
Order by bank_dir.bank_name, bank_dir.bank_branch
finally I rewarded for my long effort and thanking #Thorsten for his time n patience.

How to view the Suggsetions sorted in the Database for the last three months?

I am a New ASP.NET Developer and I am trying to develop a simple suggestion box system. I have the following part of my database desing:
User Table: Username, Name, DivisionCode... etc
Division Table: SapCode, Division
SuggestionLog Table: ID, Title, Description, submittedDate, Username
(The first attribute is the primary key in each table and the attribute (submittedDate) is of DateTime data type)
Now, I need to develop a table that shows suggestions for the last three months. I already developed a query that shows the Employee Name, Username, Division, Suggestion Title, Suggestion Description. All what I want now is to show the Month. For example, to show the suggestions for the last three months, the Month column should show: Jan-2012, Dec-2011, Nov-2011 So how to do that?
My current SQL query:
SELECT dbo.SafetySuggestionsLog.Title, dbo.SafetySuggestionsLog.Description, dbo.SafetySuggestionsType.Type, dbo.SafetySuggestionsLog.Username,
dbo.employee.Name, dbo.Divisions.DivisionShortcut
FROM dbo.Divisions INNER JOIN
dbo.employee ON dbo.Divisions.SapCode = dbo.employee.DivisionCode INNER JOIN
dbo.SafetySuggestionsLog ON dbo.employee.Username = dbo.SafetySuggestionsLog.Username INNER JOIN
dbo.SafetySuggestionsType ON dbo.SafetySuggestionsLog.TypeID = dbo.SafetySuggestionsType.ID
The desired output is to display:
Employee Name, Username, Division, SuggestionTitle, SuggstionDescription, SuggestionType Month(submissionDate)
I reformatted you query so it would fit on the page without scrolling.
Hopefully this provides what you need. It uses DATENAME to get the month and year parts from the current date and DATEPART to do the "three months ago" calculation.
Note that DATEPART doesn't behave as you might expect - it counts the number of period-end boundaries (in this case months) - hence the condition is
...WHERE DATEDIFF(month,SafetySuggestionsLog.submittedDate,getdate()) < 3
because the last three months have two month-end boundaries between them.
I also added an ORDER BY clause.
SELECT dbo.SafetySuggestionsLog.Title,
dbo.SafetySuggestionsLog.Description,
dbo.SafetySuggestionsType.Type,
dbo.SafetySuggestionsLog.Username,
dbo.employee.Name,
dbo.Divisions.DivisionShortcut,
left(datename(month,SafetySuggestionsLog.submittedDate),3)
+ '-'
+ datename(year,SafetySuggestionsLog.submittedDate) AS SubmittedMonth
FROM dbo.Divisions
INNER JOIN dbo.employee
ON dbo.Divisions.SapCode = dbo.employee.DivisionCode
INNER JOIN dbo.SafetySuggestionsLog
ON dbo.employee.Username = dbo.SafetySuggestionsLog.Username
INNER JOIN dbo.SafetySuggestionsType
ON dbo.SafetySuggestionsLog.TypeID = dbo.SafetySuggestionsType.ID
WHERE DATEDIFF(month,SafetySuggestionsLog.submittedDate,getdate()) < 3
ORDER BY SafetySuggestionsLog.submittedDate DESC
It might also be worth noting that you don't have to fully qualify the name of all the columns in the query - it's valid SQL to alias the input tables like so:
...INNER JOIN dbo.SafetySuggestionsLog AS log
You can then refer to column names by alias in the query - e.g.
log.Username
instead of
dbo.SafetySuggestionsLog.Username
which makes it a bit easier to read.