SQL - Finding entries that are the max of a count? - sql

I have a table like the above image shown, how can I display the id and names of the customers and the category of the food that have the customer ordered the most?
SELECT Customer_ID, COUNT(F_Catg)
FROM ORDER_RECORD ORD
INNER JOIN FOOD_MENU FM
ON ORD.Item_ID = FM.Item_ID
GROUP BY Customer_ID
HAVING COUNT(F_Catg) =
(SELECT MAX(c) FROM
(SELECT COUNT(F_Catg) AS c
FROM ORDER_RECORD ORD
INNER JOIN FOOD_MENU FM
ON ORD.Item_ID = FM.Item_ID
GROUP BY Customer_ID))
I tried this but it doesn't work.

Your derived table is missing an alias. Fix that, and your query should work:
SELECT Customer_ID, COUNT(F_Catg)
FROM ORDER_RECORD ORD
INNER JOIN FOOD_MENU FM ON ORD.Item_ID = FM.Item_ID
GROUP BY Customer_ID
HAVING COUNT(F_Catg) = (SELECT MAX(c) FROM (
SELECT COUNT(F_Catg) AS c
FROM ORDER_RECORD ORD
INNER JOIN FOOD_MENU FM ON ORD.Item_ID = FM.Item_ID
GROUP BY Customer_ID) t -- fix is here
);
On MySQL 8+, you could simplify a bit by using the RANK analytic function:
WITH cte AS (
SELECT Customer_ID, COUNT(F_Catg) AS cnt,
RANK() OVER (ORDER BY COUNT(F_Catg) DESC) rnk
FROM ORDER_RECORD ORD
INNER JOIN FOOD_MENU FM ON ORD.Item_ID = FM.Item_ID
GROUP BY Customer_ID
)
SELECT Customer_ID, cnt
FROM cte
WHERE rnk = 1;

Related

calculating the first three HIGHEST GPA of each school

