mysql group by, max, count implementaion - mysql

tables
items table
------------------
| id | item |
------------------
| 1 | pearl |
| 2 | ruby |
| 3 | gold |
| 4 | diamond |
------------------
transaction table
--------------------------------------------------
| item_id | sell_price | created_at |
--------------------------------------------------
| 1 | 10 | 2020-08-21 01:50:24 |
| 1 | 20 | 2020-08-22 05:51:10 |
| 3 | 20 | 2020-08-23 06:52:05 |
| 3 | 30 | 2020-08-24 22:55:07 |
| 4 | 50 | 2020-08-25 20:58:18 |
| 2 | 10 | 2020-08-26 12:59:55 |
--------------------------------------------------
expected output:
--------------------------------------------------------------------
| item_id | name | quantity | price | created_at |
--------------------------------------------------------------------
| 1 | pearl | 2 | 20 | 2020-08-22 05:51:10 |
| 2 | ruby | 1 | 10 | 2020-08-26 12:59:55 |
| 3 | gold | 2 | 30 | 2020-08-24 22:55:07 |
| 4 | diamond | 1 | 50 | 2020-08-25 20:58:18 |
--------------------------------------------------------------------
the query
SELECT
items.id AS item_id,
items.item,
COUNT(transaction.item_id) AS quantity,
MAX(transaction.price) AS sell_price
FROM transaction
LEFT JOIN items
ON transaction.item_id = items.id
GROUP BY transaction.item_id;
My query above works perfectly fine without transaction.created_at on select clause, but when I put select transaction.created_at it throws an error:
SELECT list is not in GROUP BY clause and contains nonaggregated column 'db.transaction.created_at' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

From the desired output you shared, it looks like you're trying to query the maximum created_at:
SELECT
items.id AS item_id,
items.item,
COUNT(transaction.item_id) AS quantity,
MAX(transaction.price) AS sell_price,
MAX(created_at) AS created_at -- Here!
FROM transaction
LEFT JOIN items
ON transaction.item_id = items.id
GROUP BY transaction.item_id;

When using GROUP BY, just list all columns that are in SELECT and that not inside an aggregate function.
SELECT
items.id AS item_id,
items.item,
COUNT(transaction.item_id) AS quantity,
MAX(transaction.sell_price) AS sell_price,
MAX(transaction.created_at) AS created_at
FROM transaction
LEFT JOIN items
ON transaction.item_id = items.id
GROUP BY items.id, items.item;
See db-fiddle.

Related

How Affect Group By to Other Second Join Table

