Find the row with the highest value in SQL - sql

According to the schema, the Orders table has the following columns:
OrderId integer, CustomerId integer, RetailerId integer, ProductId integer, Count integer
I'm trying to figure out what product has the highest number of orders for each retailer.
So, to sum up all the orders per product, I have the following query:
SELECT RetailerId, ProductId, SUM(Count) AS ProductTotal
FROM Orders
GROUP BY RetailerId, ProductId
ORDER BY RetailerId, ProductTotal DESC;
This gives an output like this:
RETAILERID PRODUCTID PRODUCTTOTAL
---------- ---------- ------------
1 5 115
1 10 45
1 1 15
1 4 2
1 8 1
2 9 12
2 11 10
2 7 1
3 3 3
4 2 1
5 11 1
Now, all I want to do is find the product with the highest number of orders per retailer. Right now, it shows all the products; I want only one.
I have tried way too many things. Following is one particularly abhorrent example:
SELECT O.RetailerId, O.ProductId, O.ProductTotal
FROM (
SELECT Orders.ProductId, MAX(ProductTotal) AS MaxProductTotal
FROM (
SELECT Orders.ProductId AS PID, SUM(Orders.Count) AS ProductTotal
FROM Orders
GROUP BY Orders.ProductId
) AS O INNER JOIN Orders ON Orders.ProductId = PID
GROUP BY Orders.ProductId
) AS X INNER JOIN O ON O.RetailerId = X.RetailerId AND O.ProductTotal = X.MaxProductTotal;
The solution to this is probably the simplest thing ever, but I just can't right now. So, I'd like some help.

Select the maximum total per customer with a window function:
SELECT RetailerId, ProductId, ProductTotal
FROM
(
SELECT
RetailerId, ProductId, SUM(Count) AS ProductTotal,
MAX(SUM(Count)) OVER (PARTITION BY RetailerId) AS MaxProductTotal
FROM Orders
GROUP BY RetailerId, ProductId
)
WHERE ProductTotal = MaxProductTotal
ORDER BY RetailerId;

You can try using window function row_number()
select * from
(
select *,row_number() over(partition by RetailerId order by ProductTotal desc) as rn from
(
SELECT RetailerId, ProductId, SUM(Count) AS ProductTotal
FROM Orders
GROUP BY RetailerId, ProductId
)A
)X where rn=1

you can use row_number() with cte
with cte as
(
SELECT o.RetailerId,o.ProductId AS PID, SUM(o.Count) AS ProductTotal
FROM Orders o
GROUP BY o.ProductId,o.RetailerId
), cte2 as
(
select RetailerId,PID,ProductTotal,
row_number() over(partition by RetailerId order by ProductTotal desc) as rn
from cte
) select RetailerId,PID,ProductTotal from cte2 where rn=1

Have you tried using rank?
Try this query
select OrderC, retailer_id,product_id from ( select sum(count_order) as OrderC,retailer_id,product_id,row_number() over(order by OrderC desc) as RN from order_t group by retailer_id,product_id) as d where RN=1;

Here is a version without sub-query using FIRST/LAST:
WITH t(RETAILERID, PRODUCTID, PRODUCTTOTAL) AS (
SELECT 1, 5, 115 FROM dual UNION ALL
SELECT 1, 10, 45 FROM dual UNION ALL
SELECT 1, 1, 15 FROM dual UNION ALL
SELECT 1, 4, 2 FROM dual UNION ALL
SELECT 1, 8, 1 FROM dual UNION ALL
SELECT 2, 9, 12 FROM dual UNION ALL
SELECT 2, 11, 10 FROM dual UNION ALL
SELECT 2, 7, 1 FROM dual UNION ALL
SELECT 3, 3, 3 FROM dual UNION ALL
SELECT 4, 2, 1 FROM dual UNION ALL
SELECT 5,11, 1 FROM dual)
SELECT
RETAILERID,
MAX(PRODUCTID) KEEP (DENSE_RANK LAST ORDER BY PRODUCTTOTAL) AS PRODUCTID,
MAX(PRODUCTTOTAL)
FROM t
GROUP BY RETAILERID;
+--------------------------------------+
|RETAILERID|PRODUCTID|MAX(PRODUCTTOTAL)|
+--------------------------------------+
|1 |5 |115 |
|2 |9 |12 |
|3 |3 |3 |
|4 |2 |1 |
|5 |11 |1 |
+--------------------------------------+

Related

Subqueries with Group By