I have calculated the HIGHEST GPA scored by student from each school. But how can I get the list of top 3 GPA, I mean RANK 1, 2, and 3 scored by students from each school ?
SELECT A.*
FROM (
SELECT B.SCHOOL_NAME
,C.STUDENT_NAME
,A.SYMBOL_NO
,A.AVG_GPA
,MAX(A.AVG_GPA) OVER (PARTITION BY B.SCHOOL_NAME) AS MAX_GPA
FROM TBL_STUDENT_MARKS A
INNER JOIN TBL_SCHOOL B ON A.SCHOOL_ID = B.SCHOOL_ID
INNER JOIN TBL_STUDENT_INFO C ON A.SYMBOL_NO = C.SYMBOL_NO
AND B.SCHOOL_ID = C.SCHOOL_ID
) A
WHERE A.AVG_GPA = MAX_GPA
ORDER BY A.AVG_GPA DESC;
You can use either RANK() or DENSE_RANK() or ROW_NUMBER() to achieve the result.
However, RANK() and DENSE_RANK() are preferable for this scenario.
RANK():
SELECT A.*
FROM (
SELECT B.SCHOOL_NAME
,C.STUDENT_NAME
,A.SYMBOL_NO
,A.AVG_GPA
,RANK() OVER (PARTITION BY B.SCHOOL_NAME ORDER BY A.AVG_GPA DESC) AS SCHOOL_RANK
FROM TBL_STUDENT_MARKS A
INNER JOIN TBL_SCHOOL B ON A.SCHOOL_ID = B.SCHOOL_ID
INNER JOIN TBL_STUDENT_INFO C ON A.SYMBOL_NO = C.SYMBOL_NO AND B.SCHOOL_ID = C.SCHOOL_ID
) A
WHERE SCHOOL_RANK <= 3
ORDER BY SCHOOL_NAME, SCHOOL_RANK
DENSE_RANK():
SELECT A.*
FROM (
SELECT B.SCHOOL_NAME
,C.STUDENT_NAME
,A.SYMBOL_NO
,A.AVG_GPA
,DENSE_RANK() OVER (PARTITION BY B.SCHOOL_NAME ORDER BY A.AVG_GPA DESC) AS SCHOOL_DENSE_RANK
FROM TBL_STUDENT_MARKS A
INNER JOIN TBL_SCHOOL B ON A.SCHOOL_ID = B.SCHOOL_ID
INNER JOIN TBL_STUDENT_INFO C ON A.SYMBOL_NO = C.SYMBOL_NO AND B.SCHOOL_ID = C.SCHOOL_ID
) A
WHERE SCHOOL_DENSE_RANK<= 3
ORDER BY SCHOOL_NAME, SCHOOL_DENSE_RANK
ROW_NUMBER():
SELECT A.*
FROM (
SELECT B.SCHOOL_NAME
,C.STUDENT_NAME
,A.SYMBOL_NO
,A.AVG_GPA
,ROW_NUMBER() OVER (PARTITION BY B.SCHOOL_NAME ORDER BY A.AVG_GPA DESC) AS SCHOOL_ROW_NUMBER
FROM TBL_STUDENT_MARKS A
INNER JOIN TBL_SCHOOL B ON A.SCHOOL_ID = B.SCHOOL_ID
INNER JOIN TBL_STUDENT_INFO C ON A.SYMBOL_NO = C.SYMBOL_NO AND B.SCHOOL_ID = C.SCHOOL_ID
) A
WHERE SCHOOL_ROW_NUMBER <= 3
ORDER BY SCHOOL_NAME, SCHOOL_ROW_NUMBER
Please refer here for the difference between Rank, DenseRank and RowNumber
You can use DENSE_RANK() and PARTITION BY in combination:
DENSE_RANK() OVER (
PARTITION BY <expr1>[{,<expr2>...}]
ORDER BY <expr1> [ASC|DESC], [{,<expr2>...}]
)
In this case something like
DENSE_RANK() OVER (PARTITION BY B.SCHOOL_NAME ORDER BY AVG_GPA desc) AS SCHOOL_RANK
and then in the outer query you add
WHERE SCHOOL_RANK in (1, 2, 3)
Also
Give Common Table Expressions a look! I find it often nicer than nested SELECT.
WITH ranked as (
SELECT
SCHOOL_ID
, SYMBOL_NO
, AVG_GPA
, DENSE_RANK() OVER (PARTITION BY SCHOOL_ID ORDER BY AVG_GPA DESC) AS SCHOOL_RANK
FROM TBL_STUDENT_MARKS
)
SELECT
SC.SCHOOL_NAME
, INFO.STUDENT_NAME
, ST.SYMBOL_NO
, ST.AVG_GPA
, ST.SCHOOL_RANK
FROM ranked ST
INNER JOIN TBL_SCHOOL SC ON R.SCHOOL_ID = SC.SCHOOL_ID
INNER JOIN TBL_STUDENT_INFO INFO ON ST.SYMBOL_NO = INFO.SYMBOL_NO AND SC.SCHOOL_ID = INFO.SCHOOL_ID
WHERE SCHOOL_RANK <= 3
ORDER BY SCHOOL_NAME, SCHOOL_RANK;
You can use ROW_NUMBER() OVER (PARTITION BY B.SCHOOL_NAME ORDER BY A.AVG_GPA DESC)
SELECT A.*
FROM (
SELECT B.SCHOOL_NAME
,C.STUDENT_NAME
,A.SYMBOL_NO
,A.AVG_GPA
,ROW_NUMBER() OVER (PARTITION BY B.SCHOOL_NAME ORDER BY A.AVG_GPA DESC)
AS HIGHEST_GPA
FROM TBL_STUDENT_MARKS A
INNER JOIN TBL_SCHOOL B ON A.SCHOOL_ID = B.SCHOOL_ID
INNER JOIN TBL_STUDENT_INFO C ON A.SYMBOL_NO = C.SYMBOL_NO
AND B.SCHOOL_ID = C.SCHOOL_ID
) A
WHERE HIGHEST_GPA <= 3
ORDER BY A.AVG_GPA DESC;
Use Dense_rank window function:
select *
from(SELECT A.*,DENSE_RANK()over(partition by school_name order by MAX_GPA
desc)as rankno
FROM (
SELECT B.SCHOOL_NAME
,C.STUDENT_NAME
,A.SYMBOL_NO
,A.AVG_GPA
,MAX(A.AVG_GPA) OVER (PARTITION BY B.SCHOOL_NAME) AS MAX_GPA
FROM TBL_STUDENT_MARKS A
INNER JOIN TBL_SCHOOL B ON A.SCHOOL_ID = B.SCHOOL_ID
INNER JOIN TBL_STUDENT_INFO C ON A.SYMBOL_NO = C.SYMBOL_NO
AND B.SCHOOL_ID = C.SCHOOL_ID
) A
WHERE A.AVG_GPA = MAX_GPA )B
WHERE rankno IN(1,2,3);

