I want to optimize this SQL Query - mysql

I have query like this:
DELIMITER $$
USE `kpbaru`$$
DROP PROCEDURE IF EXISTS `getAllUmurPegawai`$$
CREATE DEFINER=`root`#`localhost` PROCEDURE `getAllUmurPegawai`(IN id_user VARCHAR(20),i_tahun INT)
BEGIN
DECLARE currdate INT;
DECLARE birthdate INT;
DECLARE numRows INT;
DECLARE numIteration INT;
DECLARE tempMonth INT;
DECLARE umur INT;
SET numRows = (SELECT COUNT(*) FROM pegawai);
SET numIteration = 1;
WHILE numIteration <= numRows DO
SET currdate = i_tahun;
SET birthdate = (SELECT EXTRACT(YEAR FROM (SELECT TGL_LAHIR FROM pegawai WHERE INDEXING = numIteration AND ID_USER=id_user)));
SET umur = currdate - birthdate;
SET tempMonth = (SELECT EXTRACT(MONTH FROM (SELECT TGL_LAHIR FROM pegawai WHERE INDEXING = numIteration AND ID_USER=id_user)));
IF umur < 56 THEN
UPDATE pegawai SET pegawai.STATUS_PEGAWAI='Belum Pensiun',pegawai.BULAN_PENSIUN=tempMonth,STATUS_PENSIUN=1 WHERE pegawai.INDEXING = numIteration AND ID_USER=id_user;
ELSE
IF umur = 56 THEN
UPDATE pegawai SET pegawai.STATUS_PEGAWAI='Pensiun',pegawai.BULAN_PENSIUN=tempMonth,STATUS_PENSIUN=1 WHERE pegawai.INDEXING = numIteration AND ID_USER=id_user;
ELSE
UPDATE pegawai SET pegawai.STATUS_PEGAWAI='Pensiun',pegawai.BULAN_PENSIUN=tempMonth,STATUS_PENSIUN=0 WHERE pegawai.INDEXING = numIteration AND ID_USER=id_user;
END IF;
END IF;
SET numIteration = numIteration + 1;
END WHILE;
END$$
DELIMITER ;
i want to optimize this query, because this query will search each age in eeach people. This query runs very slow if we have big data (>1000 rows). Any one know how to optimize it?
I've tried some query like this:
UPDATE pegawai AS p LEFT JOIN(SELECT INDEXING, CAST(CASE WHEN (i_tahun - EXTRACT(YEAR FROM(SELECT TGL_lAHIR FROM pegawai WHERE ID_USER=id_user))) < 56 THEN 'Belum Pensiun' ELSE 'Pensiun' END AS VARCHAR(20))AS statusPegawai, EXTRACT(MONTH FROM(SELECT TGL_LAHIR FROM pegawai WHERE ID_USER=id_user))AS bulanPensiun, CAST(CASE WHEN (i_tahun - EXTRACT(YEAR FROM(SELECT TGL_lAHIR FROM pegawai WHERE ID_USER=id_user))) <= 56 THEN 1 ELSE 0 END AS INT)AS statusPensiun FROM pegawai WHERE ID_USER=id_user GROUP BY INDEXING)AS m
ON p.ID_USER = m.ID_USER
SET p.STATUS_PEGAWAI = m.statusPegawai, p.BULAN_PENSIUN = m.bulanPensiun, p.STATUS_PENSIUN = m.statusPensiun
WHERE p.ID_USER = id_user;
but it still wrong. Here the error is:
Query :
CREATE DEFINER=`root`#`localhost` PROCEDURE `getAllUmurPegawai`(in id_user varchar(20),i_tahun int) BEGIN DECLARE currdate INT;...
Error Code : 1064
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'varchar(20))as statusPegawai, extract(month from(select TGL_LAHIR from pegawai w' at line 29
Solution will be apreciated! :D

You cannot cast to varchar use char instead.
From the mysql documentation
The CONVERT() and CAST() functions take an expression of any type and produce a result value of a specified type.
The type for the result can be one of the following values:
- BINARY[(N)]
- CHAR[(N)]
- DATE
- DATETIME
- DECIMAL[(M[,D])]
- SIGNED [INTEGER]
- TIME
- UNSIGNED [INTEGER]

