Remove duplicates leaving at least one with highest parameter from group - mysql

I have following schema:
+--+------+-----+----+
|id|device|token|cash|
+--+------+-----+----+
column device is unique and token is not unique and null by default.
What i want to achieve is to set all duplicate token values to default (null) leaving only one with highest cash. If duplicates have same cash leave first one.
I have heard about cursor, but it seems that it can be done with usual query.
I have tried following SELECT only to see if im right about my thought how to achieve this, but it seems im wrong.
SELECT
*
FROM
db.table
WHERE
db.table.token NOT IN (SELECT
*
FROM
(
SELECT DISTINCT
MAX(db.table.balance)
FROM
db.table
GROUP BY db.table.balance) temp
)
For example:
This table after query
+-----+---------+--------+-------+
| id | device | token | cash|
+-----+---------+--------+-------+
| 1 | dev_1 | tkn_1 | 3 |
| 2 | dev_2 | tkn_1 | 10 |
| 3 | dev_3 | tkn_2 | 10 |
| 4 | dev_4 | tkn_2 | 14 |
| 5 | dev_5 | tkn_3 | 10 |
| 6 | dev_6 | null | 10 |
| 7 | dev_7 | null | 10 |
| 8 | dev_8 | tkn_4 | 11 |
| 8 | dev_8 | tkn_4 | 11 |
| 8 | dev_8 | tkn_5 | 11 |
+-----+---------+--------+-------+
should be:
+-----+---------+--------+-------+
| id | device | token | cash|
+-----+---------+--------+-------+
| 1 | dev_1 | null | 3 |
| 2 | dev_2 | tkn_1 | 10 |
| 3 | dev_3 | null | 10 |
| 4 | dev_4 | tkn_2 | 14 |
| 5 | dev_5 | tkn_3 | 10 |
| 6 | dev_6 | null | 10 |
| 7 | dev_7 | null | 10 |
| 8 | dev_8 | tkn_4 | 11 |
| 8 | dev_8 | null | 11 |
| 8 | dev_8 | tkn_5 | 15 |
+-----+---------+--------+-------+
Thanks in advance :)

Try using an EXISTS subquery:
UPDATE yourTable t1
SET token = NULL
WHERE EXISTS (SELECT 1 FROM (SELECT * FROM yourTable) t2
WHERE t2.token = t1.token AND
t2.cash > t1.cash);
Demo
Note that this answer assumes that there would never be a tie for two token records having the same highest cash amount.

To set exactly one row in the even of duplicates on the maximum cash, use the id:
update t join
(select tt.*,
(select t3.id
from t t3
where t3.token = tt.token
order by t3.cash desc, id desc
) as max_cash_id
from t tt
) tt
on t.id = tt.id and t.id < tt.max_cash_id
set token = null;

Related

How to query MIN value of MAX subquery with two distinct columns?

I have a table like this:
+---------------+--------------+------+-----+----------+
| Field | Type | Null | Key | Default |
+---------------+--------------+------+-----+----------+
| id | smallint(6) | NO | PRI | NULL |
| Book | tinyint(4) | NO | | NULL |
| Chapter | smallint(6) | NO | | NULL |
| Paragraph | smallint(6) | NO | | NULL |
| Text | text | YES | | NULL |
| RevisionNum | mediumint(9) | NO | PRI | NULL |
+---------------+--------------+------+-----+----------+
mysql> select id,Book,Chapter,Paragraph,RevisionNum FROM MyTable ORDER BY id LIMIT 11;
+-----+------+---------+-----------+-------------+
| id | Book | Chapter | Paragraph | RevisionNum |
+-----+------+---------+-----------+-------------+
| 1 | 1 | 1 | 1 | 0 |
| 1 | 1 | 1 | 1 | 1 |
| 1 | 1 | 1 | 1 | 2 |
| 2 | 1 | 2 | 2 | 0 |
| 2 | 1 | 2 | 2 | 1 |
| 2 | 1 | 2 | 2 | 2 |
| 2 | 1 | 2 | 2 | 3 |
| 3 | 1 | 2 | 3 | 0 |
| 4 | 1 | 2 | 4 | 0 |
| 4 | 1 | 2 | 4 | 1 |
| 5 | 1 | 3 | 5 | 0 |
+-----+------+---------+-----------+-------------+
To find a book or chapter which has no unrevised paragraph,
I wish to query either the minimum value of the maximums of
all the distinct id's for that chapter or book, or else in
some fashion determine that no id remains unedited (with a
MAX(RevisionNum) of zero).
Most of my attempts to date have ended in errors like this one:
SELECT DISTINCT Book,RecordNum FROM MyTable
-> WHERE 0 < ALL (SELECT DISTINCT RecordNum,MAX(RevisionNum)
FROM MyTable
WHERE MAX(RevisionNum) > 0);
ERROR 1111 (HY000): Invalid use of group function
...And I wasn't using the "GROUP BY" function at all!
The following query produces results, but simply
gives ALL id's, and does not actually show a unique
set of Book records, as requested. How could this happen?
SELECT DISTINCT Book,id,MAX(RevisionNum) FROM MyTable GROUP BY id LIMIT 5;
+------+----+------------------+
| Book | id | MAX(RevisionNum) |
+------+----+------------------+
| 1 | 1 | 30 |
| 1 | 2 | 16 |
| 1 | 3 | 15 |
| 1 | 4 | 10 |
| 1 | 5 | 9 |
+------+----+------------------+
What would the correct query be to give results more like this:
+------+-----+-----------------------+
| Book | id | MIN(MAX(RevisionNum)) |
+------+-----+-----------------------+
| 1 | 5 | 3 |
| 2 | 17 | 1 |
| 3 | 33 | 2 |
| 4 | 147 | 0 |
| 5 | 225 | 2 |
+------+-----+-----------------------+
Are you looking for two levels of aggregation?
select id, book, min(max_revisionnum)
from (select id, book, chapter, paragraph, max(revisionnum) as max_revisionnum
from mytable
group by id, book, chapter, paragraph
) t
group by id, book;
EDIT:
Based on your comment, you can use:
select *
from (select id, book, chapter, paragraph, max(revisionnum) as max_revisionnum,
row_number() over (partition by book order by max(revisionnum) desc) as seqnum
from mytable
group by id, book, chapter, paragraph
) t
where seqnum = 1;
Here is a db<>fiddle.
In older versions of MariaDB, you can use a correlated subquery:
select t.*
from mytable t
where (id, book, chapter, paragraph, revisionnum) = (select t2.id, t2.book, t2.chapter, t2.paragraph, t2.revisionnum
from mytable t2
where t2.book = t.book
order by t2.revisionnum desc
limit 1
);
For this query, try adding an index on (book, revisionnum desc).

