How to Write query for MySQL - mysql

I have two tables as:
mysql> select * from survey;
+-----------+-----------+----------+--------+-----------+
| survey_id | client_id | stage_id | by_ref | no_branch |
+-----------+-----------+----------+--------+-----------+
| 2 | 65 | 72 | P | 15 |
| 3 | 67 | 72 | D | 2 |
+-----------+-----------+----------+--------+-----------+
2 rows in set (0.07 sec)
mysql> select * from allcode where code_type="MARKETING_STAGES";
+------------------+---------+------+--------------------+
| code_type | code_id | srno | code_name |
+------------------+---------+------+--------------------+
| MARKETING_STAGES | 72 | 1 | Enquiry |
| MARKETING_STAGES | 73 | 3 | Meeting |
| MARKETING_STAGES | 74 | 4 | Presentation |
| MARKETING_STAGES | 75 | 5 | Review / Follow up |
| MARKETING_STAGES | 76 | 6 | Negotiation |
| MARKETING_STAGES | 77 | 7 | Order |
| MARKETING_STAGES | 78 | 8 | Agreement |
| MARKETING_STAGES | 162 | 9 | Complete |
| MARKETING_STAGES | 163 | 2 | Tender |
+------------------+---------+------+--------------------+
9 rows in set (0.04 sec)
I want to update stage_id of survey table to next value which will be fetch from allcode code_id.
Right now I have client_id i.e. 65 from survey table, and want to update stage_id to 163 ( i.e. Next code_id from allcode table on sorting based on srno )
What I have tried till is
update survey as s
set s.stage_id=
(select code_id from allcode
where code_id > (select stage_id from (select * from survey where client_id=65 )as su)
and code_type="MARKETING_STAGES"
limit 1)
where client_id=65;
This query update stage_id of allcode to 73 and I want it to be updated to 163 (Depending on srno)

I would use joins in the update to get the next code_id based on srno:
update survey s
inner join allcode a1 on s.stage_id=a1.code_id
inner join allcode a2 on a1.srno=a2.srno-1
set s.stage_id=a2.code_id
where a1.code_type='MARKETING_STAGES' and a2.code_type='MARKETING_STAGES' and s.client_id=65
I assumed that srno field increments by 1 without any gaps. The purpose of the 1st join is to get the current stage_id's srno. Then the 2nd join gets the stage_id for the next srno.

You were missing an order by before limit in subquery.
So without touching your rest of the query, I just tried adding an order by and it seems to update first stage id from 72 to 163 as you want.
Rextester Demo
update survey as s
set s.stage_id=
(select code_id from allcode
where code_id > (select stage_id from (select * from survey where client_id=65 )as su)
and code_type="MARKETING_STAGES"
ORDER BY SRNO
limit 1)
where client_id=65;

Related

Mysql delete similar rows according to specific columns except the ones with highest id

my table has duplicate row values in specific columns. i would like to remove those rows and keep the row with the latest id.
the columns i want to check and compare are:
sub_id, spec_id, ex_time
so, for this table
+----+--------+---------+---------+-------+
| id | sub_id | spec_id | ex_time | count |
+----+--------+---------+---------+-------+
| 1 | 100 | 444 | 09:29 | 2 |
| 2 | 101 | 555 | 10:01 | 10 |
| 3 | 100 | 444 | 09:29 | 23 |
| 4 | 200 | 321 | 05:15 | 5 |
| 5 | 100 | 444 | 09:29 | 8 |
| 6 | 101 | 555 | 10:01 | 1 |
+----+--------+---------+---------+-------+
i would like to get this result
+----+--------+---------+---------+-------+
| id | sub_id | spec_id | ex_time | count |
+----+--------+---------+---------+-------+
| 5 | 100 | 444 | 09:29 | 8 |
| 6 | 101 | 555 | 10:01 | 1 |
+----+--------+---------+---------+-------+
i was able to build this query to select all duplicate rows from multiple columns, according to this question
select t.*
from mytable t join
(select id, sub_id, spec_id, ex_time, count(*) as NumDuplicates
from mytable
group by sub_id, spec_id, ex_time
having NumDuplicates > 1
) tsum
on t.sub_id = tsum.sub_id and t.spec_id = tsum.spec_id and t.ex_time = tsum.ex_time
but now im not sure how to wrap this select with a delete query to delete the rows except for the ones with highest id.
as shown here
You can modify your sub-select query, to get maximum value of id for each duplication combination.
Now, while joining to the main table, simply put a condition that id value will not be equal to the maximum id value.
You can now Delete from this result-set.
Try the following:
DELETE t
FROM mytable AS t
JOIN
(SELECT MAX(id) as max_id,
sub_id,
spec_id,
ex_time,
COUNT(*) as NumDuplicates
FROM mytable
GROUP BY sub_id, spec_id, ex_time
HAVING NumDuplicates > 1
) AS tsum
ON t.sub_id = tsum.sub_id AND
t.spec_id = tsum.spec_id AND
t.ex_time = tsum.ex_time AND
t.id <> tsum.max_id

