How can easy select into temp table in MySQL? - 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;

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

Fill a column with a sequence int numbers in 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 =)

T-SQL: split and aggregate comma-separated values

I have the following table with each row having comma-separated values:
ID
-----------------------------------------------------------------------------
10031,10042
10064,10023,10060,10065,10003,10011,10009,10012,10027,10004,10037,10039
10009
20011,10027,10032,10063,10023,10033,20060,10012,10020,10031,10011,20036,10041
I need to get a count for each ID (a groupby).
I am just trying to avoid cursor implementation and stumped on how to do this without cursors.
Any Help would be appreciated !
You will want to use a split function:
create FUNCTION [dbo].[Split](#String varchar(MAX), #Delimiter char(1))
returns #temptable TABLE (items varchar(MAX))
as
begin
declare #idx int
declare #slice varchar(8000)
select #idx = 1
if len(#String)<1 or #String is null return
while #idx!= 0
begin
set #idx = charindex(#Delimiter,#String)
if #idx!=0
set #slice = left(#String,#idx - 1)
else
set #slice = #String
if(len(#slice)>0)
insert into #temptable(Items) values(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end;
And then you can query the data in the following manner:
select items, count(items)
from table1 t1
cross apply dbo.split(t1.id, ',')
group by items
See SQL Fiddle With Demo
Well, the solution i always use, and probably there might be a better way, is to use a function that will split everything. No use for cursors, just a while loop.
if OBJECT_ID('splitValueByDelimiter') is not null
begin
drop function splitValueByDelimiter
end
go
create function splitValueByDelimiter (
#inputValue varchar(max)
, #delimiter varchar(1)
)
returns #results table (value varchar(max))
as
begin
declare #delimeterIndex int
, #tempValue varchar(max)
set #delimeterIndex = 1
while #delimeterIndex > 0 and len(isnull(#inputValue, '')) > 0
begin
set #delimeterIndex = charindex(#delimiter, #inputValue)
if #delimeterIndex > 0
set #tempValue = left(#inputValue, #delimeterIndex - 1)
else
set #tempValue = #inputValue
if(len(#tempValue)>0)
begin
insert
into #results
select #tempValue
end
set #inputValue = right(#inputValue, len(#inputValue) - #delimeterIndex)
end
return
end
After that you can call the output like this :
if object_id('test') is not null
begin
drop table test
end
go
create table test (
Id varchar(max)
)
insert
into test
select '10031,10042'
union all select '10064,10023,10060,10065,10003,10011,10009,10012,10027,10004,10037,10039'
union all select '10009'
union all select '20011,10027,10032,10063,10023,10033,20060,10012,10020,10031,10011,20036,10041'
select value
from test
cross apply splitValueByDelimiter(Id, ',')
Hope it helps, although i am still looping through everything
After reiterating the comment above about NOT putting multiple values into a single column (Use a separate child table with one value per row!),
Nevertheless, one possible approach: use a UDF to convert delimited string to a table. Once all the values have been converted to tables, combine all the tables into one table and do a group By on that table.
Create Function dbo.ParseTextString (#S Text, #delim VarChar(5))
Returns #tOut Table
(ValNum Integer Identity Primary Key,
sVal VarChar(8000))
As
Begin
Declare #dlLen TinyInt -- Length of delimiter
Declare #wind VarChar(8000) -- Will Contain Window into text string
Declare #winLen Integer -- Length of Window
Declare #isLastWin TinyInt -- Boolean to indicate processing Last Window
Declare #wPos Integer -- Start Position of Window within Text String
Declare #roVal VarChar(8000)-- String Data to insert into output Table
Declare #BtchSiz Integer -- Maximum Size of Window
Set #BtchSiz = 7900 -- (Reset to smaller values to test routine)
Declare #dlPos Integer -- Position within Window of next Delimiter
Declare #Strt Integer -- Start Position of each data value within Window
-- -------------------------------------------------------------------------
-- ---------------------------
If #delim is Null Set #delim = '|'
If DataLength(#S) = 0 Or
Substring(#S, 1, #BtchSiz) = #delim Return
-- --------------------------------------------
Select #dlLen = DataLength(#delim),
#Strt = 1, #wPos = 1,
#wind = Substring(#S, 1, #BtchSiz)
Select #winLen = DataLength(#wind),
#isLastWin = Case When DataLength(#wind) = #BtchSiz
Then 0 Else 1 End,
#dlPos = CharIndex(#delim, #wind, #Strt)
-- --------------------------------------------
While #Strt <= #winLen
Begin
If #dlPos = 0 Begin -- No More delimiters in window
If #isLastWin = 1 Set #dlPos = #winLen + 1
Else Begin
Set #wPos = #wPos + #Strt - 1
Set #wind = Substring(#S, #wPos, #BtchSiz)
-- ----------------------------------------
Select #winLen = DataLength(#wind), #Strt = 1,
#isLastWin = Case When DataLength(#wind) = #BtchSiz
Then 0 Else 1 End,
#dlPos = CharIndex(#delim, #wind, 1)
If #dlPos = 0 Set #dlPos = #winLen + 1
End
End
-- -------------------------------
Insert #tOut (sVal)
Select LTrim(Substring(#wind,
#Strt, #dlPos - #Strt))
-- -------------------------------
-- Move #Strt to char after last delimiter
Set #Strt = #dlPos + #dlLen
Set #dlPos = CharIndex(#delim, #wind, #Strt)
End
Return
End
Then write, (using your table schema),
Declare #AllVals VarChar(8000)
Select #AllVals = Coalesce(#allVals + ',', '') + ID
From Table Where ID Is Not null
-- -----------------------------------------
Select sVal, Count(*)
From dbo.ParseTextString(#AllVals, ',')
Group By sval

MySQL DELIMITER not working

I've tried a tutorial from this site, where a sample table gets inserted along with some testing data in a stored procedure. But unfortunately an error message is thrown, saying there's something wrong with the DELIMITER. The whole script is:
CREATE TABLE filler (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT
) ENGINE=Memory;
CREATE TABLE t_hierarchy (
id INT NOT NULL PRIMARY KEY,
parent INT NOT NULL,
lft INT NOT NULL,
rgt INT NOT NULL,
sets LineString NOT NULL,
data VARCHAR(100) NOT NULL,
stuffing VARCHAR(100) NOT NULL
) ENGINE=MyISAM;
DELIMITER $$
CREATE PROCEDURE prc_filler(cnt INT)
BEGIN
DECLARE _cnt INT;
SET _cnt = 1;
WHILE _cnt <= cnt DO
INSERT
INTO filler
SELECT _cnt;
SET _cnt = _cnt + 1;
END WHILE;
END;
CREATE PROCEDURE prc_hierarchy(width INT)
main:BEGIN
DECLARE last INT;
DECLARE level INT;
SET last = 0;
SET level = 0;
WHILE width >= 1 DO
INSERT
INTO t_hierarchy
SELECT COALESCE(h.id, 0) * 5 + f.id,
COALESCE(h.id, 0),
COALESCE(h.lft, 0) + 1 + (f.id - 1) * width,
COALESCE(h.lft, 0) + f.id * width,
LineString(
Point(-1, COALESCE(h.lft, 0) + 1 + (f.id - 1) * width),
Point(1, COALESCE(h.lft, 0) + f.id * width)
),
CONCAT('Value ', COALESCE(h.id, 0) * 5 + f.id),
RPAD('', 100, '*')
FROM filler f
LEFT JOIN
t_hierarchy h
ON h.id >= last;
SET width = width / 5;
SET last = last + POWER(5, level);
SET level = level + 1;
END WHILE;
END
$$
DELIMITER ;
START TRANSACTION;
CALL prc_filler(5);
CALL prc_hierarchy(585937);
COMMIT;
CREATE INDEX ix_hierarchy_parent ON t_hierarchy (parent);
CREATE INDEX ix_hierarchy_lft ON t_hierarchy (lft);
CREATE INDEX ix_hierarchy_rgt ON t_hierarchy (rgt);
CREATE SPATIAL INDEX sx_hierarchy_sets ON t_hierarchy (sets);
Executing this on a MySQL 5.0.51a-24+lenny2 server gives me the following error message:
[Err] 1310 - End-label $$ without match
Does anyone know why this occurs and how to fix it?
$$ should be after every created procedure.
CREATE PROCEDURE prc_filler(cnt INT)
BEGIN
DECLARE _cnt INT;
SET _cnt = 1;
WHILE _cnt <= cnt DO
INSERT
INTO filler
SELECT _cnt;
SET _cnt = _cnt + 1;
END WHILE;
END$$ -- here's your problem
That's why it is called delimiter - it separates SQL commands. You have ';' inside procedures and '$$' outside of them.
I have seen some MySQL clients that don't support the DELIMITER keyword and throw an error... I believe it is your case... You should try to use a different client, maybe the CLI tool bundled with MySQL (if you can...)?