Fill a column with a sequence int numbers in MySQL - mysql

I have to alter my table adding a column that I have to fill with sequence of int numbers. Does anyone knows how can I fill this column?
I tried this:
set #i = 1;
Update db.table set new_column=(#i := #i+1) where id>0;
But this just fill all lines with the number 2. =(

you can easy do so:
UPDATE db.table
CROSS JOIN ( SELECT #myid := 0) AS parameter
SET new_column = #myid := (#myid +1);

Try
Update your_table
join
(
select id, #i := #i+1 as rank
from your_table
cross join (select #i := 0) ia
where id > 0
order by id
) tmp on tmp.id = your_table.id
set your_table.new_column = tmp.rank

All this answers works for me. And I did another one
delimiter #
create procedure fill_column()
begin
declare v_max int unsigned default 83;-- number of registers that I have
declare v_counter int unsigned default 0;
declare a int default 0;
start transaction;
while v_counter < v_max do
update db.tb_table set new_column= (#a := #a+1) where id> 0;
set v_counter=v_counter+1;
end while;
commit;
end #
call fill_column();
Thanks guys =)

Related

How to speed up stored procedure in mysql

Below given is my procedure takes too much time to execute.
BEGIN
DECLARE rank1 BIGINT DEFAULT 0;
DECLARE id1 BIGINT;
DECLARE rankskip BIGINT DEFAULT 0;
DECLARE mark DECIMAL(10,2) DEFAULT 0;
DECLARE oldmark DECIMAL(10,2) DEFAULT -100000;
DECLARE done int DEFAULT 0;
DECLARE cursor_i CURSOR FOR
SELECT
(rightmarks - negativemarks) as mark, id
FROM
testresult
WHERE
testid = testid1
ORDER BY
(rightmarks - negativemarks) DESC;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cursor_i;
read_loop: LOOP
FETCH cursor_i INTO mark, id1;
IF done = 1 THEN
LEAVE read_loop;
END IF;
IF oldmark = mark THEN
BEGIN
IF IsRankSkip = 1 THEN
BEGIN
SET rankskip = rankskip + 1;
END;
END IF;
END;
ELSE
BEGIN
SET rank1 = rank1 + rankskip + 1;
SET rankskip = 0;
END;
END IF;
SET oldmark = mark;
UPDATE testresult SET rank = rank1 WHERE id=id1;
END LOOP;
CLOSE cursor_i;
END
This loop iterate minimum 2000 times.
Here IsRankSkip and testid1 is an argument passed to the procedure.
This procedure takes 65.343152046204 time to execute. If anybody guide me how can I reduce executing time?
Thank you in advance.
You can do this with a single update statement, making use of variables that change during the execution of it:
UPDATE testresult a
JOIN ( SELECT id,
#row := #row + 1 row_number,
#rank := if(mark = #lastmark, #rank, #row) as rank,
#dense_rank := #dense_rank + if(mark = #lastmark, 0, 1) as dense_rank,
#lastmark := mark as mark
FROM ( SELECT rightmarks - negativemarks as mark,
id
FROM testresult
WHERE testid = testid1
ORDER BY 1 DESC
) data,
(SELECT #row := 0, #dense_rank := 0) r
) b
ON a.id = b.id
SET a.rank = if(IsRankSkip, b.rank, b.dense_rank);
The query with alias b calculates the rank and adds it as a column in the result set. In fact, it adds three kinds of numbers:
#row: the sequential row number without special treatment of equal values
#rank: the same as row number of the value differs from the previous value, otherwise it is the same as in the previous row
#dense_rank: this increments when the value differs from the previous value, otherwise it is the same as in the previous row
You can choose which one to update your rank column with. In the above SQL I have used the two procedure variables IsRankSkip and testid1.
Remark
If you are always calling your procedure for all testid values, then the above can be further improved, so all these updates are done with only one update statement.

MySQL If Statement and Increment

I am having issues with a MySQL If statement that creates a group rank. here is the MySQL Statement:
SELECT EnCode, EnName, QuScore,
#scorerank := IF(#currathlete = EnCode, #scorerank + 1, 1),
#currathlete := EnCode
FROM ranking ORDER BY EnCode, QuScore DESC
It currently gives the following output
'1004277','Ashe','1628','1','1004277'
'1004277','Ashe','1309','1','1004277'
'1004277','Ashe','1263','1','1004277'
'1004277','Ashe','648','1','1004277'
'1004277','Ashe','645','1','1004277'
'1004277','Ashe','1628','1','1004277'
'1015934', 'Sabina', '544', '1', '1015934'
'1015934', 'Sabina', '455', '1', '1015934'
'1015934', 'Sabina', '276', '1', '1015934'
'1015934', 'Sabina', '216', '1', '1015934'
What it should be doing is incrementing each of the '1' numbers by one for each row that has the same code, and then starting from 1 again when it sees a different code number (1004277, then 1015934 in this case)
Any help is appreciated as i have followed a number of examples online using the above method but seem to hit the same issue a this point.
Try this way in stored Procedure:
drop PROCEDURE if EXISTS INCREMENTME;
create PROCEDURE INCREMENTME()
BEGIN
DECLARE OldEnNamevar VARCHAR(10) DEFAULT NULL;
DECLARE done INT DEFAULT FALSE;
DECLARE Encodevar VARCHAR(10);
DECLARE EnNamevar VARCHAR(10);
DECLARE QuScorevar VARCHAR(10);
DECLARE scorerankvar VARCHAR(10);
DECLARE currathalthletevar VARCHAR(10);
DECLARE countcode int(29) DEFAULT(1);
DECLARE counter int(20) default 0;
DECLARE get_cur CURSOR FOR select `Encode`,`EnName`,`QuScore`,`scorerank`,`currathalthlete` from tbl_ranking;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;
drop table if exists temp_temptable;
create TEMPORARY table temp_temptable(Encodevar VARCHAR(50) NULL,EnNamevar VARCHAR(50) NULL,QuScorevar VARCHAR(50) NULL,scorerankvar VARCHAR(50) NULL,currathalthletevar VARCHAR(50) NULL,recordCount int(10) null);
OPEN get_cur;
REPEAT
set counter = counter + 1;
FETCH get_cur INTO Encodevar,EnNamevar,QuScorevar,scorerankvar,currathalthletevar;
if (OldEnNamevar = EnNamevar) THEN
set countcode = countcode +1;
ELSE
if(counter=1) then
set countcode = 1;
ELSE
set countcode = 0;
end if;
end if;
if (OldEnNamevar != EnNamevar) THEN
set countcode = 1;
end if;
if(OldEnNamevar=NULL) then
set countcode = 1;
end if;
insert into temp_temptable (Encodevar,EnNamevar,QuScorevar,scorerankvar,currathalthletevar,recordCount) values(Encodevar,EnNamevar,QuScorevar,scorerankvar,currathalthletevar,countcode);
set OldEnNamevar = EnNamevar;
UNTIL done END REPEAT;
select * from temp_temptable;
drop temporary table if exists temp_temptable;
CLOSE get_cur;
END
call the procedure like this:
call INCREMENTME();
Here's the result:
You have to initialize your variables, otherwise they are null (at least at the beginning of the session, probably not anymore if you run it twice), and your query will give strange results. Try
SELECT EnCode, EnName, QuScore,
#scorerank := IF(#currathlete = EnCode, #scorerank + 1, 1),
#currathlete := EnCode
FROM ranking, (select #currathlete := '', #scorerank := 0) init
ORDER BY EnCode, QuScore DESC

How can easy select into temp table in MySQL?

I need to create a temp table in one procedure.
Now my steps were:
1. DROP TEMPORARY TABLE if EXISTS
2. CREATE TEMPORARY TABLE
3. Do something with the table
4. DROP TEMPORARY TABLE if EXISTS
I do not want to create the temp table every time when I call the procedure.
I just want to truncate the temp table,but how to select some data into the existing temp table??
How to make the blow procedure run faster?
DROP PROCEDURE IF EXISTS `test`;
DELIMITER ;;
CREATE PROCEDURE `test`(IN type INT)
begin
DECLARE done INT DEFAULT 0;
DECLARE total INT DEFAULT 0;
DECLARE min_1 INT DEFAULT 0;
DECLARE min_10 INT DEFAULT 0;
DECLARE min_30 INT DEFAULT 0;
DECLARE tmp INT DEFAULT 0;
DECLARE cursor1 CURSOR FOR SELECT TIMESTAMPDIFF(MINUTE,time1,time2) as dif FROM t_test ;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
DROP TEMPORARY TABLE if EXISTS t_test;
CREATE TEMPORARY TABLE t_test AS (
SELECT optime as time1,dtime as time2
FROM tbmytest
WHERE thetpye = type);
OPEN cursor1;
REPEAT
FETCH cursor1 INTO tmp;
IF done = 1 THEN
close cursor1;
ELSE
SET total = total + 1;
IF tmp > 30 THEN
SET min_30 = min_30 + 1;
ELSEIF tmp > 10 THEN
SET min_10 = min_10 + 1;
ELSEIF tmp > -1 THEN
SET min_1 = min_1 + 1;
END IF;
END IF;
UNTIL done END REPEAT;
DROP TEMPORARY TABLE if EXISTS t_test;
SELECT min_1,min_10,min_30,total;
end;;
DELIMITER ;
Cursors are very, very slow. The temporary table may be not necessary.
You can achieve the same as the procedure in a single query. Use variables for this. Following query should do the job.
SELECT #min30, #min10, #min1 FROM (
SELECT #diff := TIMESTAMPDIFF(MINUTE,time1,time2),
CASE WHEN #diff > 30 THEN #min30 := #min30 + 1
WHEN #diff > 10 THEN #min10 := #min10 + 1
WHEN #diff > -1 THEN #min1 := #min1 + 1
END
FROM t_test
CROSS JOIN (SELECT #diff := NULL, #min1 := 0, #min10 := 0, #min30 := 0) var_init_subquery
) another_subquery_alias
I don't know if your question is simplified, meaning that you need the temporary table for other purposes, too. If it is so, instead of dropping and creating the table, you can also use a table with MEMORY engine and update it once in a while when needed.
CREATE TABLE my_special_temp_table
(min30 int, min10 int, min1 int)
ENGINE=MEMORY;
UPDATE my_special_temp_table
JOIN (
SELECT #min30 AS min30, #min10 AS min10, #min1 AS min1 FROM (
SELECT #diff := TIMESTAMPDIFF(MINUTE,time1,time2),
CASE WHEN #diff > 30 THEN #min30 := #min30 + 1
WHEN #diff > 10 THEN #min10 := #min10 + 1
WHEN #diff > -1 THEN #min1 := #min1 + 1
END
FROM t_test
CROSS JOIN (SELECT #diff := NULL, #min1 := 0, #min10 := 0, #min30 := 0) var_init_subquery
) another_subquery_alias
) t
SET my_special_temp_table.min30 = t.min30,
my_special_temp_table.min10 = t.min10,
my_special_temp_table.min1 = t.min1;

How to write this query in SQL server 2008

I have a requirement to insert more than one record into the table where the stored procedure will return the value for insertion. Please consider my logic.
Query Logic
foreach(var a in (select id from table1))
{
insert into table2 values(a,DateTime.Now)
}
I need the same above logic needs to be done in SQL server. Any help to this solution will be appreciated.
Thanks,
declare #a int=0
while(#a<10)
begin
if(#a in (select id from table1))
begin
insert into table2 values(a,DateTime.Now)
set #a=#a+1
end
end
I tried this and working well. Thanks to #Nikola Markovinović
insert into table2(idColumn, dateColumn) select id, getdate() from table1
hope this helps..
DECLARE #intFlag INT
SET #intFlag = 1
WHILE (#intFlag <=5)
BEGIN
PRINT #intFlag
SET #intFlag = #intFlag + 1
IF #intFlag = 4
BREAK;
END
GO
declare #a int = 0, #n int, #i int = 1
select #n=COUNT(*) from table1
while #i < #n
begin
insert into table2 values
select x.a,Getdate() from
(select ROW_NUMBER() over (order by [key]) as slno,* from table1 ) as x where x.slno = #i
set #i=#i+1;
end

How to loop the data and if username exists append incremental number e.g. JOHSMI1 or JOHSMI2

I have a userid table
UserId
JHOSMI
KALVIE
etc...
What I would like to do is create a select statement and pass user id, if the userid already exists then append 1 to the id, This gets complicated if you already have JHOSMI, JHOSMI1, then I want to return JHOSMI2.
Really appreciate help here.
Thanks in advance
edited 21-Jul
this is what i got so far.. but not working the way
select #p AS StaffID,
#old_p := #p,
#Cnt := #Cnt+1 As Lvl,
(SELECT #p :=Concat(#i, #Cnt)
FROM departmenttaff
WHERE upper(trim(UserId)) = upper(trim(StaffID))
AND upper(trim(department)) like upper(trim('SERVICE'))
) AS dummy
FROM (
SELECT
#i := upper(trim('JOHSMI')),
#p := upper(trim('JOHSMI')),
#old_p :='',
#Cnt:=0
) vars,
departmenttaff p
WHERE #p <> #old_p
order by Lvl Desc LIMIT 1;
This will do exactly what you want. You will need a unique constraint on your column.
You might also need to add in error code if success = 0.
This is in MSSQL, you will need to add the relevant commands for MySQL. I do not have MySQL so I cannot test it.
NOTE: You can replace the try catch with some IF EXISTS logic. I just prefer the try catch because its more stable for multiple threads.
begin tran
select * from #tmp
declare #success bit
declare #name varchar(50)
declare #newname varchar(50)
declare #nextid int
declare #attempts int
set #name = 'brad2something'
set #success = 0
set #attempts = 0
while #success = 0 and #attempts < 5 begin
begin try
set #attempts = #attempts + 1 -- failsafe
set #newname = #name
if exists (select * from #tmp where username = #name) begin
select #nextid = isnull(max(convert(int, substring(username, LEN(#name) + 1, 50))), 0) + 1
from #tmp where username like #name + '%' and isnumeric(substring(username, LEN(#name) + 1, 50)) = 1
set #newname = #name + CONVERT(varchar(20), #nextid)
end
insert into #tmp (username) values (#newname)
set #success = 1
end try begin catch end catch
end
--insert into #tmp (username)
--select
select #success
select * from #tmp
rollback
/*
drop table #tmp
create table #tmp (
username varchar(50) not null unique
)
insert into #tmp (username)
select 'brad'
union all select 'brad1'
union all select 'brad2something5'
union all select 'brad2'
union all select 'laney'
union all select 'laney500'
*/
I noticed you want to back fill data. If you want to back fill then this will work. It is extremely inefficient but there is no way around it. There is optimizing code you can put in for when an "error" occurs to prevent all previous counts from happening, but this will work.
begin tran
select * from #tmp
declare #success bit
declare #name varchar(50)
declare #newname varchar(50)
declare #nextid int
declare #attempts int
set #name = 'laney'
set #success = 0
set #attempts = 0
set #nextid = 1
while #success = 0 and #attempts < 5 begin
begin try
if exists (select * from #tmp where username = #name) begin
set #newname = #name + CONVERT(varchar(20), #nextid)
while exists (select * from #tmp where username = #newname) begin
set #nextid = #nextid + 1
set #newname = #name + CONVERT(varchar(20), #nextid)
end
end else
set #newname = #name
set #attempts = #attempts + 1 -- failsafe
insert into #tmp (username) values (#newname)
set #success = 1
end try begin catch end catch
end
--insert into #tmp (username)
--select
select #success
select * from #tmp
rollback
/*
drop table #tmp
create table #tmp (
username varchar(50) not null unique
)
insert into #tmp (username)
select 'brad'
union all select 'brad1'
union all select 'brad2something5'
union all select 'brad2'
union all select 'laney'
union all select 'laney500'
*/
Is it mandatory to have the count in same column? its better to have it in a different integer column. Anyways, if this is the requirement then select userid from table where userid like 'JHOSMI%', then do extract the number using mysql substr function.
For other people who might find this, here's a version in PostgreSQL:
create or replace function uniquify_username(varchar) returns varchar as $$
select $1 || coalesce((max(num) + 1)::varchar, '')
from
(select
substring(name, '^(.*?)[0-9]*$') as prefix,
coalesce(substring(name, '.*([0-9]+)$'), '0')::integer as num
from user1) users
where prefix = $1
$$ LANGUAGE sql;
I think it could be adapted to MySQL (though probably not as a stored procedure) but I don't have a MySQL server handy to do the conversion on.
Put a UNIQUE constraint on the column.
You didn't say what language you are using, so use this pseudo code
counter = 0
finished = false
while finished = false
{
try
{
if counter >= 1 then name = name + counter
counter = counter + 1
insert into table (name)
}
}
This code is extremely finicky. But will get the job done and there is no real other way to do this except for in sql, and you will always have some type of try catch to avoid two processes running at the same time. This way you use the unique key constraint to force the error, and supress it because it is expected.
I in no way condone using try/catch for business logic like this, but you are putting yourself in a situation thats unavoidable. I would say put the ID in a seperate column and make a unique constraint on both fields.
Proper solution:
Columns: Name, ID, Display Name
Unique constraint on: Name, ID
Display Name is a computed column (virtual) is Name + ID
If you do it this way, then all you have to do is INSERT INTO table (name, (select max() from table))