I have looked around on the internet for a while for a way to make this query work but am unable to work it out so far. I am trying to return the item descriptions and the quantity of items sold, This is what I have got at the moment:
SELECT itemdesc, quantity, (SELECT COUNT(quantity) FROM invoiceitem
WHERE invoiceitem.itemno = item.itemno
GROUP BY COUNT(invoiceitem.quantity)) Quantity
FROM item;
I am very lost at the moment, not sure if I am even linking the right tables together, can provide an ER Diagram if it helps, any help would be greatly appreciated, thankyou.
ANSWER:
SELECT item.itemdesc, (SELECT SUM(invoiceitem.quantity) FROM invoiceitem
WHERE invoiceitem.itemno = item.itemno
GROUP BY item.itemdesc) Quantity
FROM item
ORDER BY quantity;
Thankyou all!
Your outer query:
SELECT itemdesc,
quantity
/* ignoring the subquery */
FROM item;
Will not work as the item table does not have a quantity column.
If you intended to use the itemprice column then your query would be:
SELECT itemdesc,
itemprice,
( SELECT COUNT(quantity)
FROM invoiceitem
WHERE invoiceitem.itemno = item.itemno
) AS Quantity
FROM item
Which, for the sample data:
CREATE TABLE item (
itemno PRIMARY KEY,
itemdesc,
itemprice
) AS
SELECT 1, 'ItemA', 1 FROM DUAL UNION ALL
SELECT 2, 'ItemB', 2 FROM DUAL UNION ALL
SELECT 3, 'ItemC', 3 FROM DUAL UNION ALL
SELECT 4, 'ItemD', 4 FROM DUAL UNION ALL
SELECT 5, 'ItemE', 5 FROM DUAL;
CREATE TABLE invoiceitem( itemno, quantity ) AS
SELECT 1, 10 FROM DUAL UNION ALL
SELECT 1, 20 FROM DUAL UNION ALL
SELECT 1, 30 FROM DUAL UNION ALL
SELECT 3, 40 FROM DUAL UNION ALL
SELECT 4, 50 FROM DUAL UNION ALL
SELECT 5, NULL FROM DUAL;
Outputs:
ITEMDESC | ITEMPRICE | QUANTITY
:------- | --------: | -------:
ItemA | 1 | 3
ItemB | 2 | 0
ItemC | 3 | 1
ItemD | 4 | 1
ItemE | 5 | 0
And equivalent query using a join would be (assuming that item.itemno is a primary key):
SELECT MAX( i.itemdesc ) AS itemdesc,
MAX( i.itemprice ) AS itemprice,
COUNT(ii.quantity) AS Quantity
FROM item i
LEFT OUTER JOIN invoiceitem ii
ON ( ii.itemno = i.itemno )
GROUP BY i.itemno
You need to use LEFT OUTER JOIN rather than INNER JOIN to join the corresponding rows with zero items in the invoiceitem table.
db<>fiddle here
Group by like below should work. Please check.
select
item.itemdesc, count(invoiceitem.quantity) Quantity
from
item item
join invoiceitem invoiceitem on item.itemno = invoiceitem.itemno
group by
item.itemdesc
Thanks

Oracle SQL - Assign queue of customers to list of Employees

I need to distribute list of employees to customers.
For example:
Table 1: List of Employees: A, B & C
Table 2: List of Customers: 1, 2, 3, 4, 5, 6, 7, 8, 9
The needed result:
|------------|------------|
| Customers | Employees |
|------------|------------|
| 1 | A |
| 2 | B |
| 3 | C |
| 4 | A |
| 5 | B |
| 6 | C |
| 7 | A |
| 8 | B |
| 9 | C |
|------------|------------|
You can use ROW_NUMBER() to assign a number on the fly to the employees, and the MOD() to do the rolling join. For example:
select
c.id,
e.name
from (
select t.*,
row_number() over(order by name) as rn
from employees t
) e
join customers c on e.rn =
mod(rn, (select count(*) from customers)) + 1
Number all rows, then use a modulo function for the join:
with e as
(
select employee, row_number() over (order by employee) as rn
from employees
)
, c as
(
select customer, row_number() over (order by customer) as rn
from customers
)
select c.customer, e.employee
from c
join e on e.rn - 1 = mod(c.rn - 1, (select count(*) from e))
order by c.customer;
Demo: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=43a6ca7469dff023d5513fa209e33ea7
You can use MOD function for this purpose. Try below code.
CREATE TABLE EMP
AS
SELECT 'A' AS EMP FROM DUAL
UNION ALL
SELECT 'B' AS EMP FROM DUAL
UNION ALL
SELECT 'C' AS EMP FROM DUAL;
CREATE TABLE CUST
AS
SELECT '1' AS CUST FROM DUAL
UNION ALL
SELECT '2' AS CUST FROM DUAL
UNION ALL
SELECT '3' AS CUST FROM DUAL
UNION ALL
SELECT '4' AS CUST FROM DUAL
UNION ALL
SELECT '5' AS CUST FROM DUAL
UNION ALL
SELECT '6' AS CUST FROM DUAL
UNION ALL
SELECT '7' AS CUST FROM DUAL
UNION ALL
SELECT '8' AS CUST FROM DUAL
UNION ALL
SELECT '9' AS CUST FROM DUAL;
SELECT CUST, EMP
FROM (SELECT ROW_NUMBER () OVER (PARTITION BY 1 ORDER BY EMP) AS ID, EMP
FROM EMP) EMP
INNER JOIN CUST ON MOD (TO_NUMBER (CUST.CUST) - 1, 3) = EMP.ID - 1
ORDER BY 1;

