Mysql How to update n rows using count function? - mysql

In the following table, I want to set delete = true if the total records for same orgid exceed 500
and I want to do it according to createdate such that if records exceed 500 the old records get deleted and make total records 500 for that orgid.
here is my table
Table A
+----+-------+------------------+--------+------------+
| id | orgid | transactionvalue | delete | createdate |
+----+-------+------------------+--------+------------+
| 1 | 1 | 123 | false | 05-16-2020 |
| 2 | 1 | 412 | false | 07-16-2020 |
| 3 | 2 | 762 | false | 07-16-2020 |
+----+-------+------------------+--------+------------+
Here is the query I am trying
update A
set
delete = true
where orgid = 1
and (select count(*) as records
from (select *
from A order by createdate
) as pseudotable)) >500

use subquery and join update
UPDATE tablea
INNER JOIN
(select orgid, count(*) cnt from tablea group by orgid
) b ON tablea.orgid = b.orgid
SET
delete = 'true'
where cnt>500

You can use row_number() to find the 500th record for each org and then use that information:
update tablea a join
(select a2.org_id, a2.created_date as cutoff_created_date
from (select a2.*,
row_number() over (partition by a2.org_id order by create_date desc) as seqnum
from tablea a2
) a2
where a2.seqnum = 500
) a2
on a.org_id = a2.org_id and and
a.created_date < a2.cutoff_created_date
set delete = true;

I did not try all of the other answers, they might be correct but this is what worked for me
update A
set `delete` = true
where orgid = 1 AND id IN (
SELECT id FROM A where orgid = 1
order by createdate DESC
LIMIT 500,18446744073709551615);

Related

How to update records when subquery returns more than 1 row

Example, I need to auto/random assign a "new" Leader to lead the students (select only 1 student from each types) and by daily basis, assume there are total 10 leaders in the Leader table.
But i am getting wrong result by using the query below, it will update all students to same leader id in the first time.
UPDATE students
SET student_status = 'assigned'
, leader_id = 'abc'
WHERE student_id IN
( SELECT student_id
FROM
( SELECT *
FROM students
) s
WHERE student_status = 'New'
GROUP
BY type_id)
My expected result similar to the below query, but i don't want to run another logic to generate the randomized student_id:
UPDATE students
SET student_status='assigned', leader_id='abc'
WHERE student_id IN ('T0123','S0222','T7777','S8888')
student_id | type_id | leader_id | student_status
-----------+---------+-----------+---------------
T0121 | Type 1 | xyz | assigned
T0122 | Type 1 | | new
T0123 | Type 1 | | new
S0221 | Type 2 | | new
S0222 | Type 2 | | new
S0223 | Type 2 | xyz | assigned
T7777 | Type 3 | | new
T7779 | Type 3 | xyz | assigned
S8888 | Type 4 | xyz | assigned
S8887 | Type 4 | | new
S8886 | Type 4 | | new
Use RAND() to get a random leader_id between 1 and the max leader_id and a subquery using ROW_NUMBER and OVER to get a random student_id per type to update
UPDATE students s
JOIN (SELECT type_id, student_id, ROW_NUMBER() OVER (PARTITION BY type_id ORDER BY RAND()) rnum
FROM students
WHERE status = 'new') r ON r.type_id = s.type_id AND
r.student_id = s.student_id AND
rnum = 1
SET leader_id = (SELECT CEIL(RAND() * MAX(leader_id)) FROM leaders),
status = 'assigned'
WHERE status = 'new'
For MySQL :
UPDATE students
SET student_status='assigned', leader_id='abc'
WHERE student_id IN (
SELECT student_id FROM (
SELECT * FROM students) AS s
WHERE student_status ='New' GROUP BY type_id ORDER BY RAND()
)
This should work. This will Pick 1 new student each time from every Type and will update Status and Leader ID accordingly.
UPDATE students
SET student_status = 'assigned'
, leader_id = 'abc'
WHERE student_id IN (
SELECT student_id FROM (
SELECT c.student_id, c.student_status, c.type_id
FROM students C
WHERE c.student_status = 'New'
ORDER BY RAND()
) AS shuffled_items
GROUP BY type_id
)

Update last row in group with data from first row in group

