Update entire column with 1 to n - mysql

I have table where there is column named uid , It uses Autoincrement and its updated with 1,2,3 etc. Now I have cron job that deleted rows older than 2 days.So now my uid column is 2345 to n..I want to reset it to 1 to n again.I tried below code
UPDATE `tv` SET `uid` = ''
I was thinking to loop through all rows and update uid via php script, Is there any other alternative with single SQL command ?

You can try something like this:
UPDATE `tv` t
set t.`uid` = (SELECT count(*)
from `tv` s
WHERE t.`uid` >= s.`uid`)
This will count how many uid's are there that are smaller or equal then the one being updated, so when the first UID, lets say 2345 is being updated, there is only 1 uid that is smaller/equal to him so it will get the value 1 and so on...
EDIT: Try this-
UPDATE `tv` t
INNER JOIN(SELECT s.`uid`,count(*) as cnt
from `tv` s
INNER JOIN `tv` ss
ON(s.`uid` >= ss.`uid`)
GROUP BY s.`uid) tt
ON(t.`uid`=tt.`uid`)
SET t.`uid` = tt.cnt

Why don't decrease the uid by:
update tv set uid = uid -1

Related

MYSQL How can I update a row in a table after updating the same table?

Each row of my table has a child, For example ID 1 is parent of 11 and 11 is parent of 111 and each row has a balance, I need that if I update the balance of 111, the balance of 11 update and the balance of 1 too
for example: UPDATE ACCOUNTS SET value = 100 WHERE ID = 1
In this case the value of 11 is going to be 100 and the value of 1
then I can do something like this : UPDATE ACCOUNTS SET value = value + 150 WHERE ID = 11; in this case The value of 11 is going to be 250 and the value of 1 will be 250 and the value of 1 should stay 100. I need to do something like that
IM using mySQL
As you mentioned in the comments, MySQL will generally not allow you to define an update trigger which itself would trigger more updates on the same table. One option here, assuming you are using MySQL 8+, would be to define a recursive CTE which targets all records intended for the update:
WITH RECURSIVE cte (id, value, parent_id) AS (
SELECT id, value, parent_id
FROM ACCOUNTS
WHERE id = 111
UNION ALL
SELECT t1.id, t1.value, t1.parent_id
FROM ACCOUNTS t1
INNER JOIN cte t2
ON t1.id = t2.parent_id
)
UPDATE ACCOUNTS a1
INNER JOIN cte a2
ON a1.id = a2.id
SET value = 100;
This assumes that you would want to do the same update logic for each matching id in the hierarchy. The CTE will generate all records starting from id = 111, and working backwards up the tree to the root.

MySQL to not return same results

I have a table with list of customers:
customer
c_id c_name c_email c_role
1 abc1 a1#abc.com Dev
2 abc2 a2#abc.com Dev
3 abc3 a3#abc.com Dev
4 abc4 a4#abc.com Dev
5 abc5 a5#abc.com Dev
6 abc6 a6#abc.com Dev
7 abc7 a7#abc.com Dev
8 abc8 a8#abc.com Dev
9 abc9 a9#abc.com Dev
I query the table in the following way:
select * from customer where c_role = 'Dev' order by c_id limit 2;
So, I get the results with:
c_id c_name c_email c_role
1 abc1 a1#abc.com Dev
2 abc2 a2#abc.com Dev
The business requirements says that if any records are accessed by a set of users for last 3 days, then those should not return in the subsequent query output.
So, if the user runs a query again for the next 3 days:
select * from customer where c_role = 'Dev' order by c_id limit 2;
The result should be:
c_id c_name c_email c_role
3 abc3 a3#abc.com Dev
4 abc4 a4#abc.com Dev
Can anyone help me how to create this kind of rule in MySQL?
Adding a new column in current table is not going to help you.
You will have to create another table where you store all c_ids a user has accessed and the datetime when the query was executed.
CREATE TABLE IF NOT EXISTS `access_record` (
`id` INT(11) NOT NULL AUTO_INCREMENT ,
`c_id` INT(11) NOT NULL , // id of the record which user accessed
`user_id` INT(11) NOT NULL , // id of the user who accessed the record
`accessed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
PRIMARY KEY (`id`)
);
So whenever the user runs the next query you can use this table to know if user has already accessed a record or not and then use those c_ids to exclude them from next result set.
SELECT
c.c_id, c.c_role,c.c_name,c.c_email
FROM
customer AS c
WHERE
c.c_role = 'Dev'
AND c.c_id NOT IN (
SELECT
ar.c_id
FROM
access_record AS ar
WHERE ar.user_id = 1 // ofcourse this will change with each user (current user in your case I assume)
AND ar.accessed_at > DATE_SUB(NOW(), INTERVAL 3 DAY)
)
ORDER BY c.c_id LIMIT 2;
This will give you all records which were not accessed by specific user within last 3 days.
I hope this helps.
Answering #dang's question in comment
How do I populate access_record when a query runs?
When you have fetched all the records then you extract c_ids from those records then you insert those c_ids into the access_record table.
In MYSQL this query should do the trick
INSERT INTO access_record (c_id,user_id)
SELECT
c.c_id, 1 // user_id of the user who is fetching records
FROM
customer AS c
WHERE
c.c_role = 'Dev'
AND c.c_id NOT IN (
SELECT
ar.c_id
FROM
access_record AS ar
WHERE ar.user_id = 1 // ofcourse this will change with each user (current user in your case I assume)
AND ar.accessed_at > DATE_SUB(NOW(), INTERVAL 3 DAY)
)
ORDER BY c.c_id LIMIT 2;
You can also fetch those c_ids with one query then use second query to insert those c_ids into the access_record table.
If you have all your records fetched in $records then
$c_ids = array_column($temp, 'c_id'); // get all c_ids from fetched record array
Now run a query to insert all those c_ids.
I would add an extra table with users and accessdate. And make the business logic update those on access. For example:
user | accessdate | c_id
Your 'customer' table is data about customers. That is all it should have.
However, your selection criteria is really not what it appears to be. What business requirements want you to implement is a feed, or pipeline, with the selection acting as a consumer, being fed by un-accessed customers.
Each user (or group of users, ie: 'set of users') needs it's own feed, but that can be managed by a single table with a distinguishing field. So we need a user_group table to group your 'set of users'.
// user_group
g_id g_data
201 abc1
202 abc2
203 abc3
We will need to populate customer_feed with the access timestamps for each customer. We can add a foreign keys to delete customers and user_groups when they are deleted, but we will need update the customer feed when we use it.
create table customer_feed (
c_id int(11) not null,
g_id int(11) not null,
at timestamp not null,
primary key (c_id, g_id),
constraint customer_fk foreign key (c_id) references customer on delete cascade
constraint user_group_fk foreign key (g_id) references user_group on delete cascade,
);
// customer_feed
c_id g_id at
101 201 2018-11-26 07:40:21
102 201 2018-11-26 07:40:21
103 201 2018-11-26 07:40:22
When we want to read the customer data, we must do three queries:
Update the feed for the current user-group.
Get the users from the feed
Mark the users in the feed as consumed.
So, let's say we are using user_group 201.
When we update the feed, any new users to the feed are available to be read straight away,we give them a timestamp which is very early. So we can commemorate the battle of hastings...
// 1. update the customer_feed for user_group 201
insert into customer_feed(select c.c_id,201,TIMESTAMP('1066-10-14')
from customer c left join customer_feed f
on c.c_id = f.c_id and f.g_id=201 where f.c_id is null);
we select from the customer and the feed... We only accept records whose access dates are less than three days ago. This is the query you originally had, but with the feed restrictions.
// 2. read from feed for user_group 201
select c.* from customer c,customer_feed f where c.c_role='Dev'
and f.g_id=201 and f.c_id=c.c_id
and f.at < date_sub(now(), interval 3 day) limit 2
..and now we need to mark the values from the feed as being consumed. So, we gather the c_id we have selected into a list of c_id, eg: '102,103', and we mark them as consumed.
// 3. Mark the feed as consumed for user_group 201
update customer_feed set at=now() where g_id=201 and c_id in '102,103'
Add a new column in your customer table like start_accessing and then you can run the query:
SELECT *
FROM customer
WHERE c_role = 'Dev'
AND Date_add(start_accessing, INTERVAL 3 day) >= Curdate()
ORDER BY c_id
LIMIT 2;
start_accessing will be the column that will save when the user started accessing the resource.
Add a datetime stamp to the table and query from that.
There might be a way to get a 3 day rotation without having to change the tables.
By calculating batches of devs.
And calculate the current dev batch based on the current date.
The example below is for MySql 7.x (no window functions)
set #date = current_date;
-- set #date = date('2020-07-04');
set #dayslimit = 3;
set #grouplimit = 2;
set #devcnt = (select count(*) cnt
from test_customer
where c_role = 'Dev');
set #curr_rnk = ((floor(datediff(#date, date('2000-01-01')) / #dayslimit)%floor(#devcnt / #dayslimit))+1);
select c_id, c_name, c_email, c_role
-- , rnk
from
(
select t.*,
case when #rn := #rn +1 then #rnk := ceil((#rn/#grouplimit)%(#devcnt+1/#grouplimit)) end as rnk
from test_customer t
cross join (select #rn:=0, #rnk:= null) vars
where c_role = 'Dev'
order by c_id
) q
where rnk = #curr_rnk
order by c_id
limit 2;
A test on rextester here

SQL Update for Selected rows

I have this SQL Code:
SELECT * FROM `clients_branches`
WHERE NULLIF(clients_branches.invoice_email, '') IS NULL
GROUP BY client_code
HAVING COUNT(*) = 1
It returns all rows which appears only once in the database, also it returns only the ones with no email set. Now I need to apply UPDATE function to all of this select statement. How could I do it? I need to set clients_branches.invoice_send to 0 for all these rows.
I can't seem to use HAVING COUNT on UPDATE statement like this:
UPDATE `clients_branches`
SET clients_branches.invoice_send = 0
WHERE NULLIF(clients_branches.invoice_email, '') IS NULL
HAVING COUNT(*) = 1
Without HAVING COUNT I will change all of the rows which repeats at least once in this table. And I need to change only the ones with count = 1.
You could use a join for allow the use of the table for update and the result of your query
update `clients_branches`
JOIN
(
select client_code, count(*)
FROM `clients_branches`
WHERE NULLIF(clients_branches.invoice_email, '') IS NULL
group by client_code
HAVING COUNT(*) = 1
) t on t.client_code = `clients_branches`.client_code
set clients_branches.invoice_send = 0
;

Duplicates in MYSQL based on date and status

This is my current query to find duplicate phone numbers.
SELECT
id, cust_num, entry_date
FROM
nums
GROUP BY
cust_num
HAVING
count(*) >= 2
ORDER BY
id
DESC
However, now Im looking to update them all in one go, based on criteria.
I only want to update the newer ones with higher id's than the original.
And only if they have a certain status.
Heres an example, of what I would update based on a list of duplicates pulled from database.
ID | Num | date | status
1 555 Sep-12 NEW (this row wouldnt update first instance of 555)
33 555 Oct-12 NEW (this row would update, duplicate with NEW as status)
42 333 Dec-12 NEW (this row wouldn't update first instance of 333)
5 555 Jan-13 ACTIVE (this row wouldnt update, duplicate but wrong status)
66 333 Feb-14 NEW (this row would update, duplicate with NEW as status)
6 555 Jan-13 NEW (this row would update, duplicate with NEW as status)
77 333 Mar 15 ACTIVE (this row wouldnt update, duplicate but wrong status)
So the real question is, what query would I use to pull all the duplicates like this, and then update them based on their status.
UPDATE nums n SET ... WHERE n.status='NEW' AND (select count(*) from nums where num = n.num and id < n.id and status = 'NEW') > 0;
Add in your SET statement for whatever you want to update.
Here is the select. and the link to fiddle
select * from mytable as m where `status` = 'NEW' and exists
(select * from mytable as mm where mm.`id` < m.`id` and mm.`num` = m.`num`)
Update status to UPDATED
update mytable as m set m.`status` = 'UPDATED' where `status` = 'NEW' and exists
(select * from mytable as mm where mm.`id` < m.`id` and mm.`num` = m.`num`)

Mysql Updating a record with a value from the previous record

I have the following table, and what I'm trying to do is to update, for example, RefIDs 3-14 with the same Status value "Added" as the previous value. I want to do this until it reaches the next completed value. So when it encounters "Added", it updates all rows below that have a null with "Added" until it reaches the next RefID that has a populated Status, and subsequently updates the next batch of values. Can this be done? Any help would be greatly appreciated. I'm looking for this to be dynamic, as I don't want to manually update, say, Status = "Added" where RefID is 3.
RefID Status
1 Null
2 Added
3 Null
4 Null
5 Changed
6 Null
7 Null
What I want to achieve is this:
RefID Status
1 Null
2 Added
3 Added
4 Added
5 Changed
6 Changed
7 Changed
Try this query -
UPDATE refs_table t1
JOIN (
SELECT RefID, #s:=IF(Status IS NULL, #s, Status) Status
FROM (SELECT * FROM refs_table ORDER BY RefID) r,
(SELECT #s:=NULL) t
) t2
ON t1.RefID = t2.RefID
SET t1.Status = t2.Status
UPDATE
TableX AS t
JOIN
TableX AS tu
ON tu.RefID = (
SELECT
MAX(prev.RefID)
FROM
TableX AS prev
WHERE
prev.RefID < t.RefID AND
prev.Status IS NOT NULL)
SET
t.Status = tu.Status
WHERE
t.Status IS NULL
You can do it with a cursor.
http://dev.mysql.com/doc/refman/5.0/en/cursors.html
EDIT: Okay, okay, I read the other answers. Way better. Cursor is overkill here.