My data looks like this:
Table Name = sales_orders
Customer_id| Order_id| Item_Id
-------------------------------
1 | 1 | 10
1 | 1 | 24
1 | 1 | 37
1 | 2 | 11
1 | 2 | 15
1 | 3 | 28
2 | 4 | 37
4 | 6 | 10
2 | 7 | 10
However, I need it to look like this:
Customer_id| Order_id| Item_Id |Order_rank
------------------------------------------
1 | 1 | 10 | 1
1 | 1 | 24 | 1
1 | 1 | 37 | 1
1 | 2 | 11 | 2
1 | 2 | 15 | 2
1 | 3 | 28 | 3
2 | 4 | 37 | 1
4 | 6 | 10 | 1
2 | 7 | 10 | 2
Customer_Id is a unique person
Order_id is a unique order
item_id is the product code
To further explain, the first three lines are from Customer #1's first order (order_id = 1) where this person ordered 3 different items (10,24, and 37). They then purchased another order (order_id =2) with two other products. Person with customer_id =2 has 2 unique orders (4 and 6), while customer with ID '4' has one unique order (order_id =6)
Essentially, what I need to do is rank these orders by customer_id and order Id, so that I can say "Order_id = 7 is the second order for customer_id = 2, because Order_rank = 2"
The challenge here is that I can't use session variables (e.g. #grp := customer_id ) in the MySQL query
For example, a query such as this is NOT allowed:
SELECT
customer_id,
order_id,
#ss := CASE WHEN #grp = customer_id THEN #ss + 1 ELSE 1 END AS
order_rank,
#grp := customer_id
FROM
(
SELECT
customer_id,
order_id
FROM sales_orders
GROUP BY customer_id, order_id
ORDER BY customer_id, order_id ASC
) AS t_1
CROSS JOIN (SELECT #ss := 0, #grp = NULL)ss
ORDER BY customer_id asc
Thanks for the help!
In a Correlated Subquery, we can Count(..) the unique and previous order_id values for a specific row's customer_id and order_id to determine the rank.
We need to count unique values because you have multiple rows per order (due to multiple items).
Query
SELECT
t1.Customer_id,
t1.Order_id,
t1.Item_Id,
(SELECT COUNT(DISTINCT t2.Order_id)
FROM sales_orders t2
WHERE t2.Customer_id = t1.Customer_id AND
t2.Order_id <= t1.Order_id
) AS Order_rank
FROM sales_orders AS t1;
Result
| Customer_id | Order_id | Item_Id | Order_rank |
| ----------- | -------- | ------- | ---------- |
| 1 | 1 | 10 | 1 |
| 1 | 1 | 24 | 1 |
| 1 | 1 | 37 | 1 |
| 1 | 2 | 11 | 2 |
| 1 | 2 | 15 | 2 |
| 1 | 3 | 28 | 3 |
| 2 | 4 | 37 | 1 |
| 4 | 6 | 10 | 1 |
| 2 | 7 | 10 | 2 |
View on DB Fiddle
You can use a correlated subquery:
select so.*,
(select count(*)
from sales_orders so2
where so2.Customer_id = so.Customer_id and
so2.order_id <= so.order_id
) as rank_order
from sales_orders so;
Or in MySQL 8+:
select so.*,
dense_rank() over (partition by Customer_Id order by Order_Id) as rank_order
from sales_orders so;
Related
I have the following employee_sequence table
| id | employee_id | sequence_id | is_completed |
|----|:-----------:|:-----------:|:------------:|
| 1 | 12 | 3 | 1 |
| 2 | 12 | 4 | 1 |
| 3 | 10 | 3 | 1 |
| 4 | 10 | 4 | 0 |
I am looking for how to get, in 1 query, the first row having is_completed = 0 for an employee. If no such row for that employee, then take first row having is_completed = 1
Example for employee_id = 12 (there is no is_completed = 0, so first row having is_completed = 1)
| id | employee_id | sequence_id | is_completed |
|----|:-----------:|:-----------:|:------------:|
| 1 | 12 | 3 | 1 |
Example for employee_id = 10 (first row having is_completed = 0)
| id | employee_id | sequence_id | is_completed |
|----|:-----------:|:-----------:|:------------:|
| 4 | 10 | 4 | 0 |
You can use row_number(), if yu are running MySQL 8.0; assumuming that id can be used to order the records, you would phrase this as:
select *
from (
select es.*, row_number() over(partition by employee_id order by is_completed, id) rn
from employee_sequence es
) es
where rn = 1
In ealier version, an alternative is a correlated subquery with a row-limiting clause:
select *
from employee_sequence es
where es.id = (
select es1.id
from employee_sequence es1
where es1.employee_id = es.employee_id
order by es1.is_completed, es.id
limit 1
)
If you want this per employee (as your question suggests):
select es.*
from employee_sequence es
where es.employee_id = 12
order by is_completed, id
limit 1;
Here is a db<>fiddle.
If you are running something less than MySql 8 then:
select * from employee_sequence e1
where e1.id = (
select e2.id
from employee_sequence e2
where e2.employee_id = e1.employee_id
order by e2.is_completed, e2.sequence_id
limit 1
);
| id | employee_id | sequence_id | is_completed |
| --- | ----------- | ----------- | ------------ |
| 1 | 12 | 3 | 1 |
| 4 | 10 | 4 | 0 |
View on DB Fiddle
Sample Data:
id | room_id | seat_num
----------------------------------
1 | 1 | null
2 | 1 | null
3 | 2 | null
4 | 2 | null
Desire Data:
id | room_id | seat_num
----------------------------------
1 | 1 | 1
2 | 1 | 2
3 | 2 | 1
4 | 2 | 2
how to write a sql to update the room seat number to serial num in MySQL 5.7? the room's seat is from 2-20.
One option uses the update/join syntax. In MySQL 5.7, where window functions are not available, you can emulate row_number() with a correlated subquery (which is somehow safer than user variables):
update mytable t
inner join (
select id,
(select count(*) from mytable t1 where t1.room_id = t.room_id and t1.id <= t.id) rn
from mytable t
) t1 on t1.id = t.id
set t.seat_num = t1.rn
Demo on DB Fiddle:
id | room_id | seat_num
:- | ------: | :-------
1 | 1 | 1
2 | 1 | 2
3 | 2 | 1
4 | 2 | 2
I have the followin problem:
I want to update all rows where COUNT criteria is greater 1, when not I want to update all except 1. It also should update per other_ID.
Dummytable:
+----+----------+----------+-------------+
| id | other_ID | cirteria | updatefield |
+----+----------+----------+-------------+
| 1 | 1 | 1 | 0 |
| 2 | 1 | 1 | 0 |
| 3 | 1 | 1234 | 0 |
| 4 | 2 | 2 | 0 |
| 5 | 2 | 1 | 0 |
| 6 | 2 | 1 | 0 |
| 7 | 4 | 20 | 0 |
| 8 | 4 | 1 | 0 |
| 9 | 4 | 60 | 0 |
| 10 | 5 | 1 | 0 |
| 11 | 5 | 1 | 0 |
| 12 | 6 | 5 | 0 |
+----+----------+----------+-------------+
excpected result:
+----+----------+----------+-------------+
| id | other_ID | cirteria | updatefield |
+----+----------+----------+-------------+
| 1 | 1 | 1 | 1 |
| 2 | 1 | 1 | 1 |
| 3 | 1 | 1234 | 0 |
| 4 | 2 | 2 | 0 |
| 5 | 2 | 1 | 1 |
| 6 | 2 | 1 | 1 |
| 7 | 4 | 20 | 0 |
| 8 | 4 | 1 | 1 |
| 9 | 4 | 60 | 0 |
| 10 | 5 | 1 | 0 |
| 11 | 5 | 1 | 1 |
| 12 | 6 | 5 | 0 |
+----+----------+----------+-------------+
my idea:
UPDATE pics AS tu SET updatefield=1 WHERE criteria=1 AND (select count(*) as cnt2 from pics where criteria>1 group by other_id)>1;
Error: Table 'tu' is specified twice, both as a target for 'UPDATE' and as a separate source for data
Also I have problems to geht the right count:
SELECT other_id, count() as cnt FROM pics AS ts WHERE criteria=1 and (select count() as cnt2 from pics where criteria>1)>0 GROUP BY other_id;
i want to get cnt = 1 for other_id=5, but i get cnt=2
with
SELECT other_id, COUNT(*) AS cnt2
FROM pics
WHERE criteria>1
GROUP BY other_id;
I get all other_ids where i want to update the updatefield. But how can I connect it with the update? And how to get all except one for other_id=5
You can alias the sub query into another query, e.g.:
UPDATE test
SET updatefield = 1
WHERE updatefield = 0 AND criteria = 1
AND other_id IN (
SELECT a.id FROM (
SELECT other_id AS id
FROM test
WHERE criteria > 1
GROUP BY other_id
HAVING COUNT(*) > 1
) a
);
Here's the SQL Fiddle.
Update
This will update the ids for records with criteria 0 and >1. Now, to update the records where there is more than one record with 1 criteria, you need to do something like this:
UPDATE test
SET updatefield = 1
WHERE updatefield = 0 AND criteria = 1
AND id IN (
SELECT a.id FROM (
SELECT MIN(id) AS id
FROM test
WHERE criteria = 1
GROUP BY other_id
HAVING COUNT(*) > 1
) a
);
Thanks to #Darshan Mehtas answer and help I finally found the solution to solve it as I want.
Here's the complete solution:
UPDATE test
SET updatefield = 1
WHERE updatefield = 0 AND criteria = 1
AND id not IN (
SELECT a.id FROM (
SELECT id
FROM test
WHERE criteria>1
) a
)
AND id not IN (
SELECT b.id FROM (
SELECT id
FROM test
GROUP BY other_id
HAVING COUNT(*) = 1
) b
)
AND id NOT IN (
SELECT c.id FROM (
SELECT id
FROM test
WHERE criteria=1 AND other_id NOT IN (
SELECT other_id FROM test WHERE Criteria>1
)
GROUP BY other_id, criteria
HAVING COUNT(criteria)>1
) c
);
Short description:
First Subquery (a) filters IDs where a criteria is greater 1
Second Subquery (b) filters IDs which have only on result
Third Subquery (c) filters IDs Where criteria is 1an don't have any higher criteria and keeps, thansk grouping, the first result.
Only bad thing could be to keep in the last subquery (c) the first (mostly oldest) result instead of newest.
€dit:
to keep the last result use this for subquery c instead:
AND id NOT IN (
SELECT c.id FROM (
SELECT id
FROM test t1
JOIN (SELECT other_id, max(id) maxid
FROM test
GROUP BY other_id) t2
ON t1.otheR_id=t2.other_id AND t1.id=t2.maxid
WHERE criteria=1 AND t1.other_id NOT IN (
SELECT other_id FROM test WHERE Criteria>1
)
GROUP BY t1.other_id, criteria
) c
);
I have following mysql table:
===================================
id | uid | vid | date | num |
===================================
1 | 1 | 1 | 2017-01-01 | |
2 | 1 | 1 | 2017-02-20 | |
3 | 2 | 1 | 2017-02-25 | |
4 | 1 | 2 | 2017-03-05 | |
5 | 1 | 1 | 2017-04-01 | |
===================================
I have to update num column in such way that if the date is the earliest date for the same uid & vid, then the num column will be 1, otherwise it will be 2. So, after updating the database/table the table will be:
===================================
id | uid | vid | date | num |
===================================
1 | 1 | 1 | 2017-01-01 | 1 |
2 | 1 | 1 | 2017-02-20 | 2 |
3 | 2 | 1 | 2017-02-25 | 1 |
4 | 1 | 2 | 2017-03-05 | 1 |
5 | 1 | 1 | 2017-04-01 | 2 |
===================================
As 2017-01-01 is the earliest date among id#1, id#2 & id#5 [uid=1 & vid=1], so num is 1 for id=1 and in case of id#2 & id#5, the num values are 2.
But although uid=1 is for id#4 but vid=2 in that case. So it will be num=1 as 2017-03-05 is the earliest and only one date for id=4 uid & vid combination. In the same way, num is 1 for id#3 as uid & vid are different.
How can I create a single mysql query, so that it will update the num column automatically based on the uid, vid & date?
update your_table t
left join (
select
uid, vid, min(id) as id
from your_table t
join (
select uid, vid, min(date) as date
from your_table
group by uid, vid
) t2 using (uid, vid, date)
group by uid, vid
) t2 on t.uid = t2.uid
and t.vid = t2.vid
and t.id = t2.id
set num = case when t2.id is null then 2 else 1 end;
Is there any way to return distinct values with blank/null data from a table join. Best to explain with my example below.
Table "orders"
order_id | order_total
1 | 10
2 | 20
3 | 50
Table "order_items"
item_id | order_id | name | qty_ordered | base_price | row_total
1 | 1 | Product | 1 | 10 | 10
2 | 2 | Product | 1 | 10 | 10
3 | 2 | Product2 | 1 | 10 | 10
4 | 3 | Product | 2 | 10 | 20
5 | 3 | Product2 | 3 | 10 | 30
I'm trying to produce a result set that looks like this.
order_id | item_id | name | qty_ordered | base_price | row_total | order_total
1 | 1 | Product | 1 | 10 | 10 | 10
2 | 2 | Product | 1 | 10 | 10 | 20
null | 3 | Product2 | 1 | 10 | 10 | null
3 | 4 | Product | 2 | 10 | 20 | 50
null | 5 | Product2 | 3 | 10 | 30 | null
I only want the order_id and order_total once per order. I figure this is possible with some sort of join/distinct/sub query but alas nothing I've tried has worked so far.
Use:
SELECT x.order_id,
x.item_id,
x.name,
x.qty_ordered,
x.base_price,
x.row_total,
x.order_total
FROM (SELECT CASE
WHEN #order = o.order_id THEN NULL
ELSE o.order_id
END AS order_id,
oi.item_id,
oi.name,
oi.qty_ordered,
oi.base_price,
oi.row_total,
o.order_total,
CASE
WHEN #order = o.order_id THEN NULL
ELSE o.order_total
END AS order_total,
#order := o.order_id
FROM ORDER_ITEMS oi
JOIN ORDERS o ON o.order_id = oi.order_id
JOIN (SELECT #order := -1) r
ORDER BY o.order_id, oi.item_id) x
SELECT * FROM order_items
LEFT JOIN orders
ON (
order_items.order_id=orders.order_id
AND
order_items.item_id=(
SELECT MIN(item_id)
FROM order_items a
WHERE a.order_id=order_items.order_id
)
)
This should work because the nested query always returns the same MIN(item_id) for each order, and it only joins for that item.
But this is a very, very ugly piece of sql. Don't do this.