I have some table like this
table request_buys
| id | invoice | user_id |
| -- | ----------------- | ------- |
| 3 | 20220405/01104298 | 1 |
table traces
| id | request_buy_id | status_id | created_at |
| -- | -------------- | --------- | ------------------- |
| 37 | 3 | 1 | 2022-03-27 14:12:25 |
| 38 | 3 | 2 | 2022-03-28 14:12:25 |
| 39 | 3 | 3 | 2022-03-29 14:12:25 |
| 40 | 3 | 4 | 2022-03-30 14:12:25 |
| 41 | 3 | 5 | 2022-03-31 14:12:25 |
| 42 | 3 | 6 | 2022-04-01 14:12:25 |
table statuses
| id | nama |
| -- | ----------------- |
| 1 | Order Placed |
| 2 | Order Paid |
| 3 | Accepted |
| 4 | Picked by Courier |
| 5 | In Transit |
| 6 | Delivered |
| 7 | Rated |
| 8 | Rejected |
| 9 | Canceled |
and then i try to design query like below
select
request_buys.invoice,
MAX(traces.id) as traces_id,
MAX(statuses.nama) as statuses_nama
from
`request_buys`
inner join `traces` on `request_buys`.`id` = `traces`.`request_buy_id`
inner join `statuses` on `traces`.`status_id` = `statuses`.`id`
where
`user_id` = 1
group by
request_buys.id
and produces output like the following
output
| invoice | traces_id | statuses_nama |
| ----------------- | --------- | ----------------- |
| 20220405/01104298 | 42 | Picked by Courier |
and the output i expect should be like in the table below
expect
| invoice | traces_id | statuses_nama |
| ----------------- | --------- | ----------------- |
| 20220405/01104298 | 42 | Delivered |
I understand my error is in MAX(statuses.nama) which I should change like removing MAX() in statuses.nama
But i just get error like this "SELECT list is not in GROUP BY clause and contains nonaggregated ... this is incompatible with sql_mode=only_full_group_by"
then I tried some to clear the value "ONLY_FULL_GROUP_BY" with a query like the following
SET sql_mode=(SELECT REPLACE(##sql_mode,'ONLY_FULL_GROUP_BY',''))
and the result is like this
output
| invoice | traces_id | statuses_nama |
| ----------------- | --------- | ----------------- |
| 20220405/01104298 | 42 | Order Placed |
and I'm really stuck at this
and how to make trace_id.status_id from the "GROUP BY" result based on request_buys.id still have a relationship with statuses.id
Your problem lies with your misuse of the MAX(statuses.nama) expression. Based on your expected output,you intend to get the statuses.nama which matches the MAX(traces.id), NOT the MAX(statuses.nama) value which returns the highest value in terms of alphabetic order. In this case, the initial letter 'P' > 'D' . I have tweaked your code a bit and tried it on workbench,supposing there are more than one invoice for a particular user.(e.g insert into request_buys values (4,'20230405/01104298',1); insert into traces values (43,4,7,'2022-04-01 14:12:25');) It works as intended.
select invoice, t.id as traces_id, s.nama as statuses_name from request_buys r
join traces t on r.id=t.request_buy_id
join statuses s on t.status_id=s.id
join
(select traces.request_buy_id, MAX(traces.id) as traces_id
from `request_buys`
inner join `traces` on `request_buys`.`id` = `traces`.`request_buy_id`
where
`user_id` = 1
group by
traces.request_buy_id ) join_t
on t.request_buy_id=join_t.request_buy_id and t.id=join_t.traces_id
;
If I'm understanding correctly, you're trying to retrieve the most recent status for each invoice. Using MAX(nama) won't return that result, because it just picks the maximum status name alphabetically.
Assuming you're using MySQL 8.x, use ROW_NUMBER() to sort and rank the statuses for each invoice, by the most recent date first. Then grab the latest one using where rowNum = 1
WITH cte AS (
SELECT rb.id AS request_buy_id
, rb.invoice
, t.id AS traces_id
, s.nama AS statuses_nama
, ROW_NUMBER() OVER(PARTITION BY rb.id ORDER BY t.created_at DESC) AS RowNum
FROM request_buys rb
INNER JOIN traces t ON rb.id = t.request_buy_id
INNER JOIN statuses s ON t.status_id = s.id
WHERE user_id = 1
)
SELECT *
FROM cte
WHERE RowNum = 1
;
Result:
request_buy_id
invoice
traces_id
statuses_nama
RowNum
3
20220405/01104298
42
Delivered
1
db<>fiddle here

mysql get ranking position grouped with joined table

I have two tables, the first is players
---------------------------------------------
| player_id | player_score | player_game_id |
---------------------------------------------
| 1 | 274 | 1 |
---------------------------------------------
| 2 | 281 | 1 |
---------------------------------------------
| 3 | 156 | 2 |
---------------------------------------------
| 4 | 199 | 2 |
---------------------------------------------
| 5 | 120 | 2 |
---------------------------------------------
And another table for games
-----------------------
| game_id | game_name |
-----------------------
| 1 | gameone |
-----------------------
| 2 | gametwo |
-----------------------
I would like to have this result
----------------------------------------------------
| player_id | player_score | player_rank | game_id |
----------------------------------------------------
| 2 | 281 | 1 | 1 |
----------------------------------------------------
| 1 | 274 | 2 | 1 |
----------------------------------------------------
| 4 | 199 | 1 | 2 |
----------------------------------------------------
| 3 | 156 | 2 | 2 |
----------------------------------------------------
| 5 | 120 | 3 | 2 |
----------------------------------------------------
Have anyone idea how to achieve this in mysql?
Thank you very much
You want rank() or row_number():
select p.*,
rank() over (partition by player_game_id order by player_score desc) as rank
from players
order by player_game_id, player_score desc;
If two players have the same score, then rank() gives them the same ranking. row_number() will arbitrarily assign them adjacent rankings.
The above works on MySQL 8+. In earlier versions you have two options -- subqueries and variables. Here is an example using a correlated subquery:
select p.*,
(select 1 + count(*)
from players p2
where p2.player_game_id = p.player_game_id and
p2.player_score > p.player_score
) as ranking
from players
order by player_game_id, player_score desc;
This is not as efficient as rank(). But with an index on players(player_game_id, player_score) and not too many players per game, then this should have reasonable performance.

mysql group by keeping the more recent date

I have the following table:
+------------+--------+-----+
| reg_dat | status | id |
+------------+--------+-----+
| 2016-01-31 | 10 | 1 |
| 2017-06-31 | 12 | 1 |
| 2015-01-31 | 12 | 4 |
| 2017-01-25 | 5 | 4 |
| 2017-01-11 | 3 | 2 |
+------------+--------+-----+
I would like to do a mysql query to group the rows by id and keeping only the more recent date... so the output should be the following:
+------------+--------+-----+
| reg_dat | status | id |
+------------+--------+-----+
| 2017-06-31 | 12 | 1 |
| 2017-01-25 | 5 | 4 |
| 2017-01-11 | 3 | 2 |
+------------+--------+-----+
Unfortunately my code doesn't work...
select *
from table
group by id
order by id, reg_dat DESC
Have you some suggestions?
You can do that using a JOIN and a subquery
SELECT t.reg_dat, t.status, t.id
FROM table t
JOIN (SELECT max(reg_dat) max_date, id FROM table GROUP BY id) t1
ON t.reg_dat = t1.max_date AND t.id = t1.id

Mysql count column values and merge columns

I was having problems in creating counting rows by grouping based on a given field value.
For example: I have a Table A structure like this:
+------+------------+
| id | Person |
+------+------------+
| 1 | "Sandy" |
| 2 | "Piper" |
| 3 | "Candy" |
| 4 | "Pendy" |
+------------+------+
Also I have a Table B structure like this:
+------+------------+---------+
| id | Person | Point |
+------+------------+---------+
| 1 | "Sandy" | 10 |
| 2 | "Piper" | 20 |
| 3 | "Candy" | 30 |
| 4 | "Sandy" | 10 |
| 5 | "Piper" | 20 |
| 6 | "Zafar" | 30 |
+------------+------+---------+
And needed a result like:
+------+------------+---------+
| id | Person | Point |
+------+------------+---------+
| 1 | "Piper" | 40 |
| 2 | "Candy" | 30 |
| 3 | "Zafar" | 30 |
| 4 | "Sandy" | 20 |
| 5 | "Pendy" | 0 |
+------------+------+---------+
I hope the table examples are itself self-explanatory.
SELECT person
, SUM(point) total
FROM
( SELECT person,point FROM table_b
UNION
ALL
SELECT person,0 FROM table_a
) x
GROUP
BY person
ORDER
BY total DESC;
It is a simple left join with a group by
select tableA.person, sum(tableB.points) from tableA left join tableB on tableA.person = tableB.person group by tableA.person
union
select tableB.person, sum(tableB.points) from tableB left join tableA on tableA.person = tableB.person where tableA.id is null group by tableA.person
I think below sql useful to you.
select a.id, a.Person,b.total_point from (
select id, Person from tablea) as a join
(select Person, sum(Point) as total_point from tableb group by person) as b on a.person =b.person
Thank you

How to get info from three tables with a MIN (MySQL)

I have four tables, like these:
items
| id | name | category |
-------------------------
| 1 | item1 | toy |
| 2 | item2 | toy |
| 3 | item3 | home |
-------------------------
items2
| id | name | category | size |
--------------------------------
| 1 | itemA | toy | s |
| 2 | itemB | home | l |
--------------------------------
prices
| items.id | price |
--------------------
| 1 | 10 |
| 1 | 15 |
| 2 | 20 |
| 3 | 25 |
| 3 | 20 |
--------------------
prices
| items2.id | price |
--------------------
| 1 | 15 |
| 2 | 50 |
| 2 | 40 |
--------------------
I need to get a result which have both, items and items2, with the MIN of each price. In this example the result should be something like this:
| id | name | category | size | minprice |
-------------------------------------------
| 1 | item1 | toy | null | 10 |
| 2 | item2 | toy | null | 20 |
| 3 | item3 | home | null | 20 |
| 1 | itemA | toy | s | 15 |
| 2 | itemB | home | l | 40 |
-------------------------------------------
I also should be able to ORDER BY minprice, but I'm sure when I know how to join them I can do that too.
Thanks in advance!
I would suggest that you merge the tables items and items2. If a field doesn't apply to an item (such as size in this case), that is what null is for.
I haven't tested that this works but you want something like:
SELECT i.id, name, category, null as size, min(prices.price) from items i
JOIN prices on prices.id = i.id
GROUP BY i.id, i.name, i.category, size
UNION ALL
SELECT i2.id, name, category, size, min(prices2.price) from items2 i2
JOIN prices2 on prices2.id = i2.id
GROUP BY i2.id, i2.name, i2.category, i2.size
From what I can tell, you are over complicating things. You only need one table. For tuples in the item relation where there is no size, simply mark it as null.
This is probably an oversimplification. If prices are "sales" or something similar, you could use a left join to build the relation you are looking for
so
SELECT i.id, i.name, i.category, i.size, p.price as min_price FROM items i LEFT JOIN price p where p.price <= 40 ORDER BY min_price