Complex SQL query with group by and having in condition - mysql

Suppose I have the table test below:
------------------------------
id | active| record
------------------------------
3 | O | 2015-10-16
3 | O | 2015-10-15
3 | N | 2015-10-14
4 | N | 2015-10-15
4 | O | 2015-10-14
I want to do an update on the table on the lines with:
- An id having the column active = 'O' more than once.
- Among theses lines having active = 'O' more than once, the update shall change the value of active to 'N', except for the one with max(record), which will stay with active = 'O'.
In my example, the id having the column active = 'O' more than once is id = 3.
id |active | record
------------------------------
3 | O | 2015-10-16
3 | O | 2015-10-15
3 | N | 2015-10-14
I want to have this result:
id |active | record
------------------------------
3 | O | 2015-10-16
3 | N | 2015-10-15
3 | N | 2015-10-14
I tried this query, but there is an error:
update test as t1,
(select id
from test
where active = 'O'
group by id
having count(*) > 1) as t2
set t1.actif = 'N'
where t1.record != max(t2.record);
Thanks in advance!

Given this sample data:
CREATE TABLE t
(`id` int, `active` varchar(1), `record` date)
;
INSERT INTO t
(`id`, `active`, `record`)
VALUES
(3, 'O', '2015-10-16'),
(3, 'O', '2015-10-15'),
(3, 'N', '2015-10-14'),
(4, 'N', '2015-10-15'),
(4, 'O', '2015-10-14')
;
This query
UPDATE
t
JOIN (
SELECT
id, MAX(record) AS max_record
FROM
t
WHERE active = 'O'
GROUP BY id
HAVING COUNT(*) > 1
) sq ON t.id = sq.id
SET t.active = IF(t.record = sq.max_record, 'O', 'N');
produces this result:
+------+--------+------------+
| id | active | record |
+------+--------+------------+
| 3 | O | 2015-10-16 |
| 3 | N | 2015-10-15 |
| 3 | N | 2015-10-14 |
| 4 | N | 2015-10-15 |
| 4 | O | 2015-10-14 |
+------+--------+------------+

Can you try with something like this
select ID,
count(*) Counted,
max(record) record
into #TempTable from Table
where Active = 'O'
group by ID
Update tab
set tab.Active = 'N'
from Table tab
join #tempTable temp on tab.ID = temp.ID
where temp.Counted > 1 and
tab.record != temp.record
drop table #tempTable
Basically, you just counting Os while grabbing ID and max record into temp table and after that you doing the update, also this code might need some changes as i just took a glance to point you toward direction i would do it

Related

Is there a way to UPDATE column values based another column's value?

