I have problem with my procedure. I try to take values from sales table and make a query using them.
Procedure looks like this:
DROP PROCEDURE IF EXISTS turnover;
DELIMITER $$
CREATE PROCEDURE turnover()
BEGIN
DECLARE col INT;
DECLARE q TEXT;
DECLARE i INT DEFAULT 0;
DECLARE m TEXT;
SET col = (SELECT count(DISTINCT article) FROM sales);
SET q = "SELECT article, ";
WHILE i < co DO
SET m = (SELECT DISTINCT month FROM sales LIMIT 1 OFFSET i);
SET q = q + "SUM(IF(month=" + m + ",value,NULL)) AS " + m;
IF i < (col - 1) THEN
SET q = q + ", ";
END IF;
SET i = i + 1;
END WHILE;
SET q = q + " FROM sales GROUP BY article";
EXECUTE q;
END$$
DELIMITER ;
CALL turnover();
I receive error:
Error Code: 1292. Truncated incorrect DOUBLE value: ',value,NULL)) AS '
How i can make it works?
Thanks.
The col issue was fixed or assumed in the below.
CREATE SCHEMA safe_Tuesday_01; -- safe sandbox
USE safe_Tuesday_01; -- DO the work in this db to test it
-- a fake table, we need something
create table sales
( article varchar (100) not null,
month int not null
);
Step 1, find out what the string looks like:
DROP PROCEDURE IF EXISTS turnover;
DELIMITER $$
CREATE PROCEDURE turnover()
BEGIN
DECLARE col INT;
DECLARE q TEXT;
DECLARE i INT DEFAULT 0;
DECLARE m TEXT;
SET col = (SELECT count(DISTINCT article) FROM sales);
SET q = "SELECT article, ";
WHILE i < col DO
SET m = (SELECT DISTINCT month FROM sales LIMIT 1 OFFSET i);
SET q = CONCAT(q,"SUM(IF(month=" + m + ",value,NULL)) AS ", m);
IF i < (col - 1) THEN
SET q = q + ", ";
END IF;
SET i = i + 1;
END WHILE;
SET q = CONCAT(q," FROM sales GROUP BY article");
select q;
-- EXECUTE q; -- No no no this is wrong anyway
END$$
DELIMITER ;
CALL turnover();
SELECT article, FROM sales GROUP BY article
Well that above SELECT does not look so hot. Repeatedly fix your logic in Step 1 to fix that string.
Step 2, when you fix the code above, plop in the below. Note, at the moment, it is not fixed. So do that, again, above.
But in the below, use a proper PREPARED STATEMENT which you are not.
DROP PROCEDURE IF EXISTS turnover;
DELIMITER $$
CREATE PROCEDURE turnover()
BEGIN
DECLARE col INT;
DECLARE q TEXT;
DECLARE i INT DEFAULT 0;
DECLARE m TEXT;
SET col = (SELECT count(DISTINCT article) FROM sales);
SET q = "SELECT article, ";
WHILE i < col DO
SET m = (SELECT DISTINCT month FROM sales LIMIT 1 OFFSET i);
SET q = CONCAT(q,"SUM(IF(month=" + m + ",value,NULL)) AS ", m);
IF i < (col - 1) THEN
SET q = q + ", ";
END IF;
SET i = i + 1;
END WHILE;
SET q = CONCAT(q," FROM sales GROUP BY article");
-- select q;
SET #theSQL=q;
PREPARE stmt1 FROM #theSQL;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
END$$
DELIMITER ;
When done,
DROP SCHEMA safe_Tuesday_01; -- clean up, poof, sandbox is gone
CONCAT is your friend. You missed this step. It is important that the PREPARE works against a User Variable (with an # sign) and not a Local Var (from a DECLARE) else it will blow up. So I fixed that above with the #theSQL
Again, see the MySQL Manual Page PREPARE Syntax. It is important to get your string right. That is the point of Step 1. Only then do you move on to Step 2 and using it.
It happens when SELECT DISTINCT month FROM sales doesn't return anything. On the next line, the query fragment is generated as SUM(IF(month=,value,NULL)) AS and, of course, there is an error there (maybe MySQL doesn't produce the correct error message, but this is where the error is).
And the cause of the error is the WHILE line that compares i against the unknown variable co. It should probably read:
WHILE i < col DO
But it doesn't fix the problem because col is the number of distinct values of article and you iterate from 1 to col over the distinct values of month. Most probably they are in different amounts and if the number of articles is bigger then the error will happen again.
Related
The following stored procedure is returning an empty results set:
DROP PROCEDURE IF EXISTS indexCalc;
DELIMITER ;;
CREATE PROCEDURE indexCalc()
BEGIN
DECLARE n INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
SELECT MAX(coinIndex) FROM daily INTO n;
SET i=2;
WHILE i<n DO
UPDATE daily AS d
INNER JOIN(
SELECT
A.coinId,
B.coinIndex,
A.index * (1+B.totalReturn) AS xxx
FROM
daily A
LEFT JOIN
daily B
ON B.coinIndex = A.coinIndex + 1
AND B.coinID = A.coinID) AS c
ON c.coinId = d.coinId
AND c.coinIndex = d.coinIndex
SET d.index = c.xxx
WHERE c.coinIndex = i;
SET i = i + 1;
END WHILE;
End;
;;
The inner query executes properly when I substitute 2, 3, and so on for i in the WHERE c.coinIndex = i clause. The screenshot below shows the couple of rows that I executed manually.
Any idea why my procedure isn't looping through all records and executing the query as expected?
Thanks!
can anybody help me with my sorting function - seriously I don't know how can I make it work as supposed to. :( Database is in MariaDB in Xampp. I use phpMyAdmin to execute the query.
DELIMITER $$
DROP FUNCTION IF EXISTS convRomanNumeral$$
CREATE FUNCTION convRomanNumeral (numeral CHAR(4))
RETURNS INT
BEGIN
DECLARE intnum INT;
CASE numeral
WHEN "I" THEN intnum = 1;
WHEN "II" THEN intnum = 2;
END CASE;
RETURN intnum;
END;
$$
SET #iteration = -1;
UPDATE `st0gk_docman_documents`
SET created_on = DATE('2016-06-14') + INTERVAL(#iteration := #iteration + 1) SECOND
WHERE `docman_category_id` = 141 ORDER BY convRomanNumeral(SUBSTRING(SUBSTRING_INDEX(title,'/',1),' ',-2) ASC, SUBSTRING_INDEX(title,'/',-2)+0 ASC;
So what I want to achieve is to sort documents by title. Example titles are:
Document Nr I/36/2006
Document Nr II/36/2006
Document Nr I/32/2006
Document Nr II/19/2006
After sorting them by first Roman number and then by second Arabic number I want to update the date. Code below for updating by only second Arabic number works properly:
SET #iteration = -1;
UPDATE `st0gk_docman_documents`
SET created_on = DATE('2016-06-14') + INTERVAL(#iteration := #iteration + 1) SECOND
WHERE `docman_category_id` = 141 ORDER BY SUBSTRING_INDEX(title,'/',-2)+0 ASC;
I would like to use CASE to return proper variable for Roman values. I know it's not perfect but I can't even make the CASE and FUNCTION work. What I am doing wrong? All suggestions are welcome.
The best way to do this is to add another column that has a sortable equivalent of that string. And use non-SQL code to do the parsing and building of that column before inserting into the table.
First mistake that I was making it was trying to execute the whole query at once... After taking the first lodge out of the way the debugging seemed way simpler. :D
So I created my case function to convert Roman numerals:
DELIMITER $$
DROP FUNCTION IF EXISTS convRomanNumeralSubFunction$$
CREATE FUNCTION convRomanNumeralSubFunction (numeral CHAR(1))
RETURNS INT
BEGIN
DECLARE intnum INT;
CASE numeral
WHEN "I" THEN SELECT 1 INTO intnum;
WHEN "X" THEN SELECT 10 INTO intnum;
WHEN "C" THEN SELECT 100 INTO intnum;
WHEN "M" THEN SELECT 1000 INTO intnum;
WHEN "V" THEN SELECT 5 INTO intnum;
WHEN "L" THEN SELECT 50 INTO intnum;
WHEN "D" THEN SELECT 500 INTO intnum;
END CASE;
RETURN intnum;
END;
$$
After that I declared the second function needed for conversion. I don't know if You can declare function inside function... and I didn't want to waste more time on this. For sure You can declare Function inside Procedure. Anyhow. WARNING: This function is not proof of BAD numerals like IIX. Numerals like that or will be badly counted. Also AXI will not count.
DELIMITER $$
DROP FUNCTION IF EXISTS convRomanNumeral$$
CREATE FUNCTION convRomanNumeral (numeral CHAR(10))
RETURNS INT
BEGIN
DECLARE currentintnum, previntnum, intnum, counter, numerallength INT;
SET numerallength = LENGTH(numeral);
SET counter = numerallength;
SET intnum = 0;
SET previntnum = 0;
WHILE counter > 0 DO
SET currentintnum = CAST(convRomanNumeralSubFunction(SUBSTRING(numeral,counter, 1)) as integer);
IF currentintnum < previntnum THEN
SET intnum = intnum - currentintnum;
ELSE
SET intnum = intnum + currentintnum;
END IF;
SET previntnum = currentintnum;
SET counter = counter - 1;
END WHILE;
RETURN intnum;
END;
$$
So that's it. Now You can convert all kind of Roman numerals and sort them up.
Use this to test the conversion:
SELECT convRomanNumeral("XIX");
This is example sorting code that I in the end used:
SET #iteration = -1;
UPDATE `st0gk_docman_documents`
SET created_on = DATE('2016-06-07') + INTERVAL(#iteration := #iteration + 1) SECOND
WHERE `docman_category_id` = 67 ORDER BY convRomanNumeralBreak(SUBSTRING_INDEX(SUBSTRING_INDEX(title,'/',1),' ',-1)) ASC, SUBSTRING_INDEX(title,'/',-2)+0 ASC;
Also one more thing - if You'll try to excecute this on mySQL then You have to fix this line:
SET currentintnum = CAST(convRomanNumeralSubFunction(SUBSTRING(numeral,counter, 1)) as integer);
into this:
SET currentintnum = CAST(convRomanNumeralSubFunction(SUBSTRING(numeral,counter, 1)) as SIGNED);
This code could be improved but as the #Rick James stated this should be done differently - not in as db update but in different table structure and sorting mechanism.
I need a script to run that will update 'given_name' column and change all first letters to capital..
I currently have this code.. and nothing happens..
DROP PROCEDURE IF EXISTS CAPITALIZE;
DELIMITER $$
CREATE PROCEDURE CAPITALIZE()
BEGIN
DECLARE n INT DEFAULT 0;
DECLARE i INT DEFAULT 1;
SELECT COUNT(*) INTO n FROM users;
-- SET n = (SELECT COUNT(*) FROM users);
SET i=0;
WHILE (i<=n) DO
UPDATE users SET given_name = CONCAT( UPPER(SUBSTR(given_name,1,1)), SUBSTR(given_name,2)) WHERE user_id = n;
UPDATE users SET last_name = CONCAT( UPPER(SUBSTR(last_name,1,1)), SUBSTR(last_name,2)) WHERE user_id = n;
SET i = (i + 1);
END WHILE;
END $$
DELIMITER ;
As #Drew mentioned, you might be better off just doing a blanket UPDATE statement for the entire users table:
UPDATE users
SET given_name = CONCAT( UPPER(SUBSTR(given_name,1,1)), LOWER(SUBSTR(given_name,2))),
last_name = CONCAT( UPPER(SUBSTR(last_name,1,1)), LOWER(SUBSTR(last_name,2)))
To turn off your safe update mode, which is preventing the above query from working, type this:
SET SQL_SAFE_UPDATES = 0;
Here is a working fiddle which demonstrates the above query:
SQLFiddle
When I write my sql in each statement,it works well.Now my sql is:
SELECT COUNT(*) INTO v_Point1Num from tbpoint where Point=1;
SELECT COUNT(*) INTO v_Point2Num from tbpoint where Point=2;
SELECT COUNT(*) INTO v_Point3Num from tbpoint where Point=3;
SELECT COUNT(*) INTO v_Point4Num from tbpoint where Point=4;
SELECT COUNT(*) INTO v_Point5Num from tbpoint where Point=5;
and it work well.
Now I try to change it to loop,but it is wrong,how to fix it?I wonder it is the reason that I do not use "#" like "#v".
--can not work.
CREATE `SP_Point`()
BEGIN
DECLARE v INT DEFAULT(0);
DECLARE pointlStr VARCHAR(800);
SET v = 1;
WHILE v <= 5 DO
SELECT COUNT(*) INTO
(case v
when 1 then concat('v_Point',v,'Num')
when 2 then concat('v_Point',v,'Num')
when 3 then concat('v_Point',v,'Num')
when 4 then concat('v_Point',v,'Num')
when 5 then concat('v_Point',v,'Num')
)
from tbpoint
where Point=v;
SET v = v + 1;
END WHILE;
END
I try to change it to the other way,but it is still wrong.
SET v = 1;
WHILE v <= 5 DO
set pointlStr=
'SELECT COUNT(*) INTO #v_Point'+#v+'Num from tbpoint
where Point='+#v;
prepare stmt from #pointlStr;
execute stmt;
SET v = v + 1;
END WHILE;
You are trying to create a new variables(v_Point1Num, v_Point2Num.... etc) at run time which is not possible into mysql. you must declare a variable before using it.
You can achieve the same output by running single query as well... rather then running multiple queries
SELECT Point, COUNT(*) from tbpoint
group by Point
having point > 0 and point <= 5;
Concat() function return the varchar/String not the variable name. declare only one variable "v_pointNum"... fetch the value into variable inside loop.... and in the same loop update the other table as well. –
CREATE `SP_Point`()
BEGIN
DECLARE v INT DEFAULT(0);
-- declare a variable to hold count value
DECLARE v_pointNum INT DEFAULT(0);
DECLARE serviceAttitudeLevelStr VARCHAR(800);
SET v = 1;
WHILE v <= 5 DO
SELECT COUNT(*) INTO v_pointNum from tbpoint where Point=v;
-- update another table
update <mytable> set <mycol> = v_pointNum where <condition>;
SET v = v + 1;
END WHILE;
END
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?