Select Duplicate records in Oracle

I have a table below in Oracle
Table1
State | Product |other fields
CA | P1 | xxxx
OR | P1 | xxxx
OR | P1 | xxxx
OR | P1 | xxxx
WA | P1 | xxxx
VA | P2 | xxxx
My Output should be only select if State has been occurred more than once.
State | Product |other fields
OR | P1 | xxxx
If you just want to count duplicate states then:
SELECT DISTINCT
State,
Product,
Other_Fields
FROM (
SELECT t.*,
COUNT(1) OVER ( PARTITION BY State ) AS cnt
FROM Table1 t
)
WHERE cnt > 1;
If you want to consider duplicate rows (considering all fields) then:
SELECT *
FROM Table1
GROUP BY State, Product, Other_Fields
HAVING COUNT(1) > 1;
select state, product, column_3, column_4
from (
select state, product, column_3, column_4,
count(*) over (partition by state) as cnt
from the_table
) t
where cnt > 1;
You could use ROW_NUMBER analytic function.
For example,
SQL> WITH sample_data AS(
2 SELECT 'CA' State, 'P1' product FROM dual UNION ALL
3 SELECT 'OR', 'P1' product from dual union all
4 SELECT 'OR', 'P1' product FROM dual UNION ALL
5 SELECT 'OR', 'P1' product FROM dual UNION ALL
6 SELECT 'WA', 'P1' product FROM dual UNION ALL
7 SELECT 'VA', 'P2' product from dual
8 )
9 -- end of sample_data mimicking real table
10 SELECT distinct state,
11 product
12 FROM
13 (SELECT state,
14 product,
15 row_number() OVER(PARTITION BY state ORDER BY product) rn
16 FROM sample_data
17 )
18 WHERE rn >1;
ST PR
-- --
OR P1
SQL>

Use SUM function in oracle

I have a table in Oracle which contains :
id | month | payment | rev
----------------------------
A | 1 | 10 | 0
A | 2 | 20 | 0
A | 2 | 30 | 1
A | 3 | 40 | 0
A | 4 | 50 | 0
A | 4 | 60 | 1
A | 4 | 70 | 2
I want to calculate the payment column (SUM(payment)). For (id=A month=2) and (id=A month=4), I just want to take the greatest value from REV column. So that the sum is (10+30+40+70)=150. How to do it?
You can also use below.
select id,sum(payment) as value
from
(
select id,month,max(payment) from table1
group by id,month
)
group by id
Edit: for checking greatest rev value
select id,sum(payment) as value
from (
select id,month,rev,payment ,row_number() over (partition by id,month order by rev desc) as rno from table1
) where rno=1
group by id
This presupposes you don't have more than one value per rev. If that's not the case, then you probably want a row_number analytic instead of max.
with latest as (
select
id, month, payment, rev,
max (rev) over (partition by id, month) as max_rev
from table1
)
select sum (payment)
from latest
where rev = max_rev
Or there's this, if I've understood the requirement right:
with demo as (
select 'A'as id, 1 as month, 10 as payment, 0 as rev from dual
union all select 'A',2,20,0 from dual
union all select 'A',2,30,1 from dual
union all select 'A',3,40,0 from dual
union all select 'A',4,50,0 from dual
union all select 'A',4,60,1 from dual
union all select 'A',4,70,2 from dual
)
select sum(payment) keep (dense_rank last order by rev)
from demo;
You can check the breakdown by including the key columns:
with demo as (
select 'A'as id, 1 as month, 10 as payment, 0 as rev from dual
union all select 'A',2,20,0 from dual
union all select 'A',2,30,1 from dual
union all select 'A',3,40,0 from dual
union all select 'A',4,50,0 from dual
union all select 'A',4,60,1 from dual
union all select 'A',4,70,2 from dual
)
select id, month, max(rev)
, sum(payment) keep (dense_rank last order by rev)
from demo
group by id, month;
select sum(payment) from tableName where id='A' and month=2 OR month=4 order by payment asc;

