MySQL If Statement and Increment - mysql

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

Related

how does mysql user defined function know a selected row was found?

a MYSQL user defined function selects a row from a table. How does the UDF code determine if the selected row was found in the table?
CREATE FUNCTION snippetFolder_folderPath(folder_id int)
RETURNS varchar(512)
BEGIN
declare vFolder_id int;
declare vParent_id int;
declare vPath varchar(512) default '';
declare vFolderName varchar(256) default '';
set vFolder_id = folder_id;
build_path:
while (vFolder_id > 0) do
/* -------- how to know this select statement returns a row?? ---------- */
select a.parent_id, a.folderName
into vParent_id, vFolderName
from SnippetFolder a
where a.folder_id = vFolder_id;
if vPath = ' ' then
set vPath = vFolderName;
else
set vPath = concat_ws( '/', vFolderName, vPath );
end if ;
set vFolder_id = vParent_id;
end while ;
return vPath;
END
https://dev.mysql.com/doc/refman/8.0/en/select-into.html says:
If the query returns no rows, a warning with error code 1329 occurs (No data), and the variable values remain unchanged.
So you could declare a continue handler on warnings, something like the example from the documentation:
BEGIN
DECLARE i INT DEFAULT 3;
DECLARE done INT DEFAULT FALSE;
retry:
REPEAT
BEGIN
DECLARE CONTINUE HANDLER FOR SQLWARNING
BEGIN
SET done = TRUE;
END;
IF done OR i < 0 THEN
LEAVE retry;
END IF;
SET i = i - 1;
END;
UNTIL FALSE END REPEAT;
END
I'll leave it to you to read the documentation and adapt that example to your table and your loop.
Alternatively, if you're using MySQL 8.0 you can use recursive common table expression:
CREATE FUNCTION snippetFolder_folderPath(vFolder_id int)
RETURNS varchar(512)
BEGIN
DECLARE vPath varchar(512) DEFAULT '';
WITH RECURSIVE cte AS (
SELECT folderName, parent_id, 0 AS height
FROM SnippetFolder WHERE folder_id = vFolder_id
UNION
SELECT f.folderName, f.parent_id, cte.height+1
FROM SnippetFolder AS f JOIN cte ON cte.parent_id = f.folder_id
)
SELECT GROUP_CONCAT(folderName ORDER BY height DESC SEPARATOR '/')
INTO vPath
FROM cte;
RETURN vPath;
END
The recursive CTE result is all the ancestors of the row matching vFolder_id, and then one can use GROUP_CONCAT() to concatenate them together as one string.

Cursor in mysql stored procedure executes only 1 iteration