mysql table ordering incorrect with group by and order by

table 1: forum_threads
+-----+------+-------+
| id | title| status|
+-----+------+-------+
| 1 | a | 1 |
| 2 | b | 1 |
| 3 | c | 1 |
| 4 | d | 1 |
| 5 | e | 1 |
| 6 | f | 1 |
+-----+------+-------+
table 2: forum_comments
+-----+----------+--------------------+
| id | thread_id| comment |
+-----+----------+--------------------+
| 1 | 4 | hai |
| 2 | 4 | hello |
| 3 | 2 | welcome |
| 4 | 2 | whats your name |
| 5 | 6 | how are you |
| 6 | 5 | how old are you |
| 7 | 5 | good |
+-----+----------+--------------------+
wanted output
+-----------+----------+-----------------+
| thread_id | title | comment_count |
+-----------+----------+-----------------+
| 5 | e | 2 |
| 6 | f | 1 |
| 2 | b | 2 |
| 4 | d | 2 |
+-----------+----------+-----------------+
my Query
SELECT forum_threads.*,forum_comments.*,count(forum_comments.id) as comment_count
FROM forum_comments
LEFT JOIN forum_threads ON forum_comments.thread_id = forum_threads.id
GROUP BY forum_threads.id
ORDER BY forum_comments.id desc
Here I am trying to get the titles by the latest comment.
when I give ORDER BY forum_comments.id this returns the wrong order.
I need to order by the latest comments in the forum_comments table.
this query returns the wrong order please help me to find out the correct order.
how could I solve this easily?
This query should give you the expected result:
select t2.thread_id, t1.title, t2.comment_count from forum_threads as t1,
(SELECT id, thread_id, count(comment) as comment_count from forum_comments group by thread_id) as t2
where t1.id = t2.thread_id order by t2.id desc;
Instead of using forum_threads.* and forum_comments.* can you give specific column names and try.
If that doesn't work you should try explicitly assigning primary and foreign keys.

MySQLl key-value store ordering with specific condition

I have the following structure:
+----------+--------+---------------------+
| id| gr_id| name | value |
+----------+--------+---------------------+
| 1 | 11 | name | Burro |
| 2 | 11 | submit | 2019/05/10 |
| 3 | 11 | date | 2019/05/17 |
| 4 | 12 | name | Ajax |
| 5 | 12 | submit | 2019/05/10 |
| 6 | 12 | date | 2019/05/18 |
+----------+--------+---------------------+
I have to order it by the date(if the name is date), from highest to lowest date, also it has to keep the groups (gr_id) without mixing the elments.
The desired result would look like this:
+----------+--------+---------------------+
| id| gr_id| name | value |
+----------+--------+---------------------+
| 4 | 12 | name | Ajax |
| 5 | 12 | submit | 2019/05/10 |
| 6 | 12 | date | 2019/05/18 |
| 1 | 11 | name | Burro |
| 2 | 11 | submit | 2019/05/10 |
| 3 | 11 | date | 2019/05/17 |
+----------+--------+---------------------+
How can i implement this?
You'll have to associate the group ordering criteria with all the elements of the group. You can do it through a subquery, or a join.
Subquery version:
SELECT t.*
FROM (SELECT gr_id, value as `date` FROM t WHERE `name` = 'date') AS grpOrder
INNER JOIN t ON grpOrder.gr_id = t.gr_id
ORDER BY grpOrder.`date`
, CASE `name`
WHEN 'name' THEN 1
WHEN 'submit' THEN 2
WHEN 'date' THEN 3
ELSE 4
END
Join version:
SELECT t1.*
FROM t AS t1
INNER JOIN AS t2 ON t1.gr_id = t2.gr_id AND t2.`name` = 'date'
ORDER BY t2.value
, CASE t1.`name`
WHEN 'name' THEN 1
WHEN 'submit' THEN 2
WHEN 'date' THEN 3
ELSE 4
END

