SQL query scenario. - sql

Suppose I have the following table:
My goal is to display a select resultset that looks like this:
The tricky part here is to display the AverageCostPerType column for every single book.
I know how to get the AverageCostPerType, it's simply the following:
SELECT avg(bookcost) as AverageCostPerType FROM BOOK GROUPBY BookType;
This will display 3 rows since I have 3 distinct types. How can I display an averagecostpertype for each book ?
I'll appreciate your help.

you need use analytic functions
AVG per BookType
select b.*, avg(bookcost) over (PARTITION BY BookType)
from book b
AVG for all books
select b.*, avg(bookcost) over ()
from book b

You can use aggregate functions with an analytic partition window to get average cost by booktype.
This allows you to perform the query without joining the table to itself, or using nested select statements.
Oracle Aggregate Functions
select Book_num,
BookType,
avg(BookCost) over(partition by BookType) as AverageCostPerType,
BookCost,
BookCost - avg(BookCost) over(partition by BookType) as Difference
from YourBookTable

You can calculate the average per booktype in a derived table and join it to the original table to get the result.
select book_num, t.booktype, x.avgcost, bookcost, x.avgcost-bookcost
from tablename t join
(select booktype, avg(bookcost) as avgcost from tablename group by booktype) x
on t.booktype = x.booktype

select b.*,
b2.AverageCostPerType,
b2.AverageCostPerType - b.BookCost as difference
from book b
join
(
SELECT BookType, avg(bookcost) as AverageCostPerType
FROM BOOK
GROUP BY BookType
) b2 on b.BookType = b2.BookType

Related

How to count the rows of a column grouping by a column but omitting the others columns of the table in Oracle?

I want to do a count grouping by the first column but omitting the others columns in the group by. Let me explain:
I have a table with those columns
So, what I want to get is a new column with the work orders total by Instrument, something like this:
How can I do that? Because if I do a count like this:
SELECT INSTRUMENT, WORKORDER, DATE, COUNT(*)
FROM TABLE1
GROUP BY INSTRUMENT, WORKORDER, DATE;
I get this:
Just use a window function:
select t.*,
count(*) over (partition by instrument) as instrument_count
from table1 t;
Although answer given by Gordon is perfect but there is also another option by using group by and subquery. You can add date column to this query as well
SELECT * FROM
(
SELECT A.INSTRUMENT, B.TOTAL_COUNT_BY_INSTRUMENT
FROM work_order A,
(SELECT COUNT(1) AS TOTAL_COUNT_BY_INSTRUMENT,
INSTRUMENT
FROM WORK_ORDER
GROUP BY INSTRUMENT
) B
WHERE A.INSTRUMENT = B.instrument);

SQL MAX Query Multiple Columns

Trying to populate multiple columns based on one MAX value but the grouping returns multiple results. Is there a way I can tell SQL to only pull the values based on the MAX that I want?
Query:
Select a.ID, (MAX)a.PayDate, a.AgencyName
From a
Group By a.ID, a.AgencyName
What I need is the latest paydate per ID, then I want additional information in reference to that entry such as AgencyName (& more columns I want to add) but because of the grouping - SQL returns the latest paydate for each of the AgencyNames that the person has had - but I only want the AgencyName associated with the record that is Max Paydate for that ID. I know it's the grouping that does this but I am unsure how to proceed - any help greatly appreciated.
Thanks
Select a.ID,a.PayDate, a.AgencyName
From a
where exists (select 1 from a a1 where a1.id = a.id
having a.payDate = max(a1.paydate)
Group By a.ID,
I would just use a correlated subquery like this:
select a.*
from a
where a.paydate = (select max(a2.paydate) from a a2 where a2.id = a.id);
Note that this could return multiple rows if an id has duplicates on the most recent paydate. An alternative that guarantees one row is row_number():
select a.*
from (select a.*,
row_number() over (partition by id order by paydate desc) as seqnum
from a
) a
where seqnum = 1;

How to group by all but except one column

i want to group my results except one column:
Select Name,Title,Date from Books group by Name,Title
i don't want to group by date because books might have same date
you can use
Select Name,Title,Date from Books group by Name,Title,Date;
or
Select distinct Name,Title,Date from Books;
for the same purpose. It wont make any difference as the dates are same.
if dates are not same then you can use any aggregate function to achieve the same. like:
Select Name,Title,max(Date)--any aggregate function based on logic
from Books group by Name,Title,Date;
Quick and dirty without knowing what type of ordering or specific date values you want to include or exclude:
Select b.Name, b.Title, b.Date From Books b
Join (Select Name, Title From Books Group By Name, Title) sub
On sub.Name = b.Name And sub.Title = b.Title
These one is working as expected
SELECT [Name]
,[Title]
,max(Date)
FROM [Books].[dbo].[myBooks] group by [Name],[Title]

Adding count in select query

I am trying to find a query that would give me a count of another table in the query. The problem is that I have no idea what to set where in the count part to. As it is now it will just give back a count of all the values in that table.
Select
ID as Num,
(select Count(*) from TASK where ID=ID(Also tried Num)) as Total
from ORDER
The goal is to have a result that reads like
Num Total
_________________
1 13
2 5
3 22
You need table aliases. So I think you want:
Select ID as Num,
(select Count(*) from TASK t where t.ID = o.ID) as Total
from ORDER o;
By the way, ORDER is a terrible name for a table because it is a reserved work in SQL.
You can do it as a sub query or a join (or an OVER statement.)
I think the join is clearest when you are first learning SQL
Select
ID as Num, count(TASK.ID) AS Total
from ORDER
left join TASK ON ORDER.ID=TASK.ID
GROUP BY ORDER.ID

SQL command to get field of a maximum value, without making two select

I'm starting to learn SQL and I'm working on this exercise: I have a "books" table which holds the info on every book (including price and genre ID).
I need to get the name of the genre which has the highest average price. I suppose that I first need to group the prices by genre and then retrieve the name of the highest..
I know that I can get the results GENRE VS COST with the following:
select b.genre,
round(avg(b.price),2) as cost
from books b
group by b.genre;
My question is, to get the genre with the highest AVG price from that result, do I have to make:
select aux.genre
from (
select b.genre,
round(avg(b.price),2) as cost
from books b
group by b.genre
) aux
where aux.cost = (select max(aux.cost)
from (
select b.genre,
round(avg(b.price),2) as cost
from books l
group by b.genre
) aux);
Is it bad practice or isn't there another way? I get the correct result but I'm not confortable with creating two times the same selection.
I'm not using PL SQL so I can't use variables or anything like that..
Any help will be appreciated.
Thanks in advance!
On Sql server, you can use the avg aggragate inside the windowing function row_number
with m as(
select genre,
avg(price) cost,
row_number() over(order by avg(price) desc) rw
from books
group by genre
)
select * from m
where rw=1
with avg_price as (
select b.genre,
round(avg(b.price),2) as cost
from books b
group by b.genre
)
select genre
from avg_price
where cost = (select max(cost) from avg_price);
(This is ANSI standard SQL and works in all modern DBMS)
In MS-SQL you can do this:
SELECT TOP 1 genre, ROUND(AVG(price),2) AS cost
FROM books
GROUP BY genre
ORDER BY ROUND(AVG(price),2) DESC
(the TOP clause is MS-SQL specific, but there are similiar constructs in other DBMS)
select top 1
genre,
avg(b.price) as cost
from books
group by
genre
order by
cost desc