If I am reading your query correctly I think you can do this in a single update statement for a user:-
UPDATE pegawai
SET STATUS_PEGAWAI = IF(i_tahun - YEAR(TGL_LAHIR) < 56, 'Belum Pensiun', 'Pensiun'),
BULAN_PENSIUN = MONTH(TGL_LAHIR),
STATUS_PENSIUN = IF(i_tahun - YEAR(TGL_LAHIR) <= 56, 1, 0)
WHERE ID_USER=id_user

Related

Select count(*) and select sum(my_data) after insert- error syntax#1064

I am a beginner in sql, and even in programming, recently I discovered the triggers, and I want to calculate the maximum number of active lines, and the total sum of active weights
ps: active records are defined with 'cancled = 0'
I had error # 1064 near the line where I declared my variable (row 2)
i am running this into PHPMYADMIN (MySQL) triggers.
BEGIN
DECLARE #max_rows int;
DECLARE #max_tare int;
SET #max_rows = SELECT COUNT(*) FROM history WHERE cancled = 0;
SET #max_tare= SELECT SUM(tare) FROM history WHERE cancled = 0;
UPDATE max_tare
SET max_row = #max_rows,
max_tare = #max_tare
WHERE id = 1
END
I was expecting that I updated line 1 with the max number of rows and the total sum of active weights but I have an error
MySQL replied: # 1064 - Syntax error near '#max_rows int; DECLARE
#max_tare int; SET #max_rows = SELECT COUNT (*) FROM his' at line 2
after Paul comment i changed the code to this :
BEGIN
DECLARE max_r int;
DECLARE max_t int;
SET max_r = SELECT COUNT(*) FROM history WHERE cancled = 0;
SET max_t = SELECT SUM(tare) FROM history WHERE cancled = 0;
UPDATE max_tare
SET max_row = max_r,
max_tare = max_t
WHERE id = 1
END
this is the new error :
MySQL replied: # 1064 - Syntax error near SELECT COUNT (*) FROM
history WHERE cancled = 0; SET max_t = SELECT SUM (tare) FR 'at line 4
ty in advance.
thanks to #paul Spiegel and #forpas for helping
this code helped solved the problem
IF(NEW.mix != '') THEN
UPDATE max_tare
SET max_row = (SELECT COUNT(*) FROM history WHERE nom LIKE NEW.mix),
max_tare = (SELECT SUM(tare) FROM history WHERE cancled = 0)
WHERE id = 1;
ELSE
UPDATE max_tare
SET max_row = (SELECT COUNT(*) FROM history WHERE cancled = 0),
max_tare = (SELECT SUM(tare) FROM history WHERE cancled = 0)
WHERE id = 1;
END IF;
END
this worked fine for me, thanks guys

All Tables cant' perform INSERT OR UPDATE

I used a stored procedure that uses a cursor to loop through and process an attendance data table on Mariadb 10.1 database after calling the procedure the first time all the tables on the database lost the ability to perform INSERT INTO or UPDATE statements unless the targeted table is truncated first, can any one tell me what went wrong and how to fix it
the procedure that caused the problem:
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `settle_attendance`()
MODIFIES SQL DATA
BEGIN
DECLARE trans_done BOOLEAN DEFAULT FALSE;
DECLARE punchid BIGINT(20);
DECLARE timein DATETIME;
DECLARE utctimein DATETIME;
DECLARE timeout DATETIME;
DECLARE utctimeout DATETIME;
DECLARE inday DATE;
DECLARE outday DATE;
DECLARE todaysdate DATE;
DECLARE attendcur CURSOR FOR
SELECT id, punch_in_utc_time, punch_in_user_time,
punch_out_utc_time, punch_out_user_time
FROM ohrm_attendance_record
ORDER BY id ASC;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET trans_done = TRUE;
OPEN attendcur;
edit_loop: LOOP
SET todaysdate = CURRENT_DATE();
FETCH attendcur INTO punchid, utctimein, timein, utctimeout, timeout;
IF trans_done THEN
CLOSE attendcur;
LEAVE edit_loop;
END IF;
SET inday = DATE(timein);
SET outday = DATE(timeout);
SET todaysdate = CURRENT_DATE();
IF (inday < todaysdate) OR (outday < todaysdate) THEN
CASE
WHEN (timein IS NULL OR timein = '')
OR (utctimein IS NULL OR utctimein = '') THEN
UPDATE ohrm_attendance_record
SET punch_in_utc_time = utctimeout,
punch_in_user_time = timeout,
state = 'PUNCHED OUT'
WHERE punchid = id;
ELSE BEGIN END;
END CASE;
CASE
WHEN (timeout IS NULL OR timeout = '')
OR (utctimeout IS NULL OR utctimeout = '') THEN
UPDATE ohrm_attendance_record
SET punch_out_utc_time = utctimein,
punch_out_user_time = timein,
state = 'PUNCHED OUT'
WHERE punchid = id;
ELSE BEGIN END;
END CASE;
END IF;
END LOOP edit_loop;
END $$
DELIMITER ;
I choose to avoid the question you asked. Instead, let's try to do the query 10 times as fast by getting rid of the pesky CURSOR. The entire Stored Procedure can be done in 2 UPDATEs, no loop:
UPDATE ohrm_attendance_record
SET punch_in_utc_time = utctimeout,
punch_in_user_time = timeout,
state = 'PUNCHED OUT'
WHERE ( timein < CURDATE() OR timeout < CURDATE() )
AND ( ( timein IS NULL OR timein = '' )
OR ( utctimein IS NULL OR utctimein = '' )
);
UPDATE ohrm_attendance_record
SET punch_out_utc_time = utctimein,
punch_out_user_time = timein,
state = 'PUNCHED OUT'
WHERE ( timein < CURDATE() OR timeout < CURDATE() )
AND ( ( timeout IS NULL OR timeout = '' )
OR ( utctimeout IS NULL OR utctimeout = '' )
);
I am, however, suspicious of your tests against timein and timeout.
The queries would be easier to read if you settled on either NULL or '' for missing times.
If you store only UTC values in a TIMESTAMP, you can let the user's timezone take care of coverting to local time -- this would eliminate quite a few columns and simplify the UPDATEs.
I'll make a stab at the question... Do SHOW CREATE PROCEDURE settle_attendance;, you may find that the CHARACTER SET or COLLATION is inconsistent with what you think it should be.

stored procedure returns wrong value

I have a stored procedure that keeps giving me wrong answer. I asked the procedure to return the value of motor insurance. I run the procedure and give me the total of motor insurance premium but if I run it for the 4th time it give me the ageRange select statement value.
I moved the code into a new procedure but still the same.
My code
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `cal_motor_qoute`(in
coverID int , in dob date,
in sumMotor double , out QMsg varchar(200))
BEGIN
declare policy_cover , total , insRatio, ageExtra double;
declare ageRange int;
declare price_list varchar(200);
SELECT DATEDIFF(NOW(),dob) / 365.25 AS ageRange from dual;
if (coverID = 1) then
set policy_cover = 0.002;
elseif (coverID = 2) then
set policy_cover = 0.0025;
elseif (coverID = 3) then
set policy_cover = 0.003;
elseif (coverID = 4) then
set policy_cover = 0.0035;
end if;
if ( ageRange < 25) then
set ageExtra = 0.0005;
else
set ageExtra = 0.000;
end if;
set insRatio = policy_cover + ageExtra;
set total = (sumMotor * insRatio )* 10;
set QMsg = concat('total Premium is: ',total);
select #QMsg;
END
Any help please..
SELECT DATEDIFF(NOW(),dob) / 365.25 AS ageRange from dual;
will not set the variable ageRange, but it will do a select (of the calculated value) and name the column of the resultset ageRange.
The (or rather: one) way to set the value of your variable is to use into:
SELECT DATEDIFF(NOW(),dob) / 365.25 into ageRange from dual;
Although this is probably not the most precise way to calculate the age of a person anyway. You might want to replace
if ( ageRange < 25) then
with
if ( dob > date_sub(now(), interval 25 year) ) then

mysql stored procedure error (1172, 'Result consisted of more than one row')

When trying to run the following stored procedure from django, I get an OperationError (1172, 'Result consisted of more than one row') Any idea what I might be doing wrong?
-- --------------------------------------------------------------------------------
-- Routine DDL
-- Note: comments before and after the routine body will not be stored by the server
-- --------------------------------------------------------------------------------
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `UpdatePrices`(IN storeId int, IN bottleSize VARCHAR(50))
BEGIN
DECLARE amount DECIMAL(10,2); DECLARE isCustom INT DEFAULT 0;
DECLARE changeType VARCHAR(50) DEFAULT 'State'; DECLARE updateType INT DEFAULT 0;
IF bottleSize = '1000 Ml' THEN
SELECT S1000IncreaseChoices INTO changeType FROM store_store WHERE StoreID = storeId;
IF changeType = 'State' THEN
SELECT updateType = 0;
END IF;
IF changeType = 'Flat' THEN
SELECT S1000IncreaseAmount INTO amount FROM store_store WHERE StoreID = storeId;
SELECT updateType = 1;
END IF;
IF changeType = 'Percent' THEN
SELECT 1 - S1000IncreaseAmount/100 INTO amount FROM store_store WHERE StoreID = storeId;
SELECT updateType = 2;
END IF;
END IF;
IF updateType = 0 THEN
update store_storeliquor SL
inner join liquor_liquor LL
on liquorID_id = id
set StorePrice = ShelfPrice
where BottleSize = bottleSize
and storeID_id = storeId
and custom = 0;
END IF;
IF updateType = 1 THEN
update store_storeliquor SL
inner join liquor_liquor LL
on liquorID_id = id
set StorePrice = OffPremisePrice + amount
where BottleSize = bottleSize
and storeID_id = storeId
and custom = 0;
END IF;
IF updateType = 1 THEN
update store_storeliquor SL
inner join liquor_liquor LL
on liquorID_id = id
set StorePrice = OffPremisePrice / amount
where BottleSize = bottleSize
and storeID_id = storeId
and custom = 0;
END IF;
END
I'm not sure if it matters, but I initiate the stored procedure like so:
def priceupdate(request, store_id):
cursor = connection.cursor()
cursor.callproc("UpdatePrices", (store_id, '1000 ML'))
cursor.close()
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
Your SELECT...INTO queries give result sets with more then one record. The WHERE filters are incorrect - they compare two the same values StoreID = storeId. Rename IN storeId int parementer to another name. For example - IN storeId_param int
The query will be like this -
SELECT S1000IncreaseChoices INTO changeType FROM store_store WHERE StoreID = storeId_param;
This is a Bug and you need to apply something like that:
SELECT id,data INTO x,y FROM test.t1 LIMIT 1;

mysql procedure syntax error

I get an error by sending the following query (MYSQL 5.0):
DELIMITER //
CREATE PROCEDURE relationTable ()
BEGIN
DECLARE articlecount int;
DECLARE keywordcount int;
DECLARE articlehits int;
DECLARE ac int DEFAULT 0;
DECLARE kc int;
DECLARE articleid int;
DECLARE word varchar(100);
DECLARE word_id int;
SET articlehits = 0;
SET articlecount = (SELECT count(id) from articles);
SET keywordcount = (SELECT count(id) from keywords);
outerloop: WHILE (ac < articlecount) DO
SET kc = 0;
SET articleid = (SELECT id from articles LIMIT 1 OFFSET ac);
innerloop: WHILE (kc < keywordcount) DO
IF (articlehits < 5) THEN
SELECT keyword, id INTO word, word_id from keywords LIMIT 1 OFFSET kc;
IF (0 < (SELECT COUNT(id) from articles WHERE id=articleid AND CONCAT(title, " ",text) REGEXP word)) THEN
INSERT INTO articles (id, articleID, keywordID, type) VALUES(NULL, articleid, word_id, 'type1');
SET articlehits = articlehits + 1;
END IF;
SET kc = kc + 1;
ELSE
SET kc = keywordcount;
END IF;
END WHILE innerloop;
SET ac = ac + 1;
END WHILE outerloop;
END;
//
DELIMITER ;
This produces the following error:
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right
syntax to use near 'LIMIT 1 OFFSET ac); innerloop: WHILE (kc <
keywordcount) DO TO word, word_id from' at line 15
Any idea why this happens?
(Wrote this to create a relation table between articles and keywords, to enable smart-links in article view.)
Try to remove the while labels:
WHILE (ac < articlecount) DO
SET kc = 0;
SET articleid = (SELECT id from articles LIMIT 1 OFFSET ac);
WHILE (kc < keywordcount) DO
IF (articlehits < 5) THEN
SELECT keyword, id INTO word, word_id from keywords LIMIT 1 OFFSET kc;
IF (0 < (SELECT COUNT(id) from articles WHERE id=articleid AND CONCAT(title, " ",text) REGEXP word)) THEN
INSERT INTO articles (id, articleID, keywordID, type) VALUES(NULL, articleid, word_id, 'type1');
SET articlehits = articlehits + 1;
END IF;
SET kc = kc + 1;
ELSE
SET kc = keywordcount;
END IF;
END WHILE;
SET ac = ac + 1;
END WHILE;
MySQL has connection-specific user-defined variables of the form #varname and declared procedure variables of the type you are using in your code sample. In my experience with procedures, sometimes only one of the types is allowed, and if I recall correctly, one of those situations may be when SELECTing INTO. You might try using user-defined variables here, as follows:
SET articleid = (SELECT id from articles LIMIT 1 OFFSET ac);
innerloop: WHILE (kc < keywordcount) DO
IF (articlehits < 5) THEN
SELECT keyword, id INTO #word, #word_id from keywords LIMIT 1 OFFSET kc;
Just an idea.
One problem may be the SET acticleid = (SELECT...). Try with SELECT .. INTO:
SELECT id INTO #articleid FROM articles LIMIT 1 OFFSET ac;
Variable LIMIT in stored procedures is only supported in new MySQL versions. Note that since you dont have an ORDER BY you will get a random row. It looks like you want to use a CURSOR instead. See docs.
At the risk of seeming over-critical, I believe you should rewrite this procedure to use cursors for traversing your databases, instead of individual selects with LIMIT.
see Cursors in MySQL Docs
Thank for your help so far.
The idea of Tom Haws proved to be correct. The variables for a SELECT INTO statement have to be user-defined.
I edited my code to use cursors, and user-defined variables as followed:
delimiter //
CREATE PROCEDURE relationTable ()
BEGIN
DECLARE articlehits int;
DECLARE looparticles int DEFAULT TRUE;
DECLARE loopwords int DEFAULT TRUE;
DECLARE done INT DEFAULT FALSE;
DECLARE keywordcursor CURSOR FOR SELECT keyword, id FROM keywords;
DECLARE articlecursor CURSOR FOR SELECT id FROM articles;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN articlecursor;
WHILE (looparticles) DO
FETCH articlecursor INTO #articleid;
IF done THEN SET looparticles = FALSE;
ELSE
SET articlehits = 0;
OPEN keywordcursor;
WHILE (loopwords) DO
FETCH keywordcursor INTO #word, #wordid;
IF (articlehits < 5) AND NOT done THEN
IF (0 < (SELECT COUNT(id) FROM articles WHERE id=#articleid AND CONCAT(title, " ", text) REGEXP #word)) THEN
INSERT INTO keyword_article_rel (id, meldungID, wordID) VALUES(NULL, #articleid, #wordid);
SET articlehits = articlehits + 1;
END IF;
ELSE
SET loopwords = FALSE;
CLOSE keywordcursor;
SET done = FALSE;
END IF;
END WHILE;
END IF;
END WHILE;
CLOSE articlecursor;
END;
//
delimiter ;
And now I get an other error that I really can't explain:
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near '; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN articlecursor; W' at line 6
This error confuses me because it can't have a problem with the handler. The handler is declared as in the example of the mysql-documentation. Could the problem be that I can't create two cursors like this?