MySQL retrieve last record in JOIN with group by

I have following tables with data as:
1.Table follow_up as :
mysql> select * from follow_up;
+--------------+----------------+--------------------------------------------------+-------------------+---------+---------------+-----------+---------------+----------+
| follow_up_id | feedback_close | feedback_open | is_email_required | is_Open | reminder_date | client_id | conclusion_id | stage_id |
+--------------+----------------+--------------------------------------------------+-------------------+---------+---------------+-----------+---------------+----------+
| 1 | NULL | dsffsdfsdfsd | 1 | 1 | 2017-09-20 | 101 | 96 | 72 |
| 2 | NULL | FSGDFHFGHFG | 1 | 1 | 2017-09-28 | 101 | 251 | 72 |
| 3 | NULL | Tender stage fb | 0 | 1 | NULL | 101 | 98 | 163 |
| 4 | NULL | Call back tender stage update date from 28 to 30 | 1 | 1 | 2017-09-28 | 101 | 96 | 163 |
| 5 | NULL | Metting follow up for next meeting | 1 | 1 | 2017-10-02 | 101 | 96 | 73 |
+--------------+----------------+--------------------------------------------------+-------------------+---------+---------------+-----------+---------------+----------+
2. Table logs as :
mysql> SELECT * from logs where transaction = 'FLWUP';
+---------+---------+---------------------+---------+-------------+
| user_id | menu_id | logs_time | tran_id | transaction |
+---------+---------+---------------------+---------+-------------+
| 84 | 69 | 2017-09-19 19:31:04 | 1 | FLWUP |
| 84 | 69 | 2017-09-19 19:31:25 | 2 | FLWUP |
| 84 | 69 | 2017-09-20 19:10:41 | 2 | FLWUP |
| 84 | 69 | 2017-09-21 12:35:01 | 3 | FLWUP |
| 84 | 69 | 2017-09-21 12:35:26 | 4 | FLWUP |
| 84 | 69 | 2017-09-21 12:36:16 | 4 | FLWUP |
| 84 | 69 | 2017-09-21 12:38:30 | 5 | FLWUP |
+---------+---------+---------------------+---------+-------------+
7 rows in set (0.00 sec)
3. table allcode as :
mysql> select * from allcode where code_type like 'MARK%';
+------------------+---------+------+----------------------+
| code_type | code_id | srno | code_name |
+------------------+---------+------+----------------------+
| MARKETING_STAGES | 72 | 1 | Enquiry |
| MARKETING_STAGES | 73 | 3 | Meeting |
| MARKETING_STAGES | 74 | 4 | Presentation |
| MARKETING_STAGES | 163 | 2 | Tender |
+------------------+---------+------+----------------------+
11 rows in set (0.00 sec)
I have invoked a query and got result as :
mysql> select f.follow_up_id,f.feedback_open, f.feedback_close, f.reminder_date,
ast.code_name as stage, ac.code_name as conclusion, max(l.logs_time)
from follow_up f
join logs l on l.tran_id = f.follow_up_id
join allcode ast on ast.code_id = f.stage_id
join allcode ac on ac.code_id = f.conclusion_id
where l.transaction='FLWUP' and f.client_id = 101
group by ast.code_name order by ast.srno;
+--------------+------------------------------------+----------------+---------------+---------+------------+---------------------+
| follow_up_id | feedback_open | feedback_close | reminder_date | stage | conclusion | max(l.logs_time) |
+--------------+------------------------------------+----------------+---------------+---------+------------+---------------------+
| 1 | dsffsdfsdfsd | NULL | 2017-09-20 | Enquiry | Call Back | 2017-09-20 19:10:41 |
| 3 | Tender stage fb | NULL | NULL | Tender | Next | 2017-09-21 12:36:16 |
| 5 | Metting follow up for next meeting | NULL | 2017-10-02 | Meeting | Call Back | 2017-09-21 12:38:30 |
+--------------+------------------------------------+----------------+---------------+---------+------------+---------------------+
3 rows in set (0.00 sec)
But I want result as :
+--------------+-----------------------------------------------------+----------------+---------------+---------+------------+---------------------+
| follow_up_id | feedback_open | feedback_close | reminder_date | stage | conclusion | max(l.logs_time) |
+--------------+-----------------------------------------------------+----------------+---------------+---------+------------+---------------------+
| 2 | FSGDFHFGHFG | NULL | 2017-09-20 | Enquiry | Call Back | 2017-09-20 19:10:41 |
| 4 | Call back tender stage update date from 28 to 30 | NULL | NULL | Tender | Next | 2017-09-21 12:36:16 |
| 5 | Metting follow up for next meeting | NULL | 2017-10-02 | Meeting | Call Back | 2017-09-21 12:38:30 |
+--------------+-----------------------------------------------------+----------------+---------------+---------+------------+---------------------+
3 rows in set (0.00 sec)
I'm not able to JOIN and group by to get required result.
column conclusion_id and stage_id of table follow_up are referring to code_id of table allcode.
Question :
the result I want is to be
group by stage_id,
order by srno of allcode and
last/recent follow_up_id of follow_up table
DEMO Includes my answer, original question with full group by needed, and Reupal's answer in demo. You were missing the values in your sample data for conclusionID so I just created them based on ID (now updated to ISO, Callback but missing 98.)
and my results don't match yours in this column; but I believe your expected results are in error.
Seems like you want the max follow_up_ID for each stage_ID when multiple stage_ID's exist
This can be handled by a derived table/inline view getting that max follow_UP_ID grouped by the stage_ID and a joining it back to your set. to limit results to include only the max follow_Up_ID by stage_Id.
I'm also not a fan of mySQL's extended group by and prefer including all columns not aggregated in the select in the group by. Using the extended group by tends to hide potential problems. In this case grouping by just the ast.code_name allowed the engine to select a non distinct value from the other columns. You ended up not getting the desired results and furthermore it hide the fact you would get multiple records in your query were it not for the extended group by use/misuse.
SELECT f.follow_up_id,f.feedback_open, f.feedback_close, f.reminder_date,
ast.code_name as stage, ac.code_name as conclusion, max(l.logs_time)
from follow_up f
join logs l on l.tran_id = f.follow_up_id
join allcode ast on ast.code_id = f.stage_id
join allcode ac on ac.code_id = f.conclusion_id
JOIN SELECT max(follow_up_ID) MFID, stage_ID
FROM follow_up
GROUP BY stage_ID) Z
on f.follow_up_ID = Z.MFID
and F.Stage_ID = Z.Stage_ID
WHERE l.transaction='FLWUP' and f.client_id = 101
GROUP BY f.follow_up_id,f.feedback_open, f.feedback_close, f.reminder_date,
ast.code_name , ac.code_name
ORDER BY ast.srno;
Try below, notice ordering and group by sequence.
select f.follow_up_id,f.feedback_open, f.feedback_close, f.reminder_date,
ast.code_name as stage, ac.code_name as conclusion, max(l.logs_time)
from follow_up f
join logs l on l.tran_id = f.follow_up_id
join allcode ast on ast.code_id = f.stage_id
join allcode ac on ac.code_id = f.conclusion_id
where l.transaction='FLWUP' and f.client_id = 101
group by follow_up.stage_id order by ast.srno, follow_up.follow_up_id DESC;
This should works, and if its not then you should search like how to set ordering on multiple column.
Ref. article- SQL multiple column ordering