Oracle SQL is not a group by expression

When I run this code
SELECT sales_date AS DAY, SUM (TOTAL_AMOUNT_RM)
FROM SALESINVOICE
GROUP BY SALES_DATE
order by sales_date;
it gives me a table which is
enter image description here
Then I run the other code
SELECT SR.DAY, SUM(SR.PROFIT_RM) AS TOTALPROFIT_RM
FROM
(SELECT SALESINVOICE_ID AS NO, S.ITEM_ID AS ITEM, SALES_DATE AS DAY, S.QUANTITY, C.PRICE_RM, C.COST_RM, ((S.QUANTITY*C.PRICE_RM)-(S.QUANTITY*C.COST_RM)) AS PROFIT_RM
FROM SALESINVOICE S
INNER JOIN (SELECT ITEM_ID, PRICE_RM, COST_RM FROM ITEM I ) C
ON S.ITEM_ID = C.ITEM_ID
ORDER BY SALESINVOICE_ID, SALES_DATE) SR
GROUP BY SR.DAY
ORDER BY SR.DAY;
which gave me a table enter image description here
I try to inner join both table, but when I try to call the column sum(total_amount_rm), it gives me an group by expression error
SELECT SR.DAY, SUM(SR.PROFIT_RM) AS TOTALPROFIT_RM, D.TOTAL
FROM
(SELECT SALESINVOICE_ID AS NO, S.ITEM_ID AS ITEM, SALES_DATE AS DAY, S.QUANTITY, C.PRICE_RM, C.COST_RM, ((S.QUANTITY*C.PRICE_RM)-(S.QUANTITY*C.COST_RM)) AS PROFIT_RM
FROM SALESINVOICE S
INNER JOIN (SELECT ITEM_ID, PRICE_RM, COST_RM FROM ITEM I ) C
ON S.ITEM_ID = C.ITEM_ID
ORDER BY SALESINVOICE_ID, SALES_DATE) SR
INNER JOIN (SELECT SALES_DATE AS DAY, SUM(TOTAL_AMOUNT_RM) AS TOTAL FROM SALESINVOICE GROUP BY SALES_DATE ORDER BY SALES_DATE) D
ON D.DAY = SR.DAY
GROUP BY SR.DAY
ORDER BY SR.DAY;
However, it can run without the selection of D.TOTAL. Can anyone help me? I am sql newbie
You get an error because with GROUP BY, in SELECT clause, all columns that is not in the GROUP BY clause must come with an aggregate function
To solve it, one way is calculating sum before joining
SELECT s.day, s.totalprofit_rm, d.total
FROM
(
SELECT sr.day,
SUM(sr.profit_rm) AS totalprofit_rm
FROM
(
SELECT salesinvoice_id AS no,
s.item_id AS item,
sales_date AS day,
s.quantity,
c.price_rm,
c.cost_rm,
(s.quantity * c.price_rm) - (s.quantity * c.cost_rm) AS profit_rm
FROM salesinvoice s
INNER JOIN (SELECT item_id, price_rm, cost_rm FROM item) c
ON s.item_id = c.item_id
) sr
GROUP BY sr.day
) s
INNER JOIN
(
SELECT sales_date AS day,
SUM(total_amount_rm) AS total
FROM salesinvoice
GROUP BY sales_date
) d
ON d.day = s.day
ORDER BY s.day;
And this above query could be rewrite to be shorter:
SELECT sr.day, sr.totalprofit_rm, d.total
FROM
(
SELECT
s.sales_date AS day,
SUM((s.quantity * c.price_rm) - (s.quantity * c.cost_rm)) AS totalprofit_rm
FROM salesinvoice s
INNER JOIN item c
ON s.item_id = c.item_id
GROUP BY s.sales_date
) sr
INNER JOIN
(
SELECT sales_date AS day,
SUM(total_amount_rm) AS total
FROM salesinvoice
GROUP BY sales_date
) d
ON d.day = sr.day
ORDER BY sr.day;

