MySQL loop for every row and update - mysql

I have table called users and for example it looks like:
Name ID
Tom 1
Al 55
Kate 22
...
The problem is: the IDs are not in sequence.
I would like to give them new IDs from 1 to length of users. I would like to declare some var=1 and make UPDATE in loop and give them new ID = var, and later do var=var+1 until var <= users length
How can I do this?
Thank you very much!

Here is how you would do that in MySQL. Just run this:
set #newid=0;
update users set ID = (#newid:=#newid+1) order by ID;

If the ID in the Users table is not referenced by other tables by FK, the following query can update the ID in the table to have new consecutive values:
CREATE TABLE IF NOT EXISTS tmpUsers (
ID int not null,
newID int not null auto_increment primary key
) engine = mysisam;
INSERT INTO tmpUsers (ID,newID)
SELECT ID,NULL
FROM users
ORDER BY ID;
UPDATE users u INNER JOIN tmpUsers t
ON u.ID=t.ID
SET u.ID=t.NewID;
DROP TABLE IF EXISTS tmpUsers;
Test script:
CREATE TABLE users (ID int not null, name nvarchar(128) not null);
INSERT users(ID,name)
VALUES (1,'aaa'),(4,'bbb'),(7,'ggg'),(17,'ddd');
SELECT * FROM users;

Related

How to check if value exists in one of the columns and in which one in MySQL?

I am trying to create a function in MySQL for my 'friendships' table where I have 4 columns:
friendship_id, status, user1_id and user2_id (last two are also foreign keys which point to another table 'users').
Function should receive one parameter which is users id and return how many friends that user has.
I created a function like this:
CREATE FUNCTION num_of_friends(id int)
RETURNS int(11)
BEGIN
SET #friends_count = (SELECT COUNT(user2_id) FROM friendships WHERE user1_id = id);
RETURN #friends_count;
END
So this function returns number of friends for that specific user, if his id is in column user1_id.
But there are some users whose id is only in the column user2_id.
How could I check if the user's id which we passed as parameter exists in either of the columns and count friendships based on that?
You can compare the id value with both the columns with OR
CREATE FUNCTION num_of_friends(id int)
RETURNS int(11)
BEGIN
SET #friends_count = (SELECT COUNT(*) from (select * from friendship where user1_id=id
union
select user2_id,user1_id from friendship where user2_id=id)data);
RETURN #friends_count;
END
Check this SQL fiddle: http://sqlfiddle.com/#!9/c5928ca/8

How to get last inserted rows in MySQL from different tables?

