Missing closing parenthesis using DENSE_RANK() MySQL - mysql

I am not sure where my syntax is wrong here. I need to display the top vendors based on invoice_total
select *
from (
select vendor_id, invoice_total,
dense_rank () over(partition by vendor_id order by invoice_total asc)
as ranking
from invoices) a1

MySQL only supports dense_rank() in version 8+. You can always use a correlated subquery:
select i.*
from invoices i
where i.invoice_total = (select max(i2.invoice_total)
from invoices i2
where i2.vendor_id = i.vendor_id
);
This assumes that "top vendors" refers to the largest totals, which is the opposite of your SQL.
There are other ways to express this. I also like using tuples in MySQL:
select i.*
from invoices i
where (i.vendor_id, i.invoice_total) in
(select i2.vendor_id, max(i2.invoice_total)
from invoices i2
group by i2.vendor_id
);

Add where a1.ranking = 1 for outer part of the SQL :
select *
from (
select vendor_id, invoice_total,
dense_rank () over(partition by vendor_id order by invoice_total asc)
as ranking
from invoices) a1
where a1.ranking = 1;

Related

How to use a dynamic limit in sql query

I have a query SELECT * FROM grades WHERE userid = 4123;
I want to limit this query
I have a query SELECT * FROM grades WHERE userid = 4123 LIMIT(2);
This works great but if I want this limit to be dynamic from another query.
SELECT COUNT(id) FROM count_table WHERE course = 131;
doing this gives me a syntax error
SELECT * FROM grades WHERE userid = 4123 LIMIT (SELECT COUNT(id) FROM count_table WHERE course = 131);
if this is not possible at all, then is there an alternative way to achieve this?
please help.!
You can do it in MySQL 8.x using the ROW_NUMBER() function.
Assuming you order the rows by some column (I guessed the column ID... change it as needed), you can do:
select
g.*
from (
select
*,
row_number() over(order by id) as rn -- change ordering as needed
from grades
) g
join (
SELECT COUNT(id) as cnt FROM count_table WHERE course = 131
) c on g.rn <= c.cnt

Trying to figure out how to find the max count

Which team has the most number of members on their roster?
Okay so below is the code that I have input currently. It returns all the teams as well as how many people are on each team. I am not sure how to code it to only display the team with the most members as when I try to use a max function and a count function I get an error.
SELECT Team_Name, COUNT(Member.Student_ID_Num)
FROM Teams
JOIN Member ON Teams.Team_Number = Member.Team_Number
GROUP BY Team_Name
you can try below - using limit and order by desc
Select Team_Name, count(Member.Student_ID_Num) as cnt
from Teams join Member on Teams.Team_Number = Member.Team_Number
group by Team_Name
order by cnt desc
limit 1
If you are using MySQL 8+, then the ROW_NUMBER function comes in handy here:
SELECT Team_Name, cnt
FROM
(
SELECT t.Team_Name, COUNT(*) AS cnt,
ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC) rn
FROM Teams t
INNER JOIN Member m
ON t.Team_Number = m.Team_Number
GROUP BY t.Team_Name
) t
WHERE rn = 1;
If you instead want all ties for the highest count, should two or more teams be tied, then replace ROW_NUMBER with RANK.
If you have to do this the old fashioned way, without LIMIT or ROW_NUMBER, then get ready for a really ugly query:
SELECT
t.Team_Name,
COUNT(*) AS cnt
FROM Teams t
INNER JOIN Member m
ON t.Team_Number = m.Team_Number
GROUP BY t.Team_Name
HAVING COUNT(*) = (SELECT MAX(cnt) FROM (SELECT COUNT(*) AS cnt
FROM Teams t
INNER JOIN Member m
ON t.Team_Number = m.Team_Number
GROUP BY t.Team_Name) t );
In order to get the team with the highest member count, we need no join (unless we also want to show the member count). One thing to keep in mind that there may be multiple teams sharing the same maximum member count.
The old fashioned way in standard SQL, before there was any limit clause (FETCH FIRST ROWS in standard SQL) was this:
Count members per team number.
Get the maximum count.
Get the team number(s) with this maximum count.
Get the team(s) for these team numbers.
The query:
select *
from teams
where team_number in
(
select team_number
from member
group by team_number
having count(*) =
(
select max(cnt) as max_cnt
from
(
select count(*) as cnt
from member
group by team_number
) counted
)
);
As of MySQL 8 we would rather use a wnindow function, however:
select *
from teams
where (team_number, 1) in
(
select team_number, rank() over (order by count(*) desc)
from member
group by team_number
);