SQL Advanced SELECT Statement

translations
+---------+----------------+----------+---------+
| id_user | id_translation | referrer | id_word |
+---------+----------------+----------+---------+
| 1 | 3 | NULL | 4 |
| 1 | 17 | NULL | 3 |
| 2 | 17 | NULL | 5 |
| 2 | 17 | NULL | 1 |
| 2 | 17 | NULL | 7 |
words
+----+------+
| id | word |
+----+------+
| 4 | out |
+----+------+
users_translations
+---------+----------------+----------+---------+
| id_user | id_translation | referrer | id_word |
+---------+----------------+----------+---------+
| 1 | 17 | 1 | 4 |
| 2 | 17 | 2 | 4 |
| 3 | 18 | NULL | 4 |
I need to select all translations for current word and id_translation, but if in the row referrer = 1 (current user), then I don't need another results (translations from another users for current word), if there is no referrer = 1, show all.
SELECT DISTINCT `t`.*, `ut`.`id_user` AS tuser
FROM translations AS t
LEFT JOIN users_translations AS ut ON `t`.`id` = `ut`.`id_translation`
INNER JOIN words ON `words`.`id` = `ut`.`id_word` OR `words`.`id` = `t`.`id_word`
WHERE (`word` = 'help')
ORDER BY `t`.`translation` ASC
+----+-------------+---------+---------+-------+
| id | translation | id_word | id_user | tuser |
+----+-------------+---------+---------+-------+
| 17 | допомагати | 4 | 1 | 2 |
| 17 | допомагати | 4 | 1 | 1 |
First row doesn't need, because we have tuser = 1. When there is no tuser = 1, all results should be returned.
I don't understand how to build select statement and I will be very appreciative that somebody shows me how to make it work.
First thing that comes to mind
--add this to your where clause
id_user <=
CASE WHEN EXISTS(SELECT * FROM translations WHERE id_user = 1 AND id_word = words.id_word)
THEN 1
ELSE (SELECT MAX(Id) FROM translations)
END

MYSQL - how to string comparisons and query?

+--------------------+---------------+------+-----+---------+-------+
| ID | GKEY |GOODS | PRI | COUNTRY | Extra |
+--------------------+---------------+------+-----+---------+-------+
| 1 | BOOK-1 | 1 | 10 | | |
| 2 | PHONE-1 | 2 | 12 | | |
| 3 | BOOK-2 | 1 | 13 | | |
| 4 | BOOK-3 | 1 | 10 | | |
| 5 | PHONE-2 | 2 | 10 | | |
| 6 | PHONE-3 | 2 | 20 | | |
| 7 | BOOK-10 | 2 | 20 | | |
| 8 | BOOK-11 | 2 | 20 | | |
| 9 | BOOK-20 | 2 | 20 | | |
| 10 | BOOK-21 | 2 | 20 | | |
| 11 | PHONE-30 | 2 | 20 | | |
+--------------------+---------------+------+-----+---------+-------+
Above is my table. I want to get all records which GKEY > BOOK-2, Who can tell me the expression with mysql?
Using " WHERE GKEY>'BOOK-2' " Cannot get the correct results.
How about (something like):
(this is MSSQL - I guess it will be similar in MySQL)
select
*
from
(
select
*,
index = convert(int,replace(GKEY,'BOOK-',''))
from table
where
GKEY like 'BOOK%'
) sub
where
sub.index > 2
By way of explanation: The inner query basically recreates your table, but only for BOOK rows, and with an extra column containing the index in the right data type to make a greater than comparison work numerically.
Alternatively something like this:
select
*
from table
where
(
case
when GKEY like 'BOOK%' then
case when convert(int,replace(GKEY,'BOOK-','')) > 2 then 1
else 0
end
else 0
end
) = 1
Essentially the problem is that you need to check for BOOK before you turn the index into a numberic, as the other values of GKEY would create an error (without doing some clunky string handling).
SELECT * FROM `table` AS `t1` WHERE `t1`.`id` > (SELECT `id` FROM `table` AS `t2` WHERE `t2`.`GKEY`='BOOK-2' LIMIT 1)