HAVING COUNT() not giving out the expected result

Attempting to get duplicate values of produce.prod_id using distinct, having clauses.but not giving out the expected result.
Here's my data:
PRODUCE
--------- -----------
PROD_ID PROD_NAME
--------- -----------
1 APPLES
2 PEARS
3 ORANGES
4 BANANAS
5 PEACHES
BUYERS
---------- ------------
BUYER_ID BUYER_NAME
---------- --------------
1 ADAM BARR
2 SEAN CHAI
3 EVA CORETS
4 ERIN O`MELIA
SALES
---------- --------- ------
BUYER_ID PROD_ID QTY
---------- --------- ------
1 2 15
1 3 5
4 1 37
3 5 11
4 3 1005
and here's my code:
select produce.prod_name,
buyer.BUYER_NAME,
SALES.PROD_ID
from produce
inner join SALES on produce.PROD_ID = SALES.PROD_ID
inner join buyer on SALES.BUYER_ID = buyer.BUYER_ID
group by produce.prod_name,
buyer.BUYER_NAME,
SALES.PROD_ID
having count(SALES.PROD_ID) > 1;
Expected result:
PROD_Name Buyer_Name
----------- --------------
Oranges ADAM BARR
Oranges ERIN O`MELIA
You need to use analytical function for your requirement
WITH PRODUCE(PROD_ID, PROD_NAME) AS (
SELECT 1, 'APPLES' from dual union all
select 2, 'PEARS' from dual union all
select 3, 'ORANGES' from dual union all
select 4, 'BANANAS' from dual union all
select 5, 'PEACHES' from dual),
BUYERS (BUYER_ID, BUYER_NAME) as (
select 1, 'ADAM BARR' from dual union all
select 2, 'SEAN CHAI' from dual union all
select 3, 'EVA CORETS' from dual union all
select 4, 'ERIN O`MELIA' from dual),
SALES(BUYER_ID, PROD_ID, QTY) as (
select 1, 2, 15 from dual union all
select 1, 3, 5 from dual union all
select 4, 1, 37 from dual union all
select 3, 5, 11 from dual union all
select 4, 3, 1005 from dual),
-- End of data preparation
TABLE_ AS (
SELECT produce.prod_name,
buyers.buyer_name,
sales.prod_id,
COUNT(1) OVER (PARTITION BY sales.prod_id) p_count
FROM produce
INNER JOIN sales
ON produce.prod_id = sales.prod_id
INNER JOIN buyers
ON sales.buyer_id = buyers.buyer_id)
SELECT prod_name, buyer_name, prod_id
FROM table_
WHERE p_count > 1;
Output:
| PROD_NAME | BUYER_NAME | PROD_ID |
|-----------|--------------|---------|
| ORANGES | ERIN O`MELIA | 3 |
| ORANGES | ADAM BARR | 3 |
Update: You simplified query would be:
With TABLE_ AS (
SELECT produce.prod_name,
buyers.buyer_name,
sales.prod_id,
COUNT(1) OVER (PARTITION BY sales.prod_id) p_count
FROM produce
INNER JOIN sales
ON produce.prod_id = sales.prod_id
INNER JOIN buyers
ON sales.buyer_id = buyers.buyer_id)
SELECT prod_name, buyer_name, prod_id
FROM table_
WHERE p_count > 1;
you don't need distinct. you only need group by and having. you also cant group by buyer_name to get a count > 1. that is where the issue lies, you can to do this as a set of nested queries
select dupes.prod_name,
buyers.buyer_name
from (
select produce.prod_name,
SALES.PROD_ID,
count(SALES.PROD_ID)
from produce
inner join SALES on produce.PROD_ID=SALES.PROD_ID
group by produce.prod_name,
SALES.PROD_ID
having count(*)>1
) as dupes
inner join sales on sales.prod_id = dupes.prod_id
inner join buyers on buyers.buyer_id = sales.buyer_id
Here's my, hopefully, simple answer.
Here's my SQL Fiddle scratch pad as well. http://sqlfiddle.com/#!9/d80f6/3
select P.Prod_Name, Buyer_Name
from Sales S
join
(select prod_id, count(prod_id) as ActSales
from Sales group by prod_id having count(prod_id) > 1) SQ
on S.Prod_Id = SQ.Prod_Id
join Produce P
on S.Prod_Id = P.Prod_Id
join Buyers B
on S.Buyer_Id = B.Buyer_Id