mysql select counter of current position [duplicate] - mysql

This question already has answers here:
Rank function in MySQL
(13 answers)
Closed 2 years ago.
Having a table like this
+--------+------------+
| orderID | productID |
+--------+------------+
| 100 | 10 |
| 100 | 11 |
| 101 | 10 |
| 101 | 11 |
| 101 | 12 |
+--------+------------+
I need to add a third column, which be counter of current position of product.
Like:
+--------+------------+--------------+
| orderID | productID | currentIndex |
+--------+------------+--------------+
| 100 | 10 | 1 |
| 100 | 11 | 2 |
| 101 | 10 | 1 |
| 101 | 11 | 2 |
| 101 | 12 | 3 |
+--------+------------+--------------+
Can help me, please?
I have now this query:
SELECT orderID, productID
FROM orders;

If you are running MySQL 8.0, `row_number() does exactly what you ask for:
select orderid, productid,
row_number() over(partition by orderid order by productid) currentindex
from orders;
In earlier versions, alternatives are either user variables, or a correlated subquery. I am going to recommend the second option - user variables are rather tricky to work with, and are now officially planned for future deprecation:
select orderid, productid,
(select count(*) from orders o1 where o1.orderid = o.orderid and o1.productid <= o.productid) currentindex
from orders o;

Related

How to select other columns of a table when grouping? [duplicate]

This question already has answers here:
SQL select only rows with max value on a column [duplicate]
(27 answers)
Closed 1 year ago.
Please assume this table:
// mytable
+--------+-------------+---------+
| num | business_id | user_id |
+--------+-------------+---------+
| 3 | 503 | 12 |
| 7 | 33 | 12 |
| 1 | 771 | 13 |
| 2 | 86 | 13 |
| 1 | 772 | 13 |
| 4 | 652 | 14 |
| 4 | 567 | 14 |
+--------+-------------+---------+
I need to group it based on user_id, So, here is my query:
select max(num), user_id from mytable
group by user_id
Here is the result:
// res
+--------+---------+
| num | user_id |
+--------+---------+
| 7 | 12 |
| 2 | 13 |
| 4 | 14 |
+--------+---------+
Now I need to also get the business_id of those rows. Here is the expected result:
// mytable
+--------+-------------+---------+
| num | business_id | user_id |
+--------+-------------+---------+
| 7 | 33 | 12 |
| 2 | 86 | 13 |
| 4 | 567 | 14 | -- This is selected randomly, because of the equality of values
+--------+-------------+---------+
Any idea how can I do that?
You don't group. You filter. One method uses window functions such as row_number():
select t.*
from (select t.*,
row_number() over (partition by user_id order by num desc) as seqnum
from mytable t
) t
where seqnum = 1;
Another method which can have slightly better performance with an index on (user_id, num) is a correlated subquery:
select t.*
from mytable t
where t.num = (select max(t2.num)
from mytable t2
where t2.user_id = t.user_id
);
You should think "group by" when you want to summarize rows. You should think "where" when you want to choose rows with particular characteristics.

Fetching last 2 rows of multiple user in MySQL [duplicate]

This question already has answers here:
Grouping by a Top N in MySQL
(2 answers)
Closed 2 years ago.
I have a table to list the transaction made by users.
mysql> select * from transaction;
+-----------+----------+------------+
| emp_id_fk | trans_id | trans_date |
+-----------+----------+------------+
| 1 | 1 | 2008-01-01 |
| 1 | 2 | 2019-01-01 |
| 1 | 3 | 2020-01-01 |
| 2 | 4 | 2020-01-10 |
| 2 | 5 | 2020-01-16 |
| 2 | 6 | 2020-01-15 |
+-----------+----------+------------+
6 rows in set (0.00 sec)
I want to know the last 2 transactions made by the users along with their transaction ID.
The output should look something like this.
+-----------+----------+------------+
| emp_id_fk | trans_id | trans_date |
+-----------+----------+------------+
| 1 | 2 | 2019-01-01 |
| 1 | 3 | 2020-01-01 |
| 2 | 5 | 2020-01-16 |
| 2 | 6 | 2020-01-15 |
+-----------+----------+------------+
I've tried inner joins and group by clause but of no use. How can I generate this output?
If you are running MySQL 8.0, you can use window fuctions:
select *
from (
select t.*, row_number() over(partition by emp_id_fk order by trans_date desc) rn
from transactions t
) t
where rn <= 2
order by emp_id_fk, trans_date
If there may be more than one transaction for a given customer on the same date, consider adding another sorting criteria to the order by clause of the window function, such as trans_id for example:
row_number() over(partition by emp_id_fk order by trans_date desc, , trans_id) rn desc
In older versions, you could use a correlated subquery:
select t.*
from transactionts t
where (
select count(*)
from transactions t1
where t1.trans_date >= t.trans_date and t1.emp_id_fk = t.emp_id_fk
) <= 2

SQL Max value in a group [duplicate]

This question already has answers here:
SQL select only rows with max value on a column [duplicate]
(27 answers)
Closed 3 years ago.
I'm struggling to do something in SQL which I'm sure must be simple, but I can't figure it out. I want the MAX() value of a group, but I also want the value of another column in the same row as the max value. Here is an example table definition:
mysql> desc Sales;
+---------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+---------+------+-----+---------+-------+
| StoreID | int(11) | YES | | NULL | |
| Day | int(11) | YES | | NULL | |
| Amount | int(11) | YES | | NULL | |
+---------+---------+------+-----+---------+-------+
3 rows in set (0.00 sec)
And here is some data for it:
mysql> SELECT * FROM Sales;
+---------+------+--------+
| StoreID | Day | Amount |
+---------+------+--------+
| 1 | 1 | 44 |
| 1 | 2 | 31 |
| 1 | 3 | 91 |
| 2 | 1 | 93 |
| 2 | 2 | 32 |
| 2 | 3 | 41 |
| 3 | 1 | 48 |
| 3 | 2 | 95 |
| 3 | 3 | 12 |
+---------+------+--------+
9 rows in set (0.00 sec)
What I want to know is, what Day had the most sales (Amount) for each StoreID.
Now I know I can do this:
SELECT StoreID, MAX(Amount) FROM Sales GROUP BY StoreID;
+---------+-------------+
| StoreID | MAX(Amount) |
+---------+-------------+
| 1 | 91 |
| 2 | 93 |
| 3 | 95 |
+---------+-------------+
3 rows in set (0.00 sec)
That tells me the max amount of each store, but really what I'm after is the day that it occured. But I can't add Day back in to the query because it's not in the group by, and I don't think I really want to group by that value do I?
I'm not sure where to go from here.
In short, the results I want should look like this:
+---------+------+--------+
| 1 | 3 | 91 |
| 2 | 1 | 93 |
| 3 | 2 | 95 |
+---------+------+--------+
You want to filter. Here is one simple method using a correlated subquery:
select s.*
from s
where s.sales = (select max(s2.sales)
from sales s2
where s2.storeId = s.storeId
);
If your data is on the large side, you will want an index on sales(storeId, sales).
For the maximum amounts per store there won't exist a higher amount for the same store.
SELECT *
FROM Sales s
WHERE NOT EXISTS (
SELECT 1
FROM Sales s2
WHERE s2.StoreID = s.StoreID
AND s2.Amount > s.Amount
)
ORDER BY Amount ASC, StoreID ASC;
Test here
Typically you can just join the aggregating query back to get the rest of the row data...
SELECT s.*
FROM Sales AS s
INNER JOIN (
SELECT StoreID, MAX(Amount) AS MaxAmount
FROM Sales
GROUP BY StoreID
) AS m ON s.StoreID = m.StoredID AND s.Amount = m.MaxAmount
;
If there are multiple Sales with the MaxAmount for the same StoreID, the query will return all of them, not just one of them.

SQL select count from multiple tables

I'm a starter at SQL and I have the following tables, ORDER_PRODUCTS, listing the products of an order and EXCHANGE_PRODUCTS, listing products that will be exchanged.
Both have the same fields, and I need to make a selection counting the amount of products in both tables, distinguishing them by the order_id, does anyone knows how I can do this?
ORDER_PRODUCTS
+-----+------------+----------+---------+
| id | product_id | order_id | amount |
+-----+------------+----------+---------+
| 1 | 5 | 1 | 2 |
| 2 | 7 | 1 | 1 |
| 3 | 13 | 5 | 1 |
| 4 | 18 | 8 | 3 |
| 5 | 45 | 11 | 4 |
+-----+------------+----------+---------+
EXCHANGE_PRODUCTS
+-----+------------+----------+---------+
| id | product_id | order_id | amount |
+-----+------------+----------+---------+
| 1 | 5 | 1 | 1 |
| 2 | 7 | 1 | 2 |
| 3 | 13 | 5 | 1 |
| 4 | 3 | 8 | 2 |
| 5 | 2 | 11 | 1 |
+-----+------------+----------+---------+
You want to use union all to combine the tables and then aggregate them. I might recommend:
select order_id, sum(ordered) as ordered, sum(exchanged) as exchanged,
sum(exchanged + ordered) as total
from ((select order_id, amount as ordered, 0 as exchanged
from order_products
) union all
(select order_id, 0 as ordered, amount as exchanged
from exhange_products
)
) oe
group by order_id;
It is important to use union all rather than union, because union removes duplicates (which can result in bad numbers). Union also incurs overhead that is unnecessary.
And, by "count amount" I assume you really mean to take the sum.
I think this query should do what you Need:
select sum(amount), order_id from (
select amount,order_id from order_products
union
select amount,order_id from Exchange_products)
group by order_id

how to update table rows using calculated results from another table in mysql [duplicate]

This question already has an answer here:
MySQL UPDATE with SELECT SUM from different table
(1 answer)
Closed 7 years ago.
I'm trying to update SubTotal field in Transactions table by summing up results from bridge table TransactionRecords. Following is what my table contains.
| Transaction |
| ID | SubTotal |
| 1 | ??? |
| 2 | ??? |
| 3 | ??? |
| TransactionRecords |
| TransactionID | ProductID | QTY | SalePrice |
| 1 | 10 | 4 | 19.99 |
| 2 | 5 | 8 | 9.99 |
| 2 | 3 | 12 | 14.99 |
What I want is to have Transaction.SubTotal equal to TransactionRecords.SalePrice * TransactionRecords.QTY where TransactionRecords.TransactionID equals to Transaction.ID
This query :
select TransactionID, sum(UnitPrice * QuantitySold)
from TransactionRecord
group by TransactionID;
gives me each transaction and sale amount made in each transaction and that's the value I want Transaction table to have in it's appropriate ID.
Try this:
UPDATE Transaction t
SET SubTotal = (SELECT SUM(UnitPrice * QuantitySold) FROM TransactionRecord tr WHERE tr.TransactionId = t.ID);