DB2 - Invalid use of an aggregate function SQLCODE=-120

Can anyone help me to understand what is wrong with this Db2 query syntax,it is failing with SQLCODE -120 because i am trying to use row_number()?
SELECT COUNT(ORDER_ID) OVER() TOTAL_NO_OF_RECORDS,
ROW_NUMBER() OVER (ORDER BY CREATED_DATE DESC) AS ROW_NUM
FROM (
SELECT DISTINCT A.ORDER_ID ORDER_ID,
B.AgencyName AS AGENCY_NAME,
C.FirstName FIRST_NAME,
C.LastName LAST_NAME,
DEMOGRAPHIC.State STATE,
A.CreatedTS CREATED_DATE,
E.WritingTIN WRITING_TIN,
E.ParentTIN PARENT_TIN
FROM DBO.TABLE1 A
INNER JOIN
DBO.TABLE2 TABLE2 ON TABLE2.ORDER_ID=A.ORDER_ID
INNER JOIN
DBO.TABLE3 TABLE3 ON TABLE3.QuoteId=TABLE2.QuoteId
INNER JOIN
DBO.Demographic DEMOGRAPHIC ON
DEMOGRAPHIC.DemographicId=TABLE3 .DemographicId
INNER JOIN
DBO.Agent E ON E.AgentId=DEMOGRAPHIC.AgentId
INNER JOIN
DBO.User USER ON USER.WebAcctID=AGENT.WebAcctId
INNER JOIN
DBO.Shopper SHOPPER ON SHOPPER.ShopperId=DEMOGRAPHIC.ShopperId
LEFT OUTER JOIN
DBO.Subsidy D ON D.demographicId=DEMOGRAPHIC.demographicId
LEFT OUTER JOIN
DBO.Employer EMPLOYER ON DEMOGRAPHIC.demographicId=EMPLOYER.demographicId
WHERE E.WritingTIN = 'XYZ' AND E.ParentTIN = 'XYZ'
AND DEMOGRAPHIC.State='OH'
AND A.Status IN ('X','Y','Z')
)AS ORDER_DETAILS
where ROW_NUMBER() OVER (ORDER BY CREATED_DATE DESC) BETWEEN ((1*50)-50)+1 AND 1*50 ORDER BY CREATED_DATE DESC
ERROR SHOWN:Invalid use of an aggregate function or OLAP function.. SQLCODE=-120, SQLSTATE=42903, DRIVER=4.18.60
You have multiple errors:
X is not defined in the subquery.
B.CREATED_DATE is referenced in the outer query, but it is not defined.
ROW_NUMBER() is used in the WHERE clause.
This would seem to be the intention of the query you write:
SELECT COUNT(*) OVER () NO_OF_RECORDS, ROW_NUM
FROM (SELECT A.ID, B.FirstName as FIRST_NAME, B.LastName as LAST_NAME,
MAX(B.CREATED_DATE) as CREATED_DATE,
ROW_NUMBER() OVER (ORDER BY MAX(CREATED_DATE) DESC) AS ROW_NUM
FROM SCHEMANAME.A A INNER JOIN
SCHEMANAME.B B
ON B.ShopperId = A.ShopperId LEFT OUTER JOIN
SCHEMANAME.C C
ON C.demographicId = B.demographicId
WHERE A.WritingTIN = 'XYZ' AND A.ParentTIN = 'XYZ' AND
B.State = 'OH' AND C.Status IN ('X', 'Y', 'Z')
GROUP BY A.ID, B.FirstName, B.LastName
) ORDER_DETAILS
WHERE ROW_NUM BETWEEN ((1*50)-50)+1 AND 1*50
ORDER BY CREATED_DATE DESC;
I'm not sure that this result makes sense, but it should fix your error.

