This question already has answers here:
Fetch the rows which have the Max value for a column for each distinct value of another column
(35 answers)
Oracle SQL query: Retrieve latest values per group based on time [duplicate]
(2 answers)
Get value based on max of a different column grouped by another column [duplicate]
(1 answer)
SQL: getting the max value of one column and the corresponding other columns [duplicate]
(2 answers)
Closed 3 years ago.
I am troubled with writing a tricky query.
I have the following table:
For each department I want to print date with largest profit;
I tried coming up with such a query myself:
Select DISTINCT(Name), Date_sale, MAX(A) as B FROM (SELECT
Departments.Name, SALES.Date_sale, SUM(GOODS.Price * SALES.Quantity)
AS A FROM DEPARTMENTS, GOODS, SALES
WHERE DEPARTMENTS.Dept_id = GOODS.Dept_id AND GOODS.Good_id =
SALES.Good_id GROUP BY DEPARTMENTs.Name, SALES.Date_sale)
GROUP BY Name, Date_sale;
But the problem it that departments are printed several times because I groupped by both name and date.
How should I fix it?
You can try below way-
with cte as
(
SELECT
Departments.Name, SALES.Date_sale, SUM(GOODS.Price * SALES.Quantity)
AS profit FROM DEPARTMENTS inner join GOODS on DEPARTMENTS.Dept_id = GOODS.Dept_id
inner join SALES on GOODS.Good_id = SALES.Good_id
GROUP BY DEPARTMENTs.Name, SALES.Date_sale
)A
select * from cte a
where profit =
(select max(profit) from cte b on a.department=b.department)
OR you can use row_number()
select * from
(
select *, row_number() over(partition by department oder by profit desc) as rn
from cte
)A where rn=1
You can write it using ROW_NUMBER which will give a number to each date's total count grouped by the department as following and then you can take the highest sale date using rn = 1
SELECT NAME, DATE_SALE, A
FROM
(
SELECT
DEPARTMENTS.NAME, SALES.DATE_SALE,
ROW_NUMBER() OVER(
PARTITION BY DEPARTMENTS.NAME
ORDER BY SUM(GOODS.PRICE * SALES.QUANTITY) DESC NULLS LAST
) AS RN,
SUM(GOODS.PRICE * SALES.QUANTITY) AS A
FROM DEPARTMENTS
JOIN GOODS ON ( DEPARTMENTS.DEPT_ID = GOODS.DEPT_ID )
JOIN SALES ON ( GOODS.GOOD_ID = SALES.GOOD_ID )
GROUP BY DEPARTMENTS.NAME,
SALES.DATE_SALE
)
WHERE RN = 1;
Important, Use the standard ANSI-joins.
Cheers!!
i would use join-s here as it is needed to pull info from 2 tables linked via the third table.
Something like this (but I have not tested this query, just suggesting an approach):
Select department.name as dept, MAX(sales.quantity) as max_sales, sales.date_sale
from goods
Left outer join departments on departments.dept_id = goods.dept_id
Left outer join sales on sales.good_id = goods.good_id
Group by dept
Related
This question already has answers here:
Select top and bottom rows
(9 answers)
Closed 7 months ago.
Question: The information of the youngest and oldest employee at the same time.
I can find information of the youngest or oldest employee, but I can't find information of the youngest and oldest employee at the same time.
My sql query:
-- The information of the oldest employee
Select top 1*
From DIP_Employees1
where DogumTarihi=
(Select Min(DogumTarihi)
From DIP_Employees1
)
-- The information of the youngest employee
Select top 1*
From DIP_Employees1
where DogumTarihi=
(Select Max(DogumTarihi)
From DIP_Employees1
)
How can I combine this two code for at the same time?
You can combine 2 queries that return same columns using UNION statement.
Also it is unnecessary to use subqueries. Instead you can just use order by
SELECT *
FROM (
Select top 1 *
From DIP_Employees1
ORDER BY DogumTarihi ASC
) t1
UNION ALL
SELECT *
FROM (
Select top 1 *
From DIP_Employees1
ORDER BY DogumTarihi DESC
) t2
Another option:
with cte as (
select min(DogumTarihi) as mndg, max(DogumTarihi) as mxdg
from dbo.DIP_Employees1
)
select ...
from dbo.DIP_Employees1 as emp
inner join cte on emp.DogumTarihi = cte.mndg
or emp.DogumTarihi = cte.maxdg
order by ...
;
Notice that some suggestions assume birthdate is unique. However unlikely it might be that multiple persons will have the same birthdate, why assume at all?
This question already has answers here:
Fetch the rows which have the Max value for a column for each distinct value of another column
(35 answers)
Select First Row of Every Group in sql [duplicate]
(2 answers)
Return row with the max value of one column per group [duplicate]
(3 answers)
Get value based on max of a different column grouped by another column [duplicate]
(1 answer)
Closed 3 years ago.
The thing is that i could have values like
ID STREET_ID HOUSENUMBER POSTCODE
10000000 20512120 22 04114
11000000 20512120 22 04074
problem is that POSTCODE have to be in select, but i need distinct STREET_ID + HOUSENUMBER with MAX id,
by example i just want to show 11000000,20512120,22,04074 out of 2 records because of MAX(h.ID).
this is my code
SELECT DISTINCT
MAX(h.ID),
h.street_id,
h.houseNumber,
h.postindex AS postCode
FROM house h
WHERE
h.postindex IS NOT NULL AND
h.STREET_ID IS NOT NULL
GROUP BY
h.street_id,
h.houseNumber
ORDER BY
STREET_ID,
CAST(REGEXP_REPLACE(REGEXP_REPLACE(h.houseNumber, '(\-|\/)(.*)'), '\D+') AS NUMBER),
h.houseNumber
i have an error " ORA-00979: not a GROUP BY expression " and i understand it, because POSTCODE is not in GROUP BY, how to deal with that?
Your requirement is a good candidate for ROW_NUMBER:
WITH cte AS (
SELECT h.*,
ROW_NUMBER() OVER (PARTITION BY h.STREET_ID, h.HOUSENUMBER ORDER BY h.ID DESC) rn
FROM house h
)
SELECT ID, STREET_ID, HOUSENUMBER, POSTCODE
FROM cte
WHERE rn = 1;
An index on (STREET_ID, HOUSENUMBER, ID) might speed up the above query, because it would let Oracle quickly find the max ID record for each street/house number.
You can do it with subquery and exists:
SELECT *
FROM house h
WHERE NOT EXISTS (SELECT 1 FROM house h2
WHERE h2.street_id = h.street_id
AND h2.houseNumber = h.houseNumber
AND h2.id > h.id)
Don't aggregate. Instead, you can filter with a correlated subquery:
select h.*
from house h
where id = (
select max(h1.id)
from house h1
where h1.street_number = h.street_number and h1.house_number = h.house_number
)
I would expect this solution to be as efficient as it gets, especially with an index on (street_number, house_number, id).
select distinct id,street_id,house_no,postal_code from house where id in (
SELECT MAX(id) from house group by street_id,house_no)
This question already has answers here:
Oracle SQL - How to Retrieve highest 5 values of a column [duplicate]
(5 answers)
Oracle SELECT TOP 10 records [duplicate]
(6 answers)
Closed 6 years ago.
i have 2 tables .
abc(CID(pk), cname,)
order(order_id(pk), CID(fk), number_of_rentals)
i want to fetch top 10 customers based on number of rentals.
SELECT cid, sum(no_rentals) as sum
FROM orders
group by cid, no_rentals
order by no_rentals desc;
how can i use rownum function in above query to fetch the desired output
Just wrap your query in:
SELECT * FROM ( your_query ) WHERE ROWNUM <= 10;
However, your query does not look like it is going to do what you intend as the GROUP BY no_renalts will mean that each distinct no_rentals value will be in its own group and you will not sum the values for each customer so you probably don't want to include it in the GROUP BY. Also, if you want to order by the total number of rentals then you want to ORDER BY SUM( no_rentals ) (or by its alias) like this:
SELECT cid,
SUM(no_rentals) as total_no_rentals
FROM orders
GROUP BY cid
ORDER BY total_no_rentals DESC;
Then you can apply the row limit like this:
SELECT *
FROM (
SELECT cid,
SUM(no_rentals) as total_no_rentals
FROM orders
GROUP BY cid
ORDER BY total_no_rentals DESC
)
WHERE ROWNUM <= 10;
This question already has answers here:
Top n percent top n%
(4 answers)
Closed 7 years ago.
I've created a database where workers have a charging rate and work a particular number of hours. From this I've done a select statement which simply displays some information about the worker, and then I've created a new column called Earnings which uses rate_per_hour * task_hours to give their total earnings.
My question is, is there a way to only display the top 20% highest earners based on the new Earnings column created in the select statement?
So far I have this:
SELECT worker.worker_id
,worker_first_name
,worker_surname
,worker_case_id
,task_hours
,rate.rate_id
,rate_per_hour
,task_hours * rate_per_hour AS Earnings
FROM worker
,note
,rate
WHERE worker.worker_id = note.worker_id
AND rate.rate_id = note.rate_id;
I just need display the top 20% of earnings based on that new column I've made. Is this possible?
Thanks, apologies for my lack of experience!
First, you should use explicit join syntax. Second, you can do what you want using percentile_cont() or percentile_disc(). However, I often do this using row_number() and count():
SELECT wnr.*
FROM (SELECT w.worker_id, worker_first_name, worker_surname, worker_case_id,
task_hours, r.rate_id, rate_per_hour,
task_hours * rate_per_hour AS Earnings,
row_number() over (order by task_hours * rate_per_hour desc) as seqnum,
count(*) as cnt
FROM worker w JOIN
note n
ON w.worker_id = n.worker_id JOIN
rate r
ON r.rate_id = n.rate_id
) wnr
WHERE seqnum <= cnt * 0.2;
You also might use rank analytical function instead of row_number in case you want equal rank for equal earnings.
Select * from (
Select employee,earnings,rank() over (order by earnings desc )/count(*) over() As top from employees)
Where top<=0.2;
This question already has answers here:
ORA-00979 not a group by expression
(10 answers)
Closed 5 years ago.
When i wanted to add em_fname and emp_lname into my select statement. I kept getting NOT A GROUP BY EXPRESSION error.
The thing is when I add those 2 into GROUP BY clause, I got the unwanted query results (quite redundant).
Any suggestion on this?
select lgemployee.dept_num, emp_fname,emp_lname, max(sal_amount) as
HighestSalary
from lgsalary_history inner join lgemployee on lgsalary_history.EMP_NUM = lgemployee.EMP_NUM
group by lgemployee.dept_num;
add the none aggregated columns to group by condition, also it may required a left outer or right outer join (depends on your data)
select lgemployee.dept_num, emp_fname,emp_lname, max(sal_amount) as
HighestSalary
from lgsalary_history inner join lgemployee on lgsalary_history.EMP_NUM = lgemployee.EMP_NUM
group by lgemployee.dept_num,emp_fname,emp_lname;
But I wanted to display the highest sal_amount from employee in each dept. if i remove emp_fname and emp_lname, I got 8 rows (from 8 departments), but if I add them as you suggested, I got 363 results (not what I wanted)!
It's hard to tell what your schema is like. Is sal_amount in lgsalary_history or lgemployee (I'm guessing the latter, but it's not clear as you don't use table aliases).
With this in mind, you probably want something like the following:
SELECT dept_num, emp_fname, emp_lname FROM (
SELECT lge.dept_num, lge.emp_fname, lge.emp_lname
, lgs.sal_amount
, RANK() OVER ( PARTITION BY lge.dept_num ORDER BY lgs.sal_amount DESC) rn
FROM lgemployee lge INNER JOIN lgsalary_history lgs
ON lge.emp_num = lgs.emp_num
) WHERE rn = 1;
Now there is an added difficulty here, namely that if lgsalary_history is what I think it is, it will store past salaries in addition to current salaries. And it's just possible, even if unlikely, that a particular employee's salary could be cut. And the above query doesn't take that into account. So let's assume your salary history is dated, and there is a column dt in lgsalary_history that will allow is to determine the most recent salary for a given employee.
SELECT dept_num, emp_fname, emp_lname, sal_amount FROM (
SELECT dept_num, emp_fname, emp_lname, sal_amount
, RANK() OVER ( PARTITION BY dept_num ORDER BY sal_amount DESC ) AS rn
FROM (
SELECT e.dept_num, e.emp_fname, e.emp_lname, s.sal_amount, ROW_NUMBER() OVER ( PARTITION BY e.emp_num ORDER BY s.dt DESC ) AS rn
FROM lgemployee e INNER JOIN lgsalary_history s
ON e.emp_num = s.emp_num
) WHERE rn = 1
) WHERE rn = 1;
This will return all of the employees in a given department with the highest salary (so if there is more than one, it will return two, or three, etc.).