I'm struggling to try to have the count of order id on an item_id row, any help is greatly appreciated!
Data
item_id | order_id
1 | Order_1
2 | Order_1
3 | Order_2
4 | Order_3
Desired Result
item_id | order_id | items_in_order
1 | Order_1 | 2
2 | Order_1 | 2
3 | Order_2 | 1
4 | Order_3 | 1
SELECT S.item_id, S.`order_id`, S.order_total, C.cnt as items_in_order,
`order_discount` / C.cnt as item_discount,
`order_total` / C.cnt as item_price
FROM `orders` S
LEFT JOIN (SELECT `item_id`, `order_id`, count(`order_id`) as cnt FROM `supplier_orders` GROUP BY `order_id`)
C ON S.`order_id` = C.`order_id` AND S.id = C.item_id
This would produce this with null values
item_id | order_id | items_in_order | item_discount | item_price
3009117 | 3029511 | 2 | 0 | 25
3009118 | 3029511 | null | null | null
UPDATE, this now seems to work as intended
SELECT S.`item_id`, S.`order_id`, S.order_total, C.cnt as items_in_order,
`order_discount` / C.cnt as item_discount,
`order_total` / C.cnt as item_price
FROM `orders` S
INNER JOIN (SELECT `item_id`, `order_id`, count(`order_id`) as cnt FROM `orders` GROUP BY `order_id`)
C ON S.`order_id` = C.`order_id`
GROUP BY S.`item_id`
Your query does not relate to your sample data; however you seem to want aggregation and ranking. In MySQL 8.0, you would do:
select
row_number() over(order by count(*) desc) rn,
order_id,
count(*) items_in_order
from data
group by order_id
order by rn
I named the first column rn (for rank): I find id confusing here, since you already have a column with that name in the table.
In earlier versions, one option uses a session variable instead of row_number():
select #rn := #rn + 1 rn, order_id, items_in_order
from (
select order_id, count(*) items_in_order
from data
group by order_id
order by items_in_order desc
) t
cross join (select #rn := 0) r
order by items_in_order desc
Related
I have the following simple table:
id | patient_id | case_number | created_at
1 | 1 | x | 2021-02-25 10:57:24
2 | 1 | y | 2021-02-25 10:59:24
3 | 2 | z | 2021-02-25 10:57:14
4 | 2 | w | 2021-02-25 10:57:29
I want to get for each patient_id, its most recent case_number.
Meaning, final result of sql query I want is:
patient_id | case_number
1 | y
2 | w
This is what I've been trying:
SELECT *
FROM (SELECT patient_id, case_number FROM my_table ORDER BY created_at DESC) AS TEMP
GROUP BY patient_id
But this state returns:
patient_id | case_number
1 | x
2 | z
How to fix it?
If your mysql version didn't support Row_number window function, You can try to use self join
SELECT t1.patient_id ,t2.case_number
FROM (
SELECT MAX(created_at) latestDate,
patient_id
FROM my_table
GROUP BY patient_id
) t1 INNER JOIN my_table t2
ON t1.patient_id = t2.patient_id AND t1.latestDate = t2.created_at
From your comment, your MySQL version might be supporting ROW_NUMBER window function, you can use that to get the number which is the most recent date.
SELECT t1.patient_id,t1.case_number
FROM (
SELECT patient_id,
case_number,
ROW_NUMBER() OVER(PARTITION BY patient_id ORDER BY created_at DESC) rn
FROM my_table
) t1
WHERE rn = 1
Use window function FIRST_VALUE():
SELECT DISTINT patient_id,
FIRST_VALUE(case_number) OVER (PARTITION BY patient_id ORDER BY created_at DESC) case_number
FROM my_table
try this instead but still need to select created_time:
select distinct patient_id,case_number,time(created_time) from patients order by time(created_time) desc limit 2;
I'm trying to get just top 3 selling products grouped within categories (just top 3 products by occurrence in transactions (id) count(id) by each category). I was searching a lot for possible solution but with no result. It looks like it is a bit tricky in MySQL since one can't simply use top() function and so on. Sample data structure bellow:
+--------+------------+-----------+
| id |category_id | product_id|
+--------+------------+-----------+
| 1 | 10 | 32 |
| 2 | 10 | 34 |
| 3 | 10 | 32 |
| 4 | 10 | 21 |
| 5 | 10 | 100 |
| 6 | 7 | 101 |
| 7 | 7 | 39 |
| 8 | 7 | 41 |
| 9 | 7 | 39 |
+--------+------------+-----------+
In earlier versions of MySQL, I would recommend using variables:
select cp.*
from (select cp.*,
(#rn := if(#c = category_id, #rn + 1,
if(#c := category_id, 1, 1)
)
) as rn
from (select category_id, product_id, count(*) as cnt
from mytable
group by category_id, product_id
order by category_id, count(*) desc
) cp cross join
(select #c := -1, #rn := 0) params
) cp
where rn <= 3;
If you are running MySQL 8.0, you can use window function rank() for this:
select *
from (
select
category_id,
product_id,
count(*) cnt,
rank() over(partition by category_id order by count(*) desc) rn
from mytable
group by category_id, product_id
) t
where rn <= 3
In earlier versions, one option is to filter with a correlated subquery:
select
category_id,
product_id,
count(*) cnt
from mytable t
group by category_id, product_id
having count(*) >= (
select count(*)
from mytable t1
where t1.category_id = t.category_id and t1.product_id = t.product_id
order by count(*) desc
limit 3, 1
)
I was inspired by this post. But what I'm going to solve is more complex.
In the table below we have three columns, id,rating,created, call it test_table,
+----+--------+----------------------+
| id | rating | created |
+----+--------+----------------------+
| 1 | NULL | 2011-12-14 09:25:21 |
| 1 | 2 | 2011-12-14 09:26:21 |
| 1 | 1 | 2011-12-14 09:27:21 |
| 2 | NULL | 2011-12-14 09:25:21 |
| 2 | 2 | 2011-12-14 09:26:21 |
| 2 | 3 | 2011-12-14 09:27:21 |
| 2 | NULL | 2011-12-14 09:28:21 |
| 3 | NULL | 2011-12-14 09:25:21 |
| 3 | NULL | 2011-12-14 09:26:21 |
| 3 | NULL | 2011-12-14 09:27:21 |
| 3 | NULL | 2011-12-14 09:28:21 |
+----+--------+----------------------+
I want to write a query which selects the most recent rating but not null for every id. If all of the ratings are null for a specific id, we select the most recent rating. The desired result is as follows:
+----+--------+----------------------+
| id | rating | created |
+----+--------+----------------------+
| 1 | 1 | 2011-12-14 09:27:21 |
| 2 | 3 | 2011-12-14 09:27:21 |
| 3 | NULL | 2011-12-14 09:28:21 |
+----+--------+----------------------+
The following gets the creation date:
select t.id,
coalesce(max(case when rating is not null then creation_date end),
creation_date
) as creation_date
from t
group by t.id;
You can then do this as:
select t.*
from t
where (id, creation_date) in (select t.id,
coalesce(max(case when rating is not null then creation_date end),
creation_date
) as creation_date
from t
group by t.id
);
One possible answer is this. Create a list of max(create) date per id and id having all NULL rating.
select t1.*
from myTable t1
join (
select id, max(created) as created
from myTable
where rating is not NULL
group by id
UNION ALL
select id, max(created) as created
from myTable t3
where rating is NULL
group by id
having count(*) = (select count(*) from myTable t4 where t4.id=t3.id)
) t2
where t1.id=t2.id
and t1.created=t2.created
order by t1.id;
select a.* from #test a join (select id, max(created) created
from #test
where rating is not null
group by id )b on a.id=b.id and a.created=b.created
union
select a.* from #test a join
(select id, max(created) created
from #test
where rating is null
and id not in
(select id from (select id, max(created) created
from #test
where rating is not null
group by id )d
group by id)
group by id )b on a.id=b.id and a.created=b.created
This query should work:
select a.id, a.rating, b.m from test_table a
join (
select id, max(created) as m from test_table
where rating is not null
group by id
) b on b.id = a.id and b.m = a.created
union
select a.id, a.rating, b.m from test_table a
join(
select id, max(created) as m from test_table a
where not exists
(select 1 from test_table b where a.id = b.id and b.rating is not null)
group by id
)b on b.id = a.id and b.m = a.created
You can get the created value in a correlated LIMIT 1 subquery:
select t.id, (
select created
from mytable t1
where t1.id = t.id
order by rating is null asc, created desc
limit 1
) as created
from (select distinct id from mytable) t
If you also need the rating column, you will need to join the result with the table again:
select t.*
from (
select t.id, (
select created
from mytable t1
where t1.id = t.id
order by rating is null asc, created desc
limit 1
) as created
from (select distinct id from mytable) t
) x
natural join mytable t
Demo: http://sqlfiddle.com/#!9/49e68c/8
I have a table called employeexam which structure and data are like this:
--------------------------------------------------------
| id | course_id | employee_id | degree | date
--------------------------------------------------------
| 1 | 1 | 3 | 8 | 2013-01-14
| 2 | 2 | 4 | 15 | 2013-01-14
| 3 | 2 | 4 | 17 | 2013-01-15
--------------------------------------------------------
Desired result would be:
---------------------------------------------------------------------------
| id | course_id | employee_id | degree | date | numOfTakingExams
---------------------------------------------------------------------------
| 1 | 1 | 3 | 8 | 2013-01-14 | 1
| 3 | 2 | 4 | 17 | 2013-01-15 | 2
---------------------------------------------------------------------------
My MySQL query:
SELECT DISTINCT(employeexam.employee_id) as employeeid,
employeexam.*,
exam.numOfTakingExams
FROM employeexam
JOIN (
SELECT employee_id , COUNT(employee_id ) as numOfTakingExams
FROM employeexam
GROUP BY employee_id
) exam
ON exam.employee_id = employeexam.employee_id
ORDER BY employeexam.id DESC
This outputs numOfTakingExams value correctly, but i can't select only the data of the last time he entered an exam. Any help?
SELECT a.*, b.numOfTakingExams
FROM employeeExam a
INNER JOIN
(
SELECT employee_id,
MAX(date) max_Date,
COUNT(*) numOfTakingExams
FROM employeeExam
GROUP BY course_ID, employee_id
) b ON a.employee_id = b.employee_id AND
a.date = b.max_Date
SQLFiddle Demo
you can also get the latest record by the maximum ID if it is set as AUTO_INCREMENT, this query below yields the same result from the query above,
SELECT a.*, b.numOfTakingExams
FROM employeeExam a
INNER JOIN
(
SELECT employee_id,
MAX(id) max_Date,
COUNT(*) numOfTakingExams
FROM employeeExam
GROUP BY course_ID, employee_id
) b ON a.employee_id = b.employee_id AND
a.id = b.max_Date
SQLFiddle Demo
Try this query -
SELECT
t1.id, t1.course_id, t1.employee_id, t1.degree, t1.date, t2.numOfTakingExams
FROM
mployeexam t1
JOIN (
SELECT employee_id, MAX(date) date, COUNT(*) numOfTakingExams
FROM mployeexam
GROUP BY employee_id
) t2
ON t1.employee_id = t2.employee_id AND t1.date = t2.date
Have you tried a join with itself? In the first you select on the IDs containing the "last exams" and in the second you join the stuff that you need. Something along the lines of:
select A.* FROM
employeexam A INNER JOIN (
SELECT EMPLOYEE_ID, MAX(DATE)
FROM EMPLOYEEXAM
GROUP BY EMPLOYEE_ID
) B
ON A.EMPLOYEE_ID = B.EMPLOYEE_ID AND
A.DATE = B.DATE
Assuming of course the dates per Employee_id are unique.
SELECT x.*
, y.ttl
FROM employeexam x
JOIN
( SELECT course_id
, employee_id
, MAX(date) max_date
, COUNT(*) ttl
FROM employeexam
GROUP
BY course_id
,employee_id
) y
ON y.course_id = x.course_id
AND y.employee_id = x.employee_id
AND y.max_date = x.date;
SELECT ee.*, num_exams
FROM (
SELECT employee_id, COUNT(*) AS num_exams
FROM employeexam
GROUP BY
employee_id
) eed
JOIN employeeexam ee
ON ee.id =
(
SELECT id
FROM employeeexam eei
WHERE eei.employee_id = eed.employee_id
ORDER BY
eei.employee_id DESC, eei.date DESC, eei.id DESC
LIMIT 1
)
This will handle the case of multiple exams taken on one date correctly.
From MySQL - Get row number on select
I know how to get the row number / rank using this mysql query:
SELECT #rn:=#rn+1 AS rank, itemID
FROM (
SELECT itemID
FROM orders
ORDER BY somecriteria DESC
) t1, (SELECT #rn:=0) t2;
The result returns something like this:
+--------+------+
| rank | itemID |
+--------+------+
| 1 | 265 |
| 2 | 135 |
| 3 | 36 |
| 4 | 145 |
| 5 | 123 |
| 6 | 342 |
| 7 | 111 |
+--------+------+
My question is: How can I get the result in 1 simple SINGLE QUERY that returns items having lower rank than itemID of 145, i.e.:
+--------+------+
| rank | itemID |
+--------+------+
| 5 | 123 |
| 6 | 345 |
| 7 | 111 |
+--------+------+
Oracle sql query is also welcomed. Thanks.
An Oracle solution (not sure if it meets your criteria of "one simple single query"):
WITH t AS
(SELECT item_id, row_number() OVER (ORDER BY some_criteria DESC) rn
FROM orders)
SELECT t2.rn, t2.item_id
FROM t t1 JOIN t t2 ON (t2.rn > t1.rn)
WHERE t1.item_id = 145;
My assumption is no repeating values of item_id.
Attempting to put this in MySQL terms, perhaps something like this might work:
SELECT t2.rank, t2.itemID
FROM (SELECT #rn:=#rn+1 AS rank, itemID
FROM (SELECT itemID
FROM orders
ORDER BY somecriteria DESC), (SELECT #rn:=0)) t1 INNER JOIN
(SELECT #rn:=#rn+1 AS rank, itemID
FROM (SELECT itemID
FROM orders
ORDER BY somecriteria DESC), (SELECT #rn:=0)) t2 ON t2.rank > t1.rank
WHERE t1.itemID = 145;
Disclaimer: I don't work with MySQL much, and it's untested. The Oracle piece works.
SELECT #rn:=#rn+1 AS rank, itemID
FROM (
SELECT itemID
FROM orders
ORDER BY somecriteria DESC
) t1, (SELECT #rn:=0) t2
where rank >
(
select rank from
(
SELECT #rn:=#rn+1 AS rank, itemID
FROM
(
SELECT itemID
FROM orders
ORDER BY somecriteria DESC
) t1, (SELECT #rn:=0) t2
) x where itemID = 145
) y