SQL - get several occurences for each value with DISTINCT or GROUP BY

with DISTINCT or GROUP BY a record for each value (or set of values) can be taken like in the query below:
SELECT id, MAX(price) FROM Products GROUP BY id
The result will be something like:
ID | price
1 10
2 11
Is it possible to have, for example, 3 different prices for each ID ?
You can try below - using self join
DEMO
SELECT Products.id, Products.price,COUNT(p.price) AS rank
FROM Products
LEFT JOIN Products AS p ON Products.id = p.id AND Products.price< p.price
GROUP BY Products.id, Products.price
HAVING COUNT(p.price) < 3
ORDER BY Products.id, Products.price DESC
You can use GROUP_CONCAT if you want to get all the values, e.g.:
SELECT id, GROUP_CONCAT(price)
FROM table
GROUP BY id;
If your MySql version supports Window functions you can use RANK() and PARTITION
SELECT id, price
FROM (SELECT id, price, RANK() OVER w as price_rank
FROM test
WINDOW w as (PARTITION BY id ORDER BY price desc)) r
WHERE price_rank <= 3
ORDER BY id, price desc
One way to get the three highest prices is to use group_concat() and substring_index():
SELECT id,
SUBSTRING_INDEX(GROUP_CONCAT(price ORDER BY price DESC), ',' 3) as top3_prices
FROM Products
GROUP BY id;

MYSQL GROUP BY clause adding a non GROUP BY column

I have a table in MySQL and I want to do some group by operation. So here is my table
The expected result should be the sum of prices group by bag_no but the product_id should be the mainproduct='Y'. So the result should be like this
I can do the following
SELECT bag_no, sum(price) AS total_price
FROM myTable
GROUP BY bag_no
But this will not allow me to add product_id in the table which I also want. How can we do that?
One canonical way to do this involves joining to a subquery which finds the sums:
SELECT
t1.bag_no,
t1.product_id,
t2.total_price
FROM myTable t1
INNER JOIN
(
SELECT bag_no, SUM(price) AS total_price
FROM myTable
GROUP BY bag_no
) t2
ON t1.bag_no = t2.bag_no
WHERE
t1.mainproduct = 'Y';
With MySQL 8+, which supports analytic functions, we can slightly simplify the above query:
WITH cte AS (
SELECT
t1.bag_no,
t1.product_id,
SUM(price) OVER (PARTITION BY t1.bag_no) total_price,
t1.mainproduct
FROM myTable t1
)
SELECT bag_no, product_id, total_price
FROM cte
WHERE mainproduct = 'Y';

select last record from a relation

I have this situation. I have a table Orders that is related to OrderStatus.
OrderStatus
id | orderId | created
I need to retrieve the Orders with its last status. I tried this query, what I don't know if it is performant. I need to know if there are better solutions.
select Orders.id, OrderStatus.status from Orders
inner join OrderStatus on OrderStatus.id =
(select top 1 id from OrderStatus where orderId = Order.id order by created desc)
Correlated subquery is usually bad news (sometimes SQL Server can optimize it away, sometimes it acts like a really slow loop). Also not sure why you think you need DISTINCT when you're only taking the latest status, unless you don't have any primary keys...
;WITH x AS
(
SELECT o.id, os.status,
rn = ROW_NUMBER() OVER (PARTITION BY os.orderId ORDER BY created DESC)
FROM dbo.Orders AS o
INNER JOIN dbo.OrderStatus AS os
ON o.id = os.orderId
)
SELECT id, status
FROM x
WHERE rn = 1;
You can use the Row_Number function:
WITH CTE AS
(
SELECT Orders.id, OrderStatus.status,
RN = ROW_NUMBER() OVER (
PARTITION BY OrderStatus.OrderId
ORDER BY created DESC)
FROM Orders
INNER JOIN OrderStatus ON OrderStatus.OrderId = Orders.id
)
SELECT id, status
FROM CTE WHERE RN = 1
I've used a common-table-expression since it enables to filter directly and it's also very readable.