Get the biggest price using MAX() function

I am using SQL Oracle, and I want to change my query using MAX function and not ROWNUM.
SELECT *
FROM (SELECT a.name, price.price
FROM price
LEFT JOIN a
ON a.id = price.tk_a
ORDER BY price.price DESC)
WHERE ROWNUM <=1;
Any help or suggestions please?
You can use the MAX aggregate function with KEEP ( DENSE_RANK FIRST ORDER BY ... ) to get the maximum of another column:
SELECT MAX( a.name ) KEEP ( DENSE_RANK FIRST ORDER BY p.price DESC ) AS name,
MAX( p.price ) AS price
FROM a
LEFT OUTER JOIN price p
ON ( a.id = p.tk_a );
Try this:
select a1.name, p1.price
from price p1
JOIN a a1 ON a1.id = p1.tk_a
JOIN (SELECT MAX(p.price)
FROM price p) C on p1.price = c.price
MAX and group by
SELECT a.name, MAX(p.price)
FROM price
LEFT JOIN a ON a.id = p.tk_a
GROUP BY a.name
ORDER BY p.price DESC
If you just want the single MAX price then use this
SELECT MAX(p.price), (SELECT MAX(b.name) FROM a b LEFT JOIN price pp ON b.id = pp.tk_a WHERE a.id = b.id AND p.price = pp.price) AS name
FROM price
LEFT JOIN a ON a.id = p.tk_a
ORDER BY p.price DESC
Use This
SELECT *
FROM (SELECT a.name,MAX( price.price)
FROM price
LEFT JOIN a
ON a.id = price.tk_a
GROUP BY a.name
ORDER BY price.price DESC)

SQL: improving join efficiency

If I turn this sub-query which selects sales persons and their highest price paid for any item they sell:
select *,
(select top 1 highestProductPrice
from orders o
where o.salespersonid = s.id
order by highestProductPrice desc ) as highestProductPrice
from salespersons s
in to this join in order to improve efficiency:
select *, highestProductPrice
from salespersons s join (
select salespersonid, highestProductPrice, row_number(
partition by salespersonid
order by salespersonid, highestProductPrice) as rank
from orders ) o on s.id = o.salespersonid
It still touches every order record (it enumerates the entire table before filtering by salespersonid it seems.) However you cannot do this:
select *, highestProductPrice
from salespersons s join (
select salespersonid, highestProductPrice, row_number(
partition by salespersonid
order by salespersonid, highestProductPrice) as rank
from orders
where orders.salepersonid = s.id) o on s.id = o.salespersonid
The where clause in the join causes a `multi-part identifier "s.id" could not be bound.
Is there any way to join the top 1 out of each order group with a join but without touching each record in orders?
Try
SELECT
S.*,
T.HighestProductPrice
FROM
SalesPersons S
CROSS APPLY
(
SELECT TOP 1 O.HighestProductPrice
FROM Orders O
WHERE O.SalesPersonid = S.Id
ORDER BY O.SalesPersonid, O.HighestProductPrice DESC
) T
would
select s.*, max(highestProductPrice)
from salespersons s
join orders o on o.salespersonid = s.id
group by s.*
or
select s.*, highestProductPrice
from salespersons s join (select salepersonid,
max(highestProductPrice) as highestProductPrice
from orders o) as o on o.salespersonid = s.id
work?