How to get max value with various conditions from a single MySQL table

I have table with a bunch of (machine id) mid's and (sensor id) sid's, and their corresponding (values) v's. Needless to say the id column is a unique row number. (NB: There are other columns in the table, and not all mid's have the same sid's)
Current Table:
+------+-------+-------+-----+---------------------+
| id | mid | sid | v | timestamp |
+------+-------+-------+-----+---------------------+
| 51 | 10 | 1 | 40 | 2015/5/1 11:56:01 |
| 52 | 10 | 2 | 39 | 2015/5/1 11:56:25 |
| 53 | 10 | 2 | 40 | 2015/5/1 11:56:42 |
| 54 | 11 | 1 | 50 | 2015/5/1 11:57:52 |
| 55 | 11 | 2 | 18 | 2015/5/1 11:58:41 |
| 56 | 11 | 2 | 19 | 2015/5/1 11:58:59 |
| 57 | 11 | 3 | 58 | 2015/5/1 11:59:01 |
| 58 | 11 | 3 | 65 | 2015/5/1 11:59:29 |
+------+-------+-------+-----+---------------------+
Q: How would I get the MAX(v)for each sid for each mid?
Expected Output:
+------+-------+-------+-----+---------------------+
| id | mid | sid | v | timestamp |
+------+-------+-------+-----+---------------------+
| 51 | 10 | 1 | 40 | 2015/5/1 11:56:01 |
| 53 | 10 | 2 | 40 | 2015/5/1 11:56:42 |
| 54 | 11 | 1 | 50 | 2015/5/1 11:57:52 |
| 56 | 11 | 2 | 19 | 2015/5/1 11:58:59 |
| 58 | 11 | 3 | 65 | 2015/5/1 11:59:29 |
+------+-------+-------+-----+---------------------+
The expected output is to obtain the whole row with all the (single) max value for all the sids in all the mids.
Addendum:
Due to a very big table, I need to place boundaries with dates. For the sample above the two boundary dates should be 2015/05/01 00:00:00 (1st of May'15) till 2015/05/02 00:00:00 (2nd of May'15). Q: How could I add this date boundary?
Find the max v in subquery for each combination of mid, sid and then join it with your original table to get the desired result.
select *
from your_table t
join (
select mid, sid, max(v) as v
from your_table
group by mid, sid
) t2 using (mid, sid, v);
Note here that if there are multiple rows with same sid, mid and v, it will return all of them.
As mentioned in the comments, since you have an id column, you can include that in limited correlated query like this:
select *
from your_table t1
where id = (select id
from your_table t2
where t1.mid = t2.mid
and t1.sid = t2.sid
order by v desc, id desc
limit 1
);
This will give you one single row per mid, sid combination with max v (and latest id in case of ties).
Use MAX() function with GROUP BY clause
SELECT id, mid, sid, MAX(v) AS v, `timestamp`
FROM MyTable
GROUP BY mid, sid;
This returns rows with maximum values of v for each combination of mid and sid.