I have been struck in this stored procedure for 2 days now.
Here's the code
BEGIN
DECLARE in_township_id INT DEFAULT 2;
DECLARE in_billing_month INT DEFAULT 1;
DECLARE in_billing_month_string VARCHAR(15) DEFAULT 'January';
DECLARE in_billing_year INT DEFAULT 2016;
DECLARE in_account_id INT DEFAULT 1;
DECLARE out_status INT DEFAULT 0;
DECLARE in_invoice_date DATETIME DEFAULT CURRENT_DATE();
DECLARE _no_flats INT;
DECLARE _current_month_data_exists INT;
DECLARE _flat_id INT;
DECLARE _current_billing_month, _current_billing_year INT;
DECLARE _next_billing_month, _next_billing_year INT;
DECLARE _current_balance, _new_balance FLOAT DEFAULT 0;
DECLARE _maintenance, _power_backup, _service_tax, _penalty FLOAT DEFAULT 0;
DECLARE _maintenance_price, _power_backup_price, _service_tax_price, _balance_price, _penalty_price FLOAT DEFAULT 0;
DECLARE _consumed_units INT;
DECLARE _penal_interest_remark, _maintenance_bill_remark, _power_backup_remark, _service_tax_remark, _maintenance_account_remark VARCHAR(100);
DECLARE _total_penalty, _total_maintenance, _total_power_backup, _total_service_tax FLOAT DEFAULT 0;
DECLARE _all_flats_cursor CURSOR FOR SELECT id FROM flat_resident_v WHERE township_id=2;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET _no_flats=1;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SET out_status=2;
SELECT 'An error has occurred, operation rollbacked and the stored procedure was terminated';
END;
START TRANSACTION;
SET _current_billing_month=in_billing_month;
SET _current_billing_year=in_billing_year;
SET _next_billing_month=in_billing_month;
SET _next_billing_year=in_billing_year;
IF in_billing_month=1 THEN
SET _current_billing_month=12;
SET _current_billing_year=in_billing_year-1;
END IF;
IF in_billing_month=12 THEN
SET _next_billing_month=1;
SET _next_billing_year=in_billing_year+1;
ELSE
SET _next_billing_month = in_billing_month+1;
END IF;
SELECT CONCAT('On account of billing month ', in_billing_month_string) INTO _maintenance_account_remark;
SELECT penalty, rate_maintenance, rate_power_backup, service_tax_rate
INTO _penalty, _maintenance, _power_backup, _service_tax FROM account_master WHERE id=in_account_id;
SELECT CONCAT('Charged # ', _penalty*1200, '%') INTO _penal_interest_remark;
SELECT CONCAT('Charged # Rs. ', _maintenance, '/sq. feet') INTO _maintenance_bill_remark;
SELECT CONCAT('Charged # Rs. ', _power_backup, '/unit') INTO _power_backup_remark;
SELECT CONCAT('Charged # ', _service_tax_price*100, '%') INTO _service_tax_remark;
SET out_status = 0;
INSERT INTO temp(value1, value2) VALUES('SUCCESS', 'Initialization Completed');
OPEN _all_flats_cursor;
SET _no_flats=0;
ALL_FLATS_LOOP: LOOP
FETCH _all_flats_cursor INTO _flat_id;
IF _no_flats=1 THEN
INSERT INTO temp(value1, value2) VALUES('LOOP BREAK', _no_flats);
LEAVE ALL_FLATS_LOOP;
END IF;
IF _no_flats=0 THEN
INSERT INTO temp(value1, value2) VALUES('INSIDE LOOP', _no_flats);
INSERT INTO temp(value1, value2) VALUES('FLAT_ID', _flat_id);
SELECT count(FLATS.id) INTO _current_month_data_exists FROM account_flat_ledger as FLATS
WHERE FLATS.flat_id=_flat_id AND FLATS.debit_month=in_billing_month
AND FLATS.debit_year=in_billing_year FOR UPDATE;
IF _current_month_data_exists=0 THEN
SET out_status = 1;
INSERT INTO temp(value1, value2) VALUES('_current_month_data_exists', 'FAILED: Data not exists for billing year');
LEAVE ALL_FLATS_LOOP;
END IF;
SELECT consumed_units INTO _consumed_units FROM account_power_backup_usage
WHERE flat_id=_flat_id AND month=in_billing_month AND year=in_billing_year;
INSERT INTO temp(value1, value2) VALUES('SUCCESS', 'Power Ok');
SELECT balance INTO _current_balance FROM account_flat_ledger as FLATS
WHERE FLATS.flat_id=_flat_id AND FLATS.debit_month=_current_billing_month AND FLATS.debit_year=_current_billing_year FOR UPDATE;
IF _current_balance>0 THEN
SET _penalty_price = _penalty*_current_balance;
END IF;
SET _maintenance_price = _maintenance*1000;
SET _power_backup_price = _power_backup*_consumed_units;
SET _service_tax_price = _service_tax * (_maintenance+_power_backup);
SET _new_balance = _current_balance + _penalty + _maintenance + _power_backup + _service_tax;
INSERT INTO temp(value1, value2) VALUES('SUCCESS', 'All Values Set');
UPDATE account_flat_ledger SET penal_interest=_penalty_price, maintenance_bill=_maintenance_price, service_tax=_service_tax_price,
power_backup=_power_backup_price, opening_balance=_current_balance,received_amount=0, balance=_new_balance,
debit_date=CURDATE(), penal_interest_remark=_penal_interest_remark, maintenance_bill_remark=_maintenance_bill_remark,
power_backup_remark=_power_backup_remark, service_tax_remark=_service_tax_remark
WHERE flat_id=_flat_id AND debit_month=in_billing_month AND debit_year=in_billing_year;
INSERT INTO account_flat_ledger(flat_id, debit_month, debit_year)
VALUES(_flat_id, _next_billing_month, _next_billing_year);
INSERT INTO temp(value1, value2) VALUES('SUCCESS', 'Insertion of new Flats OK');
SET _total_maintenance = _total_maintenance + (_maintenance_price
+ _power_backup_price + _service_tax_price + _penalty_price);
SET _total_penalty = _total_penalty + _penalty_price;
SET _total_power_backup = _total_power_backup + _power_backup_price;
SET _total_service_tax = _total_service_tax + _service_tax_price;
INSERT INTO temp(value1, value2) VALUES('SUCCESS', 'Flat over');
END IF;
END LOOP ;
CLOSE _all_flats_cursor;
IF out_status != 0 THEN
ROLLBACK;
END IF;
SELECT #out_status;
END IF;
COMMIT;
END
Previously I have done this whole thing using Java code (using ResultSets and PreparedStatements). But someone told me to use stored procedure for this purpose to make things handy and efficient.
Problem: The loop i.e. Cursor executes only ones, but there are 3 records present in the table.
I used logging into temp table to trace the error but couldn't find.