I'm currently in the process of converting data from one structure to another, and in the process I have to take a status id from the first entry in the group and apply it to the last entry in that same group. I am able to target and update the last item in the group just fine when using a hard-coded value, but I'm hitting a wall when trying to use the status_id from the first entry. Here is an example of the data structure.
-----------------------------------------------------------
| id | ticket_id | status_id | new_status_id | created_at |
-----------------------------------------------------------
| 1 | 10 | NULL | 3 | 2018-06-20 |
| 2 | 10 | 1 | 1 | 2018-06-22 |
| 3 | 10 | 1 | 1 | 2018-06-23 |
| 4 | 10 | 1 | 1 | 2018-06-26 |
-----------------------------------------------------------
So the idea would be to take the new_status_id of ID 1 and apply it to the same field for ID 4.
Here is the query that works when using a hard-coded value
UPDATE Communications_History as ch
JOIN
(
SELECT communication_id, MAX(created_at) max_time, new_status_id
FROM Communications_History
GROUP BY communication_id
) ch2
ON ch.communication_id = ch2.communication_id AND ch.created_at = ch2.max_time
SET ch.new_status_id = 3
But when I use the following query, I get Unknown column ch.communication_id in where clause
UPDATE Communications_History as ch
JOIN
(
SELECT communication_id, MAX(created_at) max_time, new_status_id
FROM Communications_History
GROUP BY communication_id
) ch2
ON ch.communication_id = ch2.communication_id AND ch.created_at = ch2.max_time
SET ch.new_status_id = (
SELECT nsi FROM
(
SELECT new_status_id FROM Communications_History WHERE communication_id = ch.communication_id AND status_id IS NULL
) as ch3
)
Thanks!
So I just figured it out using variables. It turns out the original "solution" only worked when there was one ticket's worth of history in the table, but when all the data was imported, it no longer worked. However, this tweak did seem to fix the issue.
UPDATE Communications_History as ch
JOIN
(
SELECT communication_id, MAX(created_at) max_time, new_status_id
FROM Communications_History
GROUP BY communication_id
) ch2
ON ch.communication_id = ch2.communication_id AND ch.created_at = ch2.max_time
SET ch.new_status_id = ch2.new_status_id;

how to update max row per group in SQL

I just added the 'default' column to my DB. I am trying to set the default value to '1' based on the latest 'addDate' per accountId.
+----+-----------+--------------------+--------+
| id | accountId | addDate | default|
+----+-----------+--------------------+--------+
| 1 | 45 |2012-02-29 08:41:59 | |
| 2 | 55 |2012-03-29 08:41:59 | |
| 3 | 45 |2012-04-29 08:41:59 | |
| 4 | 55 |2012-05-29 08:41:59 | |
| 5 | 60 |2012-05-29 08:41:59 | |
+----+-----------+--------------------+--------+
I found I was able to isolate the proper rows by using =>
select * from tble1
where addDate = (select max(addDate) from tble1 as sl where sl.accountId = tble1.accountId);
I need to be able to run an UPDATE that sets 'default' column to '1' only 1 time per 'accountId' basing it off of latest 'addDate'.
try this
UPdate Table1
SET `default` = 1
where addDate in (select * from (
select max(addDate) from table1 as sl group by accountId)t
)
DEMO HERE
UPDATE table1 x
LEFT
JOIN table1 y
ON y.accountid = x.accountid
AND y.adddate > x.adddate
SET x.default = 1
WHERE y.id IS NULL;
or (faster)
UPDATE table1 x
JOIN
( SELECT accountid
, MAX(addDate) max_adddate
FROM table1
GROUP
BY accountid
) y
ON y.accountId = x.accountId
AND y.max_adddate = x.adddate
SET x.default = 1;

combining 2 select statements with different number of columns, where 1st statement is already using JOIN