select groups of adjacent rows randomly

I have a huge table, I want to select groups of rows randomly.
The classic random query (SELECT * FROM table ORDER BY RAND() LIMIT 1000;
) selects not adjacent rows, but I want to select random groupS of n rows (in my picture n = 3rows).
The following picture is just example, the rows are random with every execution.
Not perfect - but maybe adequate for your purposes...
SELECT * FROM my_table;
+-----+
| id |
+-----+
| 1 |
| 2 |
| 3 |
...
| 188 |
| 189 |
| 190 |
| 191 |
...
| 253 |
| 254 |
| 255 |
| 256 |
+-----+
SELECT DISTINCT a.* FROM my_table a JOIN (SELECT * FROM my_table ORDER BY RAND() LIMIT 10) b ON b.id BETWEEN a.id AND a.id+2 ORDER BY id;
+-----+
| id |
+-----+
| 1 |
| 31 |
| 32 |
| 33 |
| 108 |
| 109 |
| 110 |
| 144 |
| 145 |
| 146 |
| 166 |
| 167 |
| 168 |
| 199 |
| 200 |
| 201 |
| 202 |
| 203 |
| 204 |
| 225 |
| 226 |
| 227 |
| 232 |
| 233 |
| 234 |
| 246 |
| 247 |
| 248 |
+-----+
28 rows in set (0.00 sec)
Assuming langids are contiguous you can select one group with SELECT ... WHERE id>3*r and id<=3*(r+1) where r is a random integer from 1 to MAX(id)/3. Multiplying r by 3 ensures no groups will overlap.
You could create a temporary table or subquery by SELECT DISTINCT CAST(langid/3 AS INT), order it randomly, and select the first N of them, then join against this table.
Consider this
SELECT id, name, #rank:=#rank+1 AS rank, CAST(rank/3 AS INT) AS groupid FROM
(SELECT id, name FROM Objects) z, (SELECT #rank:=0) zz;
This result set will give new contiguous IDs to the rows in the Objects table, so we don't have to assume anything about their actual primary keys. groupid indexes the groups.
From this set you can select any number of groupids randomly, and then for each chosen groupid you can find the original primary key.

Get MAX row for GROUP in MySQL

I have the following data:
+---------+----------+----------+--------+
| id | someId | number | data |
+---------+----------+----------+--------+
| 27 | 123 | 1 | abcde1 |
| 28 | 123 | 3 | abcde2 |
| 29 | 123 | 1 | abcde3 |
| 30 | 123 | 5 | abcde4 |
| 31 | 124 | 4 | abcde1 |
| 32 | 124 | 8 | abcde2 |
| 33 | 124 | 1 | abcde3 |
| 34 | 124 | 2 | abcde4 |
| 35 | 123 | 16 | abcde1 |
| 245 | 123 | 3 | abcde2 |
| 250 | 125 | 0 | abcde3 |
| 251 | 125 | 1 | abcde4 |
| 252 | 125 | 7 | abcde1 |
| 264 | 125 | 0 | abcde2 |
| 294 | 123 | 0 | abcde3 |
| 295 | 126 | 0 | abcde4 |
| 296 | 126 | 0 | abcde1 |
| 376 | 126 | 0 | abcde2 |
+---------+----------+----------+--------+
And I want to get a MySQL query that gets me the data of the row with the highest number for each someId. Note that id is unique, but number isn't
SELECT someid, highest_number, data
FROM test_1
INNER JOIN (SELECT someid sid, max(number) highest_number
FROM test_1
GROUP BY someid) t
ON (someid=sid and number=highest_number)
Unfortunately it is not look quite efficient. In Oracle it could be possible to user OVER clause without subqueries, but MySQL…
Update 1
If there are several instances of highest number this will returs also several data for each pair of someid and number.
To get the only row per each someid we should preaggregate the source table to make someid and number pairs unique (see t1 subquery)
SELECT someid, highest_number, data
FROM
(SELECT someid, number, MIN(data) data
FROM test_1
GROUP BY
someid, number) t1
INNER JOIN
(SELECT someid sid, max(number) highest_number
FROM test_1
GROUP BY someid) t2
ON (someid=sid and number=highest_number)
Update 2
It is possible to simplify previous solution
SELECT someid,highest_nuimber,
(select min(data)
from test_1
where someid=t1.someid and number=highest_nuimber)
FROM
(SELECT someid, max(number) highest_nuimber
FROM test_1
GROUP BY someid) t1
If we materialize unique pairs of someid and number than it is possible to use correlated subquery. Unlike a JOIN it would not produce additional rows if highest value of number is repeated several times.
Slight tweak to Naeel's answer but to return just a single data result for any someId even if there's a tie you should add a GROUP BY:
SELECT t1.someid, t1.number, t1.data
FROM Table1 t1
INNER JOIN (SELECT someId sid, max(number) max_number
FROM Table1
GROUP BY someId) t2
ON (someId = sid AND number = max_number)
GROUP BY t1.someId
SQL Fiddle here