1172 - Result consisted of more than one row in mysql

How can I solve this problem (Result consisted of more than one row in mysql)
DROP PROCEDURE IF EXISTS `doMarksApplication`;
CREATE PROCEDURE `doMarksApplication`(
in kuser varchar(20),
out idpro int(11))
SP:BEGIN
declare no_more_rows int default FALSE;
declare total_marks decimal(10,2) default 0;
declare idfor int(11) default 0;
declare sskod int(5) default getCurSession();
declare bdata int(5) default 0;
declare nopmh varchar(20);
# Data PB [Permohonan Baru] DM [Proses Pemarkahan]
declare cur1 cursor for
select ind_nopmh from pinduk
left join pprses on pro_nopmh = ind_nopmh
where ind_sskod = sskod and
concat(pro_stats,pro_statp) in ('PB','DM') and
not exists (select mar_idnum from pmrkah where mar_nopmh = ind_nopmh)
order by ind_nopmh;
declare continue handler for not found set no_more_rows = TRUE;
begin
select count(ind_nopmh) into bdata
from pinduk
left join pprses on pro_nopmh = ind_nopmh
where ind_sskod = sskod and
concat(pro_stats,pro_statp) in ('PB','DM') and
not exists (select mar_idnum from pmrkah where mar_nopmh = ind_nopmh);
end;
begin
select count(for_idnum) into idfor from xkod_markah_00_formula
where for_stats = 'A' and
curdate() between for_tkhdr and for_tkhhg;
end;
if idfor = 1 and sskod <> 0 then
begin
select for_idnum into idfor from xkod_markah_00_formula
where for_stats = 'A' and
curdate() between for_tkhdr and for_tkhhg;
end;
begin
insert into pprmar
(pma_tkmla,pma_msmla,pma_puser,pma_sskod,pma_idfor,pma_bdata)
values
(curdate(),curtime(),kuser,sskod,idfor,bdata);
end;
begin
select last_insert_id() into idpro;
end;
open cur1;
LOOP1:loop
fetch cur1 into nopmh;
if no_more_rows then
close cur1;
leave LOOP1;
end if;
begin
call getMarksAnakPerak(nopmh,#total_perak);
call getMarksAkademik(nopmh,#total_akdmk);
call getMarksSosioekonomi(nopmh,#total_sosio);
end;
set total_marks = #total_perak + #total_akdmk + #total_sosio;
begin
insert into pmrkah
(mar_idpro,mar_nopmh,mar_idfor,mar_perak,mar_akdmk,mar_sosio,mar_total)
values
(idpro,nopmh,idfor,#total_perak,#total_akdmk,#total_sosio,total_marks);
end;
begin
update pprses
set pro_stats = 'D',
pro_statp = 'M',
pro_tkmsk = curdate(),
pro_msmsk = curtime(),
pro_kuser = kuser
where pro_nopmh = nopmh;
end;
end loop;
begin
update pprmar
set pma_tktmt = curdate(),
pma_mstmt = curtime()
where pma_idnum = idpro;
end;
end if;
END;
i have been programming in mysql for 15 years and this is easily the most confusing stored procedure i have ever seen.
None the less, one possible place for your issue is here
select for_idnum into idfor from xkod_markah_00_formula
where for_stats = 'A' and
curdate() between for_tkhdr and for_tkhhg;
I know it does not seem to be the reason but without knowing the content of the other three stored procedures you are calling this is the only candidate. You should add a limit 1 to it, and to every select into statement that reads from a table (i.e. not a sum() or a count() etc...) as that would always have the potential to cause the error you are seeing.
select for_idnum into idfor from xkod_markah_00_formula
where for_stats = 'A' and
curdate() between for_tkhdr and for_tkhhg limit 1;
In addition, you should comment out the three stored procedure calls and see if the error goes away. My guess is that the issue is one of those stored procedures due to a select into similar to above has more than one row in the result set but does not use limit 1 and does not filter properly.

MySQL Cursor Fetch not working

I have the following stored procedure that is meant to implement Dijkstra's shortest path algorithm:
CREATE PROCEDURE `Dijkstras`(IN `pids` VARCHAR(512), IN `startP` VARCHAR(8), IN `endP` VARCHAR(8), OUT `dist` DECIMAL(20,10), OUT `eset` VARCHAR(1024))
BEGIN
DECLARE currentP VARCHAR(4);
DECLARE finished INT DEFAULT 0;
DECLARE pt_from, pt_to int;
DECLARE pt_dist decimal(20,10);
DECLARE done INT DEFAULT 0;
DECLARE cur2 CURSOR FOR
select F.id as `from`, T.id as `to`, dist(F.lat, F.lng, T.lat, T.lng)
as dist
from sampledata F, sampledata T
where F.id < T.id and
find_in_set(convert(F.id, char(10)), pids) and
find_in_set(convert(T.id, char(10)), pids)
order by dist;
DECLARE CONTINUE HANDLER FOR not found SET done = 1;
SET currentP= startP;
SET eset = '';
SET dist = 0;
SET done=0;
OPEN cur2; -- this finds pariwise distances in miles.
REPEAT
FETCH cur2 INTO pt_from, pt_to, pt_dist;
SET dist= dist+pt_dist;
SET eset= CONCAT(eset, ',');
IF(currentP=pt_from OR currentP=pt_to) AND
(IN_SET(pt_from,pids) AND IN_SET(pt_to,pids)) THEN
BEGIN
SET dist= dist+ pt_dist;
SET pids= REMOVE_MEMBER(currentP, pids);
SET eset = concat(eset, ',', concat(pt_from, ':', pt_to));
IF left(eset, 1) = ',' then
SET eset = substring(eset, 2); -- remove extra comma.
END IF;
IF currentP=pt_from THEN
SET currentP=pt_to;
ELSE
SET currentP=pt_from;
END IF;
IF currentP= endP THEN
SET finished= 1;
END IF;
END;
END IF;
UNTIL done
END REPEAT;
CLOSE cur2;
END
My issue is that the cursor isn't working properly. When I fetch the current row into pt_from, pt_to, and pt_dist all I get are NULL values. The sampledata table is properly stored in the database and all the point ids in pids are also in the sampledata table. Plus this EXACT code works for another procedure, but reusing it here isn't working.
Anybody know what I'm doing wrong?
The error was that I passed in the point ids like this '12, 15, 18' with spaces in between. MySQL counts the whitespace when it parses the strings, and the id's in the table were listed without spaces. The correct way to pass in the string set is '12,15,18'.

Shuffle a string with mysql/sql

I was wondering, if there is some way to shuffle the letters of a string in mysql/sql, i.e. something like the pseudocode: SELECT SHUFFLE('abcdef')?
Couldn't find any from http://dev.mysql.com/doc/refman/5.0/en/string-functions.html and searching for it just seems to find solutions for shuffling results, not a string.
Here you go:
DELIMITER //
DROP FUNCTION IF EXISTS shuffle //
CREATE FUNCTION shuffle(
v_chars TEXT
)
RETURNS TEXT
NOT DETERMINISTIC -- multiple RAND()'s
NO SQL
SQL SECURITY INVOKER
COMMENT ''
BEGIN
DECLARE v_retval TEXT DEFAULT '';
DECLARE u_pos INT UNSIGNED;
DECLARE u INT UNSIGNED;
SET u = LENGTH(v_chars);
WHILE u > 0
DO
SET u_pos = 1 + FLOOR(RAND() * u);
SET v_retval = CONCAT(v_retval, MID(v_chars, u_pos, 1));
SET v_chars = CONCAT(LEFT(v_chars, u_pos - 1), MID(v_chars, u_pos + 1, u));
SET u = u - 1;
END WHILE;
RETURN v_retval;
END;
//
DELIMITER ;
SELECT shuffle('abcdef');
See sqlfiddle.com for the output.
Tested successfully with mariadb 10.1 (mysql 5.6 equivalent)
Edit: this solution is for Microsoft SQL Server.
As it's not allowed to use RAND() in user defined function, we create a view to use it later in our shuffle function:
CREATE VIEW randomView
AS
SELECT RAND() randomResult
GO
The actual shuffle function is as following:
CREATE FUNCTION shuffle(#string NVARCHAR(MAX))
RETURNS NVARCHAR(MAX) AS
BEGIN
DECLARE #pos INT
DECLARE #char CHAR(1)
DECLARE #shuffeld NVARCHAR(MAX)
DECLARE #random DECIMAL(18,18)
WHILE LEN(#string) > 0
BEGIN
SELECT #random = randomResult FROM randomView
SET #pos = (CONVERT(INT, #random*1000000) % LEN(#string)) + 1
SET #char = SUBSTRING(#string, #pos, 1)
SET #shuffeld = CONCAT(#shuffeld, #char)
SET #string = CONCAT(SUBSTRING(#string, 1, #pos-1), SUBSTRING(#string, #pos+1, LEN(#string)))
END
RETURN #shuffeld
END
Calling the function
DECLARE #string NVARCHAR(MAX) = 'abcdefghijklmnonpqrstuvwxyz0123456789!"ยง$%&/()='
SELECT dbo.shuffle(#string)
There is nothing in standard SQL - your best bet is probably to write a user defined function