Part 1 of my SQL task involves restructuring data. The jist of my task is as follows: Based on the event_type, if it is "begin" I am trying to use that "time" to find it's stopping time (in another row) and add it to a column (event_end) on the same row as the start time so that all the data for an event sits nicely in one row.
pID customerID locationID event_type time event_end (new row)
1 1 a begin 12.45
2 2 a begin 11.10
3 1 a stop 1.30
4 2 b begin 9.45
5 3 b stop 8.78
I would like to add another column (event_end), and have event_end = the minimum value of event_start IF event_start = 'stop', IF locationID = locationID, and IF customerID = customerID. The final step would be to delete all event_start 'begin' rows.
I have tried UPDATE SET WHERE sequences, and a little bit of CASE, but my issue is that I cannot wrap my head around how to perform this without a loop like VBA. The following is my best stab at it:
UPDATE table
SET event_end = MIN(time)
WHERE event_type = 'stop'
WHERE customerid = customerid
WHERE locationid = locationid
WHERE time > time
SELECT *
FROM table
I'm hoping to have a table with all event data in one row, not spread out over multiple rows. If this is a handful, I appologize but am thankful in advance.
Thanks
Problem Statement:
Add event_end as an extra attribute to the existing row, data will be populated based on customer_id, location_id.
We will populate data in event_end to all events which have event type as begin
Data would be picked from rows which have the same customer_id, location_id but event type as stop.
Finally, we will remove all events with type stop.
Solution: Consider your table name is customer_events and will use self join concept for the same.
First, identify which records needs to be updated. We can use a SELECT query to identify such records.
c1 table will represent rows with begin event type.
c2 table will represent rows with stop event type.
SELECT *
FROM customer_events c1
LEFT JOIN customer_events c2 ON c1.customerID = c2.customerID AND c1.locationID = c2.locationID AND c1.event_type = 'begin' AND c2.event_type = 'stop'
WHERE c1.event_type = 'begin'; -- As we want to populate data in events with value as `begin`
Write a query to update the records.
UPDATE customer_events c1
LEFT JOIN customer_events c2 ON c1.customerID = c2.customerID AND c1.locationID = c2.locationID AND c1.event_type = 'begin' AND c2.event_type = 'stop'
SET c1.event_end = c2.`time`
WHERE c1.event_type = 'begin';
Now every record with event type as begin has either value in event_end column or it would be null if no records match as stop event.
For rows with event type as stop, either they are mapped with some row with event type as begin or some are not mapped. In both cases, we don't want to keep them. To remove all records with event type as stop.
DELETE FROM customer_events
WHERE event_type = 'stop';
Note: Don't run DELETE statement unless you are sure that this solution will work for you.
Updated: We can have multiple records of begin & stop events for single customer & location.
Sample Input:
| pID | customerID* | *locationID* | *event_type* | *time* | *event_end* |
| 1 | 1 | a | begin | 02:45:00 | |
| 2 | 2 | a | begin | 03:10:00 | |
| 3 | 1 | b | begin | 04:30:00 | |
| 4 | 2 | b | begin | 05:45:00 | |
| 5 | 2 | a | stop | 06:49:59 | |
| 6 | 1 | a | begin | 07:38:00 | |
| 7 | 3 | b | begin | 08:57:19 | |
| 8 | 2 | b | stop | 09:57:43 | |
| 9 | 3 | b | stop | 10:58:03 | |
| 10 | 4 | a | begin | 11:58:34 | |
| 11 | 1 | a | stop | 12:09:36 | |
| 12 | 1 | b | stop | 13:09:50 | |
| 13 | 1 | a | stop | 14:10:02 | |
Query:
SELECT *
FROM (
SELECT
ce.*,
IF(#c_id <> ce.customerId OR #l_id <> ce.locationID, #rank:= 1, #rank:= #rank + 1 ) as rank,
#c_id:= ce.customerId,
#l_id:= ce.locationID
FROM customer_events ce,
(SELECT #c_id:= 0 c, #l_id:= '' l, #rank:= 0 r) AS t
WHERE event_type = 'begin'
ORDER BY customerId, locationID, `time`) AS c1
LEFT JOIN (
SELECT
ce.*,
IF(#c_id <> ce.customerId OR #l_id <> ce.locationID, #rank:= 1, #rank:= #rank + 1 ) as rank,
#c_id:= ce.customerId,
#l_id:= ce.locationID
FROM customer_events ce,
(SELECT #c_id:= 0 c, #l_id:= '' l, #rank:= 0 r) AS t
WHERE event_type = 'stop'
ORDER BY customerId, locationID, `time`
) AS c2 ON c1.customerID = c2.customerID AND c1.locationID = c2.locationID AND c1.rank = c2.rank;
Output:
| pId | customerID| locationId| event_type| Start_Time|End_Id| End_Time |
| 1 | 1 | a | begin | 02:45:00 | 11 | 12:09:36 |
| 6 | 1 | a | begin | 07:38:00 | 13 | 14:10:02 |
| 3 | 1 | b | begin | 04:30:00 | 12 | 13:09:50 |
| 2 | 2 | a | begin | 03:10:00 | 5 | 06:49:59 |
| 4 | 2 | b | begin | 05:45:00 | 8 | 09:57:43 |
| 7 | 3 | b | begin | 08:57:19 | 9 | 10:58:03 |
| 10 | 4 | a | begin | 11:58:34 | | |
Update Statement: Create two columns end_pID and event_end for migration.
UPDATE customer_events
INNER JOIN (
SELECT c1.pId, c2.pID End_Id, c2.time AS End_Time
FROM (
SELECT
ce.*,
IF(#c_id <> ce.customerId OR #l_id <> ce.locationID, #rank:= 1, #rank:= #rank + 1 ) as rank,
#c_id:= ce.customerId,
#l_id:= ce.locationID
FROM customer_events ce,
(SELECT #c_id:= 0 c, #l_id:= '' l, #rank:= 0 r) AS t
WHERE event_type = 'begin'
ORDER BY customerId, locationID, `time`) AS c1
LEFT JOIN (
SELECT
ce.*,
IF(#c_id <> ce.customerId OR #l_id <> ce.locationID, #rank:= 1, #rank:= #rank + 1 ) as rank,
#c_id:= ce.customerId,
#l_id:= ce.locationID
FROM customer_events ce,
(SELECT #c_id:= 0 c, #l_id:= '' l, #rank:= 0 r) AS t
WHERE event_type = 'stop'
ORDER BY customerId, locationID, `time`
) AS c2 ON c1.customerID = c2.customerID AND c1.locationID = c2.locationID AND c1.rank = c2.rank) AS tt ON customer_events.pID = tt.pId
SET customer_events.end_pID = t.End_Id, customer_events.event_end = t.End_Time;
Finally, remove all events with event_type = 'stop'

Select max values from double joined table

In DB i have three tables like:
+-----------------+--------+
| ident (PrimKey) | ident2 |
+-----------------+--------+
| 123 | 333 |
| 321 | 334 |
| 213 | 335 |
| 1234 | 336 |
+-----------------+--------+
+---------+----------+-------+-------+
| PrimKey | group_id | value | ident |
+---------+----------+-------+-------+
| 1 | 1 | 10 | 213 |
| 2 | 1 | 5 | 321 |
| 3 | 1 | 15 | 1234 |
| 4 | 1 | 10 | 1234 |
| 5 | 2 | 7 | 213 |
| 6 | 2 | 15 | 321 |
+---------+----------+-------+-------+
+---------+----------+----------+
| PrimKey | ident2_1 | ident2_2 |
+---------+----------+----------+
| 1 | 333 | 334 |
| 2 | 333 | 335 |
| 3 | 333 | 336 |
+---------+----------+----------+
The third table is connection between two rows from first one. And Second Contains data from different group of this rows.
I have to find max values from second table grouped by group_id for specific user connected rows from third table. In example 333.
The correct answer should be:
+----------+-------+-------+
| group_id | value | ident |
+----------+-------+-------+
| 1 | 15 | 1234 |
| 2 | 15 | 321 |
+----------+-------+-------+
But for now i have all rows sorted:
+----+----------+-------+-------+
| | group_id | value | ident |
+----+----------+-------+-------+
| 1 | 1 | 15 | 1234 |
| 2 | 1 | 10 | 213 |
| 3 | 1 | 5 | 321 |
| 4 | 2 | 15 | 321 |
| 5 | 2 | 10 | 1234 |
| 6 | 2 | 7 | 213 |
+----+----------+-------+-------+
Or correct rows with incorrect ident's
+----+----------+-------+-------+
| | group_id | value | ident |
+----+----------+-------+-------+
| 1 | 1 | 15 | 213 |
| 2 | 2 | 15 | 1234 |
+----+----------+-------+-------+
The sql is :
DROP TABLE first;
DROP TABLE second;
DROP TABLE third;
CREATE TABLE first(group_id integer, value integer, ident integer);
CREATE TABLE second(ident integer, ident2 integer);
CREATE TABLE third(ident_1 integer, ident_2 integer);
INSERT INTO first VALUES(1, 10, 213);
INSERT INTO first VALUES(1, 5, 321);
INSERT INTO first VALUES(1, 15, 1234);
INSERT INTO first VALUES(2, 10, 1234);
INSERT INTO first VALUES(2, 7, 213);
INSERT INTO first VALUES(2, 15, 321);
INSERT INTO second VALUES(123, 333);
INSERT INTO second VALUES(321, 334);
INSERT INTO second VALUES(213, 335);
INSERT INTO second VALUES(1234, 336);
INSERT INTO third VALUES (333, 334);
INSERT INTO third VALUES (333, 335);
INSERT INTO third VALUES (333, 336);
SELECT f.group_id, max(f.value) as value, f.ident
FROM first as f
INNER JOIN second AS s ON f.ident = s.ident
INNER JOIN third AS t ON t.ident_2 = s.ident2
WHERE t.ident_1 = '333'
GROUP BY f.group_id
ORDER BY f.group_id ASC, f.value DESC;
SELECT f.group_id, f.value as value, f.ident
FROM first as f
INNER JOIN second AS s ON f.ident = s.ident
INNER JOIN third AS t ON t.ident_2 = s.ident2
WHERE t.ident_1 = '333'
ORDER BY f.group_id ASC, f.value DESC;
Tested on: https://rextester.com/l/mysql_online_compiler
Greetings
EDIT:
The third table is something like connection between friends with are rows in second table. And the first table is contain scores for different tasks with are identified by group_id. I need the best friends scores for different tasks. So list of friends i have from third table. And scores i have from first one. Connection between this tables is second one.
EDIT2:
In First table as Primary Key is ident(PrimKey).
The second and third as primary key have just another column.
In Second column ident column is index connected to ident (PrimKey) from first table.
In Third table columns ident2_1 and ident2_2 are indexes connected to indet2 from first table.
I came up with this SQL:
SELECT f.group_id, f.value as value, f.ident
FROM first as f
INNER JOIN second AS s
ON f.ident = s.ident
INNER JOIN third AS t
ON t.ident_2 = s.ident2
WHERE t.ident_1 = '333'
and f.value IN ( SELECT MAX(f1.value)
FROM first as f1
WHERE f1.group_id = f.group_id )
ORDER BY f.group_id ASC, f.value DESC;
There is surely a more elegant solution, but this will give the result you asked for.
select m.*
from(
select f.group_id, f.value value, f.ident
from first f,
second s,
third t
where f.ident = s.ident
and t.ident_2 = s.ident2
and t.ident_1 = '333'
ORDER BY f.group_id ASC, f.value DESC ) m,
(
select max(f.value) value
from first f,
second s,
third t
where f.ident = s.ident
and t.ident_2 = s.ident2
and t.ident_1 = '333' ) n
where m.value = n.value
here is a second (also clunky) technique that delivers the desired results:
select f.group_id, f.value value, f.ident
from first f,
second s,
third t
where f.ident = s.ident
and t.ident_2 = s.ident2
and t.ident_1 = '333'
and value = ( select max(f.value) value
from first f,
second s,
third t
where f.ident = s.ident
and t.ident_2 = s.ident2
and t.ident_1 = '333' )
ORDER BY f.group_id ASC, f.value DESC

mysql random selection in inner join

Question Mysql Random Row Query on Inner Join is much the same as mine but it was never answered.
I have a master table m and slave s. S contains 1 to many rows for each m. I would like a query that selects every master row joined to exactly one randomly chosen slave.
If the table schemas were:
M
---
id
S
---
id
mid
then, in pseudo code the query would be:
select * from m inner join s on m.id = s.mid where s.id is one randomly chosen from the values that exist
Can this be translated into real SQL?
I think the following query does the required job but using a subquery (not inner join):
SELECT *, (SELECT id FROM S WHERE S.mid = M.id ORDER BY RAND() LIMIT 1) AS S_id
FROM M
Here is a link to test it.
Hope it helps.
This can be solved using Row_Number() concept. We need to randomly assign row number values within a partition of mid in the table s. And, do a Join from the m table to s using mid and row_number = 1. This will pick a single Random row everytime.
In MySQL version below 8, we can use User-defined Variables to emulate Row_Number(). To understand how this works, you may check this answer for the explanation: https://stackoverflow.com/a/53465139/2469308
Note that this technique will be efficient on Large tables than using a Subquery (in the SELECT clause), as it will be doing overall table Sorting only once
View on DB Fiddle
create table m (id int, m_nm varchar(10));
create table s (id int,
mid int references m(mid),
s_nm varchar(10));
insert into m values(1, "a");
insert into m values(2, "b");
insert into m values(3, "c");
insert into s values(1, 1, "aa");
insert into s values(2, 1, "aa");
insert into s values(3, 2, "bb");
insert into s values(4, 2, "bbb");
insert into s values(5, 2, "bbbb");
insert into s values(6, 3, "cc");
insert into s values(7, 3, "ccc");
Query
SELECT
m.*, s_dt.id, s_dt.mid, s_dt.s_nm
FROM
m
JOIN
(
SELECT
#rn := IF(#m = dt.mid, #rn+1, 1) AS row_num,
#m := dt.mid AS mid,
dt.id,
dt.s_nm
FROM
(
SELECT
id, mid, s_nm, RAND() as rand_num
FROM s
ORDER BY mid, rand_num ) AS dt
CROSS JOIN (SELECT #rn:=0, #m:=0) AS user_vars
) AS s_dt
ON s_dt.mid = m.id AND
s_dt.row_num = 1;
Result (Run #1)
| id | m_nm | id | mid | s_nm |
| --- | ---- | --- | --- | ---- |
| 1 | a | 2 | 1 | aa |
| 2 | b | 5 | 2 | bbbb |
| 3 | c | 7 | 3 | ccc |
Result (Run #2)
| id | m_nm | id | mid | s_nm |
| --- | ---- | --- | --- | ---- |
| 1 | a | 1 | 1 | aa |
| 2 | b | 4 | 2 | bbb |
| 3 | c | 6 | 3 | cc |
Result (Run #3)
| id | m_nm | id | mid | s_nm |
| --- | ---- | --- | --- | ---- |
| 1 | a | 1 | 1 | aa |
| 2 | b | 3 | 2 | bb |
| 3 | c | 7 | 3 | ccc |
MySQL 8.0.2+ / MariaDB 10.3+ solution would be simply the following:
SELECT
m.*, s_dt.id, s_dt.mid, s_dt.s_nm
FROM
m
JOIN
(
SELECT
s.*,
ROW_NUMBER() OVER w AS row_num
FROM s
WINDOW w AS (PARTITION BY mid
ORDER BY RAND())
) AS s_dt
ON s_dt.mid = m.id AND
s_dt.row_num = 1
View on DB Fiddle

Get last mysql record only from a column

This is my existing table
id name version
| 1 | a | 1.1 |
| 2 | b | 2.1 |
| 3 | c | 3.1 |
| 4 | d | 1.2 |
| 5 | e | 4.1 |
how can I write a query to generate results where i will return all records but only the last record in the column version is selected like below?
id name version
| 4 | d | 1.2 |
| 2 | b | 2.1 |
| 3 | c | 3.1 |
| 5 | e | 4.1 |
If you prefer a slightly less laborious solution...
SELECT x.*
FROM t x
JOIN
( SELECT MAX(grade) grade
FROM t
GROUP
BY FLOOR(grade)
) y
ON y.grade = x.grade
http://sqlfiddle.com/#!9/f17db1/16
This is a bit laborious but it can be done
SELECT
SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY REPLACE(grade,'.','')*1 DESC),',',1) as id,
SUBSTRING_INDEX(GROUP_CONCAT(letter ORDER BY REPLACE(grade,'.','')*1 DESC),',',1) as letter,
MAX(grade) as grade
FROM
t
GROUP BY SUBSTRING_INDEX(grade,'.',1)
ORDER BY REPLACE(grade,'.','')*1
Assuming the last column is float you can use ORDER BY lastcol directly
FIDDLE
CREATE TABLE t
(`id` int, `letter` varchar(7), `grade` varchar(55))
;
INSERT INTO t
VALUES
(1, 'a', '1.1'),
(2, 'b', '2.1'),
(3, 'c', '3.1'),
(4, 'd', '1.2'),
(5, 'e', '4.1')

MySQL riddle with subquery

EDIT: looks like an index issue, update at the bottom of the question
I have the following query + subquery whose results I cannot explain. I am starting with this minimal input data set (the application here is capturing data change, and the PK is the id + the tx_id).
mysql> select * from tag_version;
+----+-------------------+------------+-------+----------------+
| id | name | article_id | tx_id | operation_type |
+----+-------------------+------------+-------+----------------+
| 1 | some tag | 1 | 1 | 0 |
| 1 | updated tag | 1 | 2 | 1 |
| 1 | updated again tag | 1 | 3 | 1 |
| 2 | other tag | 1 | 2 | 0 |
+----+-------------------+------------+-------+----------------+
4 rows in set (0.00 sec)
The subquery, standalone
SELECT max(f.tx_id) as max_tx_id, f.id
from tag_version f
WHERE f.tx_id <= 2
GROUP BY f.id
Result is
+-----------+----+
| max_tx_id | id |
+-----------+----+
| 2 | 1 |
| 2 | 2 |
+-----------+----+
2 rows in set (0.00 sec)
The query, where I manually inject the subquery results, notice how they are equal to the above
select t.*
from tag_version t
where t.article_id = 1
AND (t.tx_id, t.id) IN (
(2,1),
(2,2)
)
With expected results
+----+-------------+------------+-------+----------------+
| id | name | article_id | tx_id | operation_type |
+----+-------------+------------+-------+----------------+
| 1 | updated tag | 1 | 2 | 1 |
| 2 | other tag | 1 | 2 | 0 |
+----+-------------+------------+-------+----------------+
2 rows in set (0.00 sec)
And lastly, using the subquery in place of the tuples...
select t.*
from tag_version t
where t.article_id = 1
AND (t.tx_id, t.id) IN (
SELECT max(f.tx_id) as tx_id, f.id
from tag_version f
WHERE f.tx_id <= 2
GROUP BY f.id
)
The result is Empty set (0.00 sec)! Can someone explain this? I get the same empty results when I re-write the query using EXISTS instead of IN
I noticed than when I remove the line WHERE f.tx_id <= 2 from the subquery, I actually get results (although the wrong ones):
+----+-------------------+------------+-------+----------------+
| id | name | article_id | tx_id | operation_type |
+----+-------------------+------------+-------+----------------+
| 1 | updated again tag | 1 | 3 | 1 |
| 2 | other tag | 1 | 2 | 0 |
+----+-------------------+------------+-------+----------------+
2 rows in set (0.00 sec)
Replacing the subquery with a JOIN actually returns the expected correct results
SELECT t.*
FROM tag_version t
JOIN (
SELECT max(f.tx_id) as max_tx_id, f.id
from tag_version f
WHERE f.tx_id <= 2
GROUP BY f.id
) as max_ids
ON max_ids.max_tx_id = t.tx_id
AND max_ids.id = t.id
where t.article_id = 1
Result:
+----+-------------+------------+-------+----------------+
| id | name | article_id | tx_id | operation_type |
+----+-------------+------------+-------+----------------+
| 1 | updated tag | 1 | 2 | 1 |
| 2 | other tag | 1 | 2 | 0 |
+----+-------------+------------+-------+----------------+
2 rows in set (0.00 sec)
In addition, running the same query+subquery on the same data set with both PostgreSQL and SQLite gives the expected correct results.
My MySQL version is Server version: 5.5.40-0ubuntu0.14.04.1 (Ubuntu).
I think the clue to figuring out what's happening is that I actually get results when I remove the WHERE from the subquery, but I can't make something useful out of it.
EDIT: updated with input data set
EDIT: add table information
The table create statement is as follows
CREATE TABLE `tag_version` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`article_id` int(11) DEFAULT NULL,
`tx_id` bigint(20) NOT NULL,
`operation_type` smallint(6) NOT NULL,
PRIMARY KEY (`id`,`tx_id`),
KEY `ix_tag_version_operation_type` (`operation_type`),
KEY `ix_tag_version_tx_id` (`tx_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Data population as well
insert into tag_version (id, name, article_id, tx_id, operation_type) VALUES
(1, 'some tag', 1, 1, 0),
(1, 'updated tag', 1, 2, 1),
(1, 'updated again tag', 1, 3, 1),
(2, 'other tag', 1, 2, 0)
;
When I remove the ix_tag_version_tx_id index, the query returns the correct results... An explanation of why would be useful.
I believe you made a mistake in showing the result of first code(subquery).
The output of this query:
SELECT max(f.tx_id) as qwer, f.id
from tag_version f
WHERE f.tx_id <= 2
GROUP BY f.id
-is not:
+--------------+----+
| max(f.tx_id) | id |
+--------------+----+
| 2 | 1 |
| 2 | 2 |
+--------------+----+
It is:
+------+----+
| qwer | id |
+------+----+
| 2 | 1 |
| 2 | 2 |
+------+----+
(*Notice: the code line max(f.tx_id) as qwer *)
Now try this code for the expexted output.
There is a change when selecting max(f.tx_id).
select t.*
from tag_version t
where t.`article_id` = 1
AND t.`operation_type` != 2
AND (t.`tx_id`, t.`id`) IN (
SELECT max(f.`tx_id`) as `tx_id`, f.`id`
from tag_version f
WHERE f.`tx_id` <= 2
GROUP BY f.`id`
)
Let me know if this gives you the result or any other error.