I'm trying to combine 2 select statements with different number of columns.
The 1st statement is this:
SELECT s.id, s.date_sent, m.sam_subject, m.sam_msg_id, (SELECT
COUNT(id) FROM tbl_something WHERE schedule_id = s.id) AS
total_recipients FROM tbl_something2 AS s INNER JOIN
tbl_something3 AS m ON s.message_id = m.sam_msg_id ORDER BY
s.date_sent DESC
The 2nd statement:
SELECT * FROM sms_something4 WHERE status = '0' ORDER BY id DESC
the table output for the 1st statement:
id date_sent sam_subject sam_msg_id total_recipients
1 1372880628 e-Newsletter 2 2
output for 2nd:
id | subject | sent | failed | date_sent | data_sent | data_failed | message | sam_uid from | select_members | status | from_no
11 | test | 2 | 0 | 1372881670 | 639176286411,639224588324 | | | | | | 0 | 0
any suggestions on how would i be able to combine these two statements?
my target output is
id | subject | sent | failed | date_sent | sam_subject | total_recipients | date_sent for email
sam_msg_id can be ignored.
Thank you.
here is basic that you need to have .. you might have to trouble shoot. add column as you need.
SELECT *
FROM (
SELECT s.id, s.date_sent, m.sam_subject, m.sam_msg_id, (SELECT COUNT(id)
FROM tbl_something
WHERE schedule_id = s.id) AS total_recipients
FROM tbl_something2 AS s
INNER JOIN tbl_something3 AS m
ON s.message_id = m.sam_msg_id
) as tbl
INNER JOIN (SELECT * FROM sms_something4 WHERE status = '0') as tbl2
ON tbl2.subject = tbl.sam_subject
and tbl.date_sent=tbl2.date_sent
and tbl.total_Recipients = tbl2.sent+ tbl2.failed
ORDER BY tbl.date_sent DESC
As AJP said you can just do this:
SELECT s.id, a.subject,a.sent, s.date_sent, m.sam_subject,
(SELECT COUNT(id) FROM tbl_something WHERE schedule_id = s.id) AS total_recipients
FROM tbl_something2 AS s
INNER JOIN tbl_something3 AS m ON s.message_id = m.sam_msg_id
INNER JOIN
(
SELECT * FROM sms_something4 WHERE status = '0' ORDER BY id DESC
) a on a.subject = m.sam_subject and a.date_sent = s.date_sent
ORDER BY
s.date_sent DESC

MySQL Query to get the record for a specific date

I have the following table named staff_status with structure and records:
----------------------------------------------------
| id (INT) | status (VARCHAR) | status_date (DATE) |
----------------------------------------------------
| 1 | Working | 2009-05-03 |
| 2 | Working | 2009-07-21 |
| 1 | Leave | 2010-02-01 |
| 1 | Working | 2010-02-15 |
----------------------------------------------------
Now I want to query this to get the status of the staff on a specific date. Example: status of id = 1 on 2010-02-10 should return Leave while on 2010-03-01 should return Working
What I have tried without success:
SELECT t1.status FROM staff_status t1 INNER JOIN (SELECT * FROM staff_status WHERE id = 1 AND status_date < '2010-02-10') t2 ON (t1.id = t2.id AND t1.status_date < t2.status_date);
You could try something like
SELECT s.*
FROM staff_status s INNER JOIN
(
SELECT id,
MAX(status_date) status_date
FROM staff_status
WHERE status_date < '2010-02-10'
AND id = 1
) m ON s.id = m.id
AND s.status_date = m.status_date
Additionaly you could try an ORDER BY status_date DESC LIMIT 1
from 13.2.8. SELECT Syntax
Something like
SELECT *
FROM staff_status
WHERE id = 1
AND status_date < '2010-02-10'
ORDER BY status_date DESC
LIMIT 1
First, you'll need the MAX() of the dates per id:
SELECT id, MAX(status_date)
FROM staff_status
WHERE status_date < "2010-02-10" GROUP BY id
...but MySQL doesn't guarantee that the status will be from the row of the MAX(status_date) (in fact, this is almost never the case). So you'll have to take the information you found above, and pull out those records from the original table, matching on id and status_date:
SELECT id, status
FROM staff_status
WHERE
(id, status_date)
IN
(
SELECT id, MAX(status_date)
FROM staff_status
WHERE status_date < "2010-02-10" GROUP BY id
);
This generates a list of ids and statuses for the most recent date found before 2010-02-10:
+------+---------+
| id | status |
+------+---------+
| 2 | Working |
| 1 | Leave |
+------+---------+
2 rows in set (0.01 sec)
Surely simply:
SELECT status FROM staff_status WHERE status_date = '2010-02-10'
Would return you "leave"?
try this:
select status
from staff_status
where status_date<='2010-03-01'
and id=1
order by status_date desc
limit 1
try this:
SELECT IFNULL((SELECT status
FROM staff_status
WHERE id = 1 AND
status_date = '2010-02-10'),
"Leave") AS status;