I have a lot of different tables in my database, and I need somehow to get last inserted rows from those tables. Like social networks feeds. Also, those tables are have not random, but unknown names, because they all generated by users.
In example:
I have tables: A,B,C and D with 5k rows in each table.
I need somehow to get last rows from those tables and make it ordered by id, like we do in a simple query: "SELECT * FROM table A ORDER BY id DESC", but I'm looking for something like: "SELECT * FROM A,B,C,D ORDER BY id DESC".
Tables have same structure.
You can use union and order by if your tables have the same structure. Something like:
select *
from (
select * from A
union all
select * from B
union all
select * from C
) order by id desc
If the tables don't have the same structure then you cannot select * from all and order them and you might want to do two queries. First would be:
select id, tableName
from (
select id, 'tableA' as tableName from A
union all
select id, 'tableB' as tableName from B
union all
select id, 'tableC' as tableName from C
) order by id desc
Which will give you the last IDs and the tables where they are inserted. And then you need to get the rows from each respective table.
With pure Mysql it will be a bit hard. You can select the table names like:
SELECT table_name FROM information_schema.tables; but still how to use them in the statement? You will need to generate it dynamically
A procedure to generate the query dynamically can be something like ( I haven't tested it but I believe with some debugging it should work) :
DELIMITER $$
CREATE PROCEDURE buildQuery (OUT v_query VARCHAR)
BEGIN
DECLARE v_finished INTEGER DEFAULT 0;
DECLARE v_table_count INTEGER DEFAULT 0;
DECLARE v_table varchar(100) DEFAULT "";
-- declare cursor for tables (this is for all tables but can be changed)
DEClARE table_cursor CURSOR FOR
SELECT table_name FROM information_schema.tables;
-- declare NOT FOUND handler
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET v_finished = 1;
OPEN table_cursor;
SET v_query="select * from ( ";
get_table: LOOP
FETCH table_cursor INTO v_table;
SET v_table_count = v_table_count + 1;
IF v_finished = 1 THEN
LEAVE get_table;
END IF;
if v_table_count>1 THEN
CONCAT(vquery, " UNION ALL ")
END IF;
SET v_query = CONCAT(vquery," select * from ", v_table );
END LOOP get_table;
SET v_query = CONCAT(vquery," ) order by id desc " );
-- here v_query should be the created query with UNION_ALL
CLOSE table_cursor;
SELECT #v_query;
END$$
DELIMITER ;
If each table's id is counted seperatly you can't order by ID, so you'll need to calculate a global id and use it on all of your tables.
You can do it as follows:
Assuming you have 2 tables A,B:
Create Table A(id int NOT NULL auto_increment, name varchar(max), value varchar(max), PRIMARY_KEY(id));
Create Table B(id int NOT NULL auto_increment, name varchar(max), value varchar(max), PRIMARY_KEY(id));
Add another table IDS with id as auto increment primary key.
Create table IDS (id int NOT NULL auto_increment, ts Timestamp default CURRENT_TIMESTAMP, PRIMARY_KEY(id));
For all your tables id column should use now the id from the IDS table as foreign key instead of auto increment.
Create Table A(id int NOT NULL auto_increment, name varchar(max), value varchar(max), PRIMARY_KEY(id),CONSTRAINT fk_A_id FOREIGN KEY(id) REFERENCE IDS(id) ON DELETE CASCADE ON UPDATE CASCADE);
Create Table B(id int NOT NULL auto_increment, name varchar(max), value varchar(max), PRIMARY_KEY(id),CONSTRAINT fk_A_id FOREIGN KEY(id) REFERENCE IDS(id) ON DELETE CASCADE ON UPDATE CASCADE);
for each table add before insert trigger, the trigger should first insert row to the IDS table and insert the LAST_INSERT_ID the table.
Create TRIGGER befor_insert_A BEFORE INSERT On A
FOR EACH ROW
BEGIN
insert into IDS() values ();
set new.id = LAST_INSERT_ID();
END
Create TRIGGER befor_insert_B BEFORE INSERT On B
FOR EACH ROW
BEGIN
insert into IDS() values ();
set new.id = LAST_INSERT_ID();
END
Now you can create view from all tables with union all, the rows of v can be sorted now by the id and give the cronlogic order of insertion.
Create view V AS select * from A UNION ALL select * from B
For example you can query on V the latest 10 ids:
select * from V Order by id desc LIMIT 10
Other option is to add timestamp for each table and sort the view by the timestamp.
Hi are you looking for this? However the id is not a good column to see the last updated among different tables.
select *
from A
join B
on 1=1
join C
on 1=1
join D
on 1=1
order by A.id desc

CREATE TRIGGER for :two tables USER & FILE. Wen user uploads file,the USER_ID column present in USER table must be generated in the other table FILE

two tables have the following columns:-
USER
user_id, password,user_name
FILE
file_id,file_name,user_id
how to create trigger which will display the user_id in FILE table when any user uploads a file.
create table FILE
(
file_id int identity(1,1),
file_name varchar(30),
user_id int
)
create table USER
(
user_id int identity(1,1),
user_name nvarchar(30),
specialization nvarchar(20),
date_of_upload datetime )
create trigger T1
ON dbo.USER
AFTER UPDATE OF user_id ON USER
FOR EACH ROW
WHEN //condition
BEGIN
INSERT INTO FILE (USER_ID)
SELECT USER_ID FROM USER
END
Several things:
SQL Server doesn't have FOR EACH ROW feature - if you update 10 rows in a single statement, the trigger is called once with 10 rows in the Deleted and Inserted pseudo tables
you cannot define a column criteria on the trigger - the trigger is called whenever something in the table is updated
Try this code instead:
CREATE TRIGGER T1
ON dbo.USER
AFTER UPDATE
AS
INSERT INTO dbo.FILE(USER_ID)
SELECT i.USER_ID
FROM Inserted i
WHERE NOT EXISTS (SELECT * FROM dbo.USER WHERE USER_ID = i.USER_ID)
END
This will insert all USER_ID values that aren't already in the dbo.USER table

Update based on select data

I have a query which returns
Primary Key | Value
And I want to update my data where primary key = primary key and value = another value. So basically I have
SELECT
id,
custom_value
FROM custom
JOIN user
USING (user_id)
WHERE id = 45
AND custom_name = "stuff";
And this generates
id | custom_value
1 | stuff
2 | stuff 2
And I want to then UPDATE the existing db with
UPDATE table SET field = custom_value WHERE id = id;
Or if I used the first row it would be
UPDATE table SET field = 'stuff' WHERE id = 1;
How could I do this?
So...
SELECT UPDATE table
id, -> WHERE id = id
custom_value -> SET field = custom_value
FROM custom
JOIN user
USING (user_id)
WHERE id = 45
AND custom_name = "stuff";
Select data then update another table with that data.
Two steps:
create temporary table tmp_tbl as select .... your select goes here;
then
update table, tmp_tbl
set table.field = tmp_tbl.custom_value
where table.id = tmp_tbl.id
and then, optionally, to clean up the unneeded data:
drop table tmp_tbl
(Or let MySQL drop it automatically on session closure.)

add duplicate value for primary key

I have a table with fields (id , brand, model , os)
id as primary key
tables have ~ 6000 rows
Now i want to add new field with id=4012 (already exist) & increment id++ for id>4012
silliest way :
make table backup
remove entries with id >= 4012
insert new entry with id = 4012
restore table from backup
stupid, but works ))
Looking for more beautiful solution
Thx
table structure :
CREATE TABLE IF NOT EXISTS `mobileslist` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`brand` text NOT NULL,
`model` text NOT NULL,
`os` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=14823 ;
i try :
UPDATE mobileslist SET id = id + 1 WHERE id IN (SELECT id FROM
mobileslist WHERE id >= 4822 ORDER BY id);
but got answer :
1093 - You can't specify target table 'mobileslist' for update in FROM clause
1) Create a temporary table, with descending order by ID.
2) Perform an UPDATE query on the temporary table which sets ID = ID + 1 WHERE ID >= 4012
3) Drop the temporary table
4) Perform your insert operation on the original table.
Hope i understood it right, you want to insert a new entry at position 4012 moving & reassigning all the entries present at Id = 4012 or more with a new id incremented by 1.
Hope this helps.
Try this:
UPDATE <TableName>
SET
id = id + 1
WHERE id IN (SELECT id FROM <TableName> WHERE id >= 4012 ORDER BY id)
INSERT INTO <TableName>
(id , brand, model , os)
VALUE
(4012, "<BrandName>", "<Model>", "<OS>")
Updated Answer:
DECLARE #MaxId INT, #Difference INT
SELECT
#MaxId = MAX(id)
FROM mobileslist
SET #Difference = #MaxId - 4012
UPDATE mobileslist
SET
id = id + #Difference
where id >= 4012
INSERT INTO mobileslist
(id , brand, model , os)
VALUE
(4012, "TestBrand", "TestModel", "TestOS")
UPDATE mobileslist
SET
id = id - #Difference
where id > 4012