Count of line item in MySQL - mysql

I have a complex query joined to multiple tables to show a "transaction history", I want a quick and easy way to count the occurrences of order_id to make it as the line_number. I had it done with code but for performance reason I want to have it done with MySQL. Here is an example of what I have:
order_id item_id
--------- --------
1111111 444444
1111111 555555
1111111 666666
1111112 123333
1111112 121111
1111113 123123
1111114 123123
1111115 123123
This is what I want it to look like:
order_id line_number item_id
--------- ------------ ---------
1111111 1 444444
1111111 2 555555
1111111 3 666666
1111112 1 123333
1111112 2 121111
1111113 1 123123
1111114 1 123123
1111115 1 123123
I tried COUNT(order_id) but that didn't work, unless there is a good way to do it. Thanks!
MySQL version 5.5.43

In MySQL 8.0, just use row_number():
select
order_id,
row_number() over(partition by order_id order by item_id) line_number,
item_id
from mytable
order by order_id, item_id
In earlier versions, one solution (other than using variables) is to do an inline count with a subquery:
select
order_id,
(
select 1 + count(*)
from mytable t1
where t1.order_id = t.order_id and t1.item_id < t.item_id
) line_number,
item_id
from mytable t
order by order_id, item_id

Related

SQL that returns the total products quantity for orders with a status of delivered (different two tables)

I want to get the total products quantity for orders with a status of delivered. How can I write it as a single sql query?
table_1:
id
order_id
product_id
quantity
1
100001
123456780
3
2
100002
123456781
1
3
100002
123456782
5
4
100003
123456783
2
table_2:
id
order_id
order_status
order_date
1
100001
preparing
2023-01-26
2
100001
prepared
2023-01-26
3
100001
delivered
2023-01-26
4
100002
preparing
2023-01-26
5
100002
prepared
2023-01-26
6
100002
delivered
2023-01-26
7
100003
preparing
2023-01-26
8
100004
preparing
2023-01-26
9
100001
returned
2023-01-27
The sql query below didn't work as expected. Because it doesn't look at the latest status of the order:
SELECT SUM(t1.quantity) AS total
FROM table_1 t1
LEFT JOIN table_2 t2
ON t1.order_id = t2.order_id
WHERE t2.order_status = 'delivered'
Result: 9 (3+1+5) This result is not correct. Because the last status of the order number 100001 was Returned, it should not have added the number 3 to the total.
Correct result should be 6 (1+5).
There are two issues in your query:
You're not taking the latest record for each order_id. You can do such thing using the ROW_NUMBER window function, that assigns a ranking to each record of order_id ordered by order_date descendently (last order_date for each order_id has rownum = 1). Then you can filter on the rownum = 1.
You're using a LEFT JOIN, but you want only rows that can be found in "table_2" and that strictly satisfy the conditions. You should rather use the INNER JOIN.
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY order_id
ORDER BY order_date DESC, id DESC) AS rn
FROM table_2
)
SELECT SUM(quantity)
FROM table_1 t1
INNER JOIN cte t2
ON t1.order_id = t2.order_id
AND t2.order_status = 'delivered'
AND t2.rn = 1
If you can't use common table expressions for some reason, an equivalent way of dealing with it is turning the cte into a subquery:
SELECT SUM(quantity)
FROM table_1 t1
INNER JOIN (SELECT *, ROW_NUMBER() OVER(PARTITION BY order_id
ORDER BY date DESC, id DESC) AS rn
FROM table_2) t2
ON t1.order_id = t2.order_id
AND t2.order_status = 'delivered'
AND t2.rn = 1
Output:
SUM(quantity)
6
Check the demo here.

SQL query to only yield multiple occurances

I am using mariadb and I have a table called links:
id | product_id | last_change
------------------------------
1 1 xxx
2 2 xxx
3 5 xxx
4 5 xxx
I want to find every object (3, 4 in this example) that occures more than once. Following this answer I tried:
SELECT product_id, COUNT(*) from links HAVING COUNT(*) > 1
But this results in the (adapted to this example) first row being shown and the total number of product_id occurrences:
product_id | COUNT(*)
---------------------
1 4
I wanted to achieve a list of all items occuring more than once:
id | product_id | last_change
------------------------------
3 5 xxx
4 5 xxx
An aggregation function without GROUP BY always results in only one row result as it aggregates all rows
So use a GROUP BY
SELECT product_id, COUNT(*) from links GROUP BY product_id HAVING COUNT(*) > 1
To see all entry with the count of the product_id , you can do following
SELECT l1.product_id , last_change , Count_
FROM links l1
JOIN (SELECT product_id, COUNT(*) as Count_ from links GROUP BY product_id HAVING COUNT(*) > 1) l2
ON l1.product_id = l2.product_id
Try below statement
select id, product_id, count(product_id)
from links
group by (product_id)
having count(product_id)> 1;

SQL select max from specified column

I need to show data max from specified colum, is this even possible?
I am still newbie on this SQL.
I have data like this
Id Order_id Sequence
------------------------
1 A01 1
2 A01 2
3 A02 1
4 A02 2
5 A02 3
6 A03 1
Id is the primary key.
Expected result
Id Order_id Sequence
------------------------
2 A01 2
5 A02 3
6 A03 1
You need to do a sub-query:
SELECT t.id, t.order_id, t.sequence
FROM t
INNER JOIN
(SELECT order_id, MAX(sequence) as mx
FROM t
GROUP BY order_id) sub
ON t.order_id = sub.order_id
WHERE t.sequence = sub.mx
So basically the query called sub finds the max sequence value for each order ID. Then these results are joined back the main table, which I'm calling t, so you can slap on the id.
Use GROUP BY and ORDER BY
SELECT Id,Order_id,MAX(Sequence) AS Sequence FROM table_name GROUP BY Order_id ORDER BY Id
If you don't need the id in the result, you can just do a group by... otherwise you need an inner query as the answer provided by #kbball.
select order_id, max(sequence)
from tablename
Group by order_id;
You can use correlated subquery as below.
SELECT *
FROM table1 t1
WHERE t1.Sequence =
( SELECT max(t2.Sequence)
FROM table1 t2
WHERE t1.Order_id = t2.Order_id );
Result:
Id Order_id Sequence
------------------------
2 A01 2
5 A02 3
6 A03 1
DEMO

Query to only retrieve records that are not duplicated

This is my table structure for product
product_id product_name
---------------------------------
100 Mouse
101 Keyboard
101 Pendrive
102 Motherboard
102 Card Reader
103 Adapter
I want the query which will give me following output by eliminating the duplicate record
OUTPUT
------------------
100
103
I have tried this query
SELECT count(product_id)
FROM product
GROUP BY product_id
Try this:
SELECT product_id
FROM product
GROUP BY product_id
HAVING COUNT(product_id) = 1;
You can try this SQL
SELECT t1.product_id FROM (SELECT product_id, COUNT(*) AS cnt FROM table GROUP BY product_id) t1 WHERE t1.cnt = 1

Joining multiple Rows and summing them With MySQL (and PHP)

I have a table structure similar to this:
id | order1_id | order1_type | order1_amount | order2_id | order2_type | order2_amount
------------------------------------------------------------------------------------------
1 1 3 4 1 4 5
2 2 1 1 1 3 2
3 1 4 4 2 2 1
I want to get the data like this:
order_id | order_type | order_amount
1 3 6
1 4 9
2 1 1
2 2 1
I want to group by type, and sum the order amounts. How can I do that ?
Thanks,
I'm going to use a union in a subquery to line the columns up, then group and sum on that.
Assuming you're stuck with this less-than-ideal table structure, you may want to create a view that represents the subquery below, then run the group by/sum against that view. I'm guessing such a view might be useful in more places than just this one query.
select t.order_id, t.order_type, sum(t.order_amount)
from (select order1_id as order_id, order1_type as order_type, order1_amount as order_amount
from orders
union all
select order2_id as order_id, order2_type as order_type, order2_amount as order_amount
from orders
union all
select order3_id as order_id, order3_type as order_type, order3_amount as order_amount
from orders
union all
select order4_id as order_id, order4_type as order_type, order4_amount as order_amount
from orders
union all
select order5_id as order_id, order5_type as order_type, order5_amount as order_amount
from orders) t
group by t.order_id, t.order_type
The easy way would be to use a view to untangle the table of questionable design, then group and sum on the view.
create view normalized_orders as
select order1_id as order_id,
order1_type as order_type,
order1_amount as order_amount
from your_table
union all
select order2_id as order_id,
order2_type as order_type,
order2_amount as order_amount
from your_table
Then you can do this:
select order_id, order_type, sum(order_amount)
from normalized_orders
group by order_id, order_type
order by order_id, order_type