I have a table like so (I'm not sure how to format tables)
Category / Products / Purchases
1 | A | 12
1 | B | 13
1 | C | 11
2 | A | 1
2 | B | 2
2 | C | 3
Expected output:
1 | B | 13
2 | C | 3
However I keep on getting
1 | A | 13
2 | A | 3
ie. It just selects the first occurrence of the second column.
Here is my code:
SELECT Category, Products, MAX(Purchases) FROM myTable GROUP BY Category;
Use filtering in the where clause:
select t.*
from t
where t.purchases = (select max(t2.purchases) from t t2 where t2.category = t.category);
With NOT EXISTS:
select m.* from myTable m
where not exists (
select 1 from myTable
where category = m.category and purchases > m.purchases
)
See the demo.
Results:
| Category | Products | Purchases |
| -------- | -------- | --------- |
| 1 | B | 13 |
| 2 | C | 3 |
You can use row_number() to identify max purchase for each group or replace rownumber() to rank() if there are ties of max purchases for each group
Select Category, Products,
Purchases from (Select Category,
Products,
Purchases,
row_number() over (partition by
category, products order by
purchases desc) rn from table) t
where t.rn=1
)
Related
I need to find the sum of occurrences of ids in a join table. The id could be present in two different columns (id_type_1 and id_type_2).
Table types
id | name
1 | Test1
2 | Test2
3 | Test3
Table products
id | name | id_type_1 | id_type_2
1 | Product1 | 1 | 2
2 | Product2 | 3 | 1
3 | Product3 | 1 | 3
I need to get a result like this:
Type | Total
Test1 | 3
Test2 | 1
Test3 | 2
Here's my query, but it takes several seconds to execute:
SELECT t.name,
(SELECT COUNT(p.id) FROM products p WHERE p.id_type_1 = t.id || p.id_type_2 = t.id) AS total
FROM types t
WHERE 1
ORDER BY total DESC
Is there a more effective way to achieve the result?
Join the tables and aggregate:
select t.id, t.name,
sum((t.id = p.id_type_1) + (t.id = p.id_type_2)) Total
from types t inner join products p
on t.id in (p.id_type_1, p.id_type_2)
group by t.id, t.name
If there is no case for the id to exist in both id_type_1 and id_type_2 in the same row then:
select t.id, t.name,
count(*) Total
from types t inner join products p
on t.id in (p.id_type_1, p.id_type_2)
group by t.id, t.name
See the demo.
Results:
> id | name | Total
> -: | :---- | ----:
> 1 | Test1 | 3
> 2 | Test2 | 1
> 3 | Test3 | 2
I have 2 columns in my pricing table: car_id and price
I want to
Group the records with same car_ids and sort these groups by price in itself. (namely, all records with the same car_id must be listed consecutively.)
I want to sort these groups by minimum price they include.
For example,
car_id | price
---------------
1 | 2
1 | 5
2 | 3
2 | 1
2 | 10
3 | 7
3 | 3
3 | 8
should be sorted like this:
car_id | price
---------------
2 | 1
2 | 3
2 | 10
1 | 2
1 | 5
3 | 3
3 | 7
3 | 8
How can I write a query that performs this operation? I am using MySQL V5.6.
You may use a subquery since the 5.6 does not support the window functions.
select *,
(
select min(price)
from datatab d2
where d1.car_id = d2.car_id
) min_price
from datatab d1
order by min_price, car_id, price
DBFIDDLE DEMO
You can use a subquery that returns the min price for each car_id in the ORDER BY clause:
select t.*
from tablename t
order by
(select min(price) from tablename where car_id = t.car_id),
car_id,
price
See the demo.
Or join the table to the subquery that returns all the min prices:
select t.*
from tablename t inner join (
select car_id, min(price) minprice
from tablename
group by car_id
) m on m.car_id = t.car_id
order by
m.minprice,
t.car_id,
t.price
See the demo.
Results:
| car_id | price |
| ------ | ----- |
| 2 | 1 |
| 2 | 3 |
| 2 | 10 |
| 1 | 2 |
| 1 | 5 |
| 3 | 3 |
| 3 | 7 |
| 3 | 8 |
You can use window functions in the order by:
select p.*
from pricing p
order by min(price) over (partition by car_id),
car_id;
The car_id handles the case when multiple car_ids have the same minimum price.
In older versions of MySQL, you can do something similar with a subquery:
select p.*
from pricing p
order by (select min(p2.price) from pricing p2 where p2.car_id = p.car_id),
car_id;
I have table my_table which contains groups of categories, each category has initial budget (original_budget):
I am trying to add a new column balance so it contains the balance after reducing expense from the original_budget in each category group. Something like:
my try:
SELECT category, expense, original_budget, (original_budget-expense) AS balance
FROM my_table GROUP BY category order by `trans_date`
MySQL version: innodb_version 5.7.25
10.2.23-MariaDB
If you are using MySQL 8+, then it is fairly straightforward to use SUM here as a window function:
SELECT
trans_date,
category,
expense,
original_budget,
original_budget - SUM(expense) OVER
(PARTITION BY category
ORDER BY trans_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) balance
FROM my_table
ORDER BY
category,
trans_date;
Demo
On earlier versions of MySQL, we can try to compute the rolling balance using a correlated subquery:
SELECT
trans_date,
category,
expense,
original_budget,
original_budget - (SELECT SUM(t2.expense) FROM my_table t2
WHERE t1.category = t2.category AND
t2.trans_date <= t1.trans_date) balance
FROM my_table t1
ORDER BY
category,
trans_date;
Demo
For All MySQL versions:
You can use MySQL User defined Variable to reduce balance amount for a category. For this keep same category records together with sorted dates.
SELECT
category,
expense,
original_budget,
IF(#cat <> category, #budg:= original_budget - expense, #budg:= #budg - expense) AS balance,
#cat:= category -- Set category to current value so we can compare it in next iteration
FROM my_table,
(SELECT #cat:= '' AS c, #budg:= NULL AS b) AS t
ORDER BY category, `trans_date`;
Output:
| category | expense | original_budget | balance | #cat:= category |
| A | 10 | 100 | 90 | A |
| A | 2 | 100 | 88 | A |
| A | 1 | 100 | 87 | A |
| B | 12 | 300 | 288 | B |
| B | 1 | 300 | 287 | B |
| B | 1 | 300 | 286 | B |
| B | 1 | 300 | 285 | B |
I have the following table:
+----+-----------+-----------+
| id | teacherId | studentId |
+----+-----------+-----------+
| 1 | 1 | 4 |
| 2 | 1 | 2 |
| 3 | 1 | 1 |
| 4 | 1 | 3 |
| 5 | 2 | 2 |
| 6 | 2 | 1 |
| 7 | 2 | 3 |
| 8 | 3 | 9 |
| 9 | 3 | 6 |
| 10 | 1 | 6 |
+----+-----------+-----------+
I need a query to find two teacherId's with maximum number of common studentId's.
In this case teachers with teacherIds 1,2 have common students with studentIds 2, 1, 3, which is greater than 1,3 having common students 6.
Thanks in Advance!
[Edit]: After several hours I've had the following solution:
SELECT * FROM (
SELECT r1tid, r2tid, COUNT(r2tid) AS cnt
FROM (
SELECT r1.teacherId AS r1tid, r2.teacherId AS r2tid
FROM table r1
INNER JOIN table r2 ON r1.studentId=r2.studentId AND r1.teacherId!=r2.teacherId
ORDER BY r1tid
) t
GROUP BY r1tid, r2tid
ORDER BY cnt DESC
) t GROUP BY cnt ORDER BY cnt DESC LIMIT 1;
I was sure that there must exist more short and elegant solution, but I could not find it.
You would do this with a self-join. Assuming no duplicates in the table:
select t.teacherid, t2.teacherid, count(*) as NumStudentsInCommon
from table t join
table t2
on t.studentid = t2.studentid and
t.teacherid < t2.teacherid
group by t.teacherid, t2.teacherid
order by NumStudentsInCommon desc
limit 1;
If you had duplicates, you would just replace count(*) with count(distinct studentid), but count(distinct) requires a bit more work.
select t.teacherId, t2.teacherId, sum(t.studentId) as NumStudentsInCommon
from table1 t join
table1 t2
on t.studentId = t2.studentId and
t.teacherId < t2.teacherId
group by t.teacherId, t2.teacherId
order by NumStudentsInCommon desc
My apologies. I have edited the below into 2 table, Im just having a bit of confusion.
I have a tables very similar as the ones below and I wanted to show all the table2-class 1 but only 1 random item per each table1-category
Sample Item Table1
+---------+---------------+---------------+
| ID | Item Name | Category |
+---------+---------------+---------------+
| 01 | Item A | Cat 1 |
| 02 | Item B | Cat 1 |
| 03 | Item C | Cat 2 |
| 04 | Item D | Cat 2 |
| 05 | Item E | Cat 3 |
| 06 | Item F | Cat 3 |
+---------+---------------+---------------+
Sample Item Table2
+---------------+---------------+
| Category | Class |
+---------------+---------------+
| Cat 1 | 1 |
| Cat 2 | 1 |
| Cat 3 | 2 |
+---------------+---------------+
I wanted to show all the table2-class 1 but only 1 random item per each table1-category
Desired Result
+---------+---------------+---------------+
| 02 | Item B | Cat 1 |
| 03 | Item C | Cat 2 |
+---------+---------------+---------------+
(This is within my PHP script)
Thanks in advance
You can do something like this
SELECT t.id, itemname, category
FROM
(
SELECT
(
SELECT id
FROM table1
WHERE category = t.category
ORDER BY RAND()
LIMIT 1
) id
FROM table1 t
GROUP BY category
) q JOIN table1 t
ON q.id = t.id
Note: using RAND() is very costly
Output:
| ID | ITEMNAME | CATEGORY |
|----|----------|----------|
| 1 | Item A | Cat 1 |
| 3 | Item C | Cat 2 |
| 6 | Item F | Cat 3 |
Here is SQLFiddle demo
Try something like this:
SELECT id, itemname, category FROM (
SELECT id, itemname, category FROM sample_table
ORDER BY RAND()
) AS tmp
GROUP BY category
Note that this query is totally valid in MySQL
http://dev.mysql.com/doc/refman/5.0/en/group-by-extensions.html
The safest way to do this is with a correlated subquery. To get the item_id:
select category,
(select item_id from sample s where s2.category = s.category order by rand() limit 1) as item_id
from sample s
group by category;
To get the rest of the item information, join that back in:
select s.*
from (select category,
(select item_id from sample s where s2.category = s.category order by rand() limit 1) as item_id
from sample s
group by category
) c join
sample s
on s.item_id = c.item_id;
Prior to the above edited scenario, I used the below query and it works fine except that it doesn't randomize the entry of each category:
SELECT * FROM Table1,Table2
WHERE Table2.Class = '1'
AND Table1.Category = Table2.Category
GROUP BY Table1.Category ORDER BY RAND()