SQL cursor & stored procedure order arrangement - mysql

I was working on a stored procedure to update the order field in a product table.
It works only the problem now is the the last item in the loop(cur), is increased twice instead of once (so dubbeled). Like so:
+-----------------+
|product + order |
|_id | |
| | |
| 1 | 0 |
| 2 | 1 |
| etc.. | etc..|
| 36 | 35 |
| 37 | 36 |
| 38 | 38 |
| |
+-----------------+
I cant figure out why. The link table(CategoryProduct) in this case goes to 38 with a category_id of 2 CALL curorder(2);
Stored procedure:
DELIMITER //
CREATE PROCEDURE curorder(
IN catid INT
)
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE i INT DEFAULT 0;
DECLARE p INT;
DECLARE cur CURSOR FOR SELECT product_id FROM test.CategoryProduct WHERE category_id = catid;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO p;
UPDATE `test`.`Product` SET `order` = i WHERE `Product`.`product_id` =p;
SET i = i + 1;
IF done THEN
LEAVE read_loop;
END IF;
END LOOP;
CLOSE cur;
END //
DELIMITER ;
The Database is a Mysql Database. Any suggestions for improving the procedure are always welcome.
Thanks in advance.
EDIT:
I already tried to place the SET i STATEMENT beneath the IF STATEMENT but with no result.

You should put:
IF done THEN
LEAVE read_loop;
END IF;
Above your update statement, the last time mysql walks trough the loop is uses the old variables because there is no new 'p'. but i is incremented.
I good way to debug stored procedures is with a log table:
CREATE TABLE procedureLog
(
id INTEGER AUTO_INCREMENT,
description TEXT,
PRIMARY KEY (id)
);
For this case you can log the update parameters with the follow query:
INSERT INTO  `test`.`procedureLog` (`id` ,`description`) VALUES (null,  CONCAT('id: ', CAST(p as CHAR), ' order: ', CAST(i as CHAR)));
Good luck!

Related

Mysql, check if all rows of each column have the same value

A friend of mine wants to check if all the rows, of each of the columns of a table, have the same value.
If they do, then print the value.
Else just print an empty string or null or something.
Imagine this table for example:
+--------+----------+-----+
| Name | Lastname | Age |
+--------+----------+-----+
| Peter | White | 30 |
| Marry | Jane | 30 |
| John | Doe | 30 |
+--------+----------+-----+
The result of the wanted query would output the following:
+--------+----------+-----+
| Name | Lastname | Age |
+--------+----------+-----+
| NULL | NULL | 30 |
+--------+----------+-----+
I tried to create a function where I would get the columns of a given table, loop through each column name and execute a query. But since I am not familiar with Mysql I obviously miss something out and I can't figure out how to achieve what I'm trying to do here.
DROP PROCEDURE IF EXISTS test;
DELIMITER //
create procedure test()
begin
declare i int(11);
declare col_name varchar(50);
declare num_rows int(11);
DECLARE col_names CURSOR FOR
SELECT column_name
FROM information_schema.columns
WHERE table_name='name_of_my_table' and table_schema='name_of_db';
select FOUND_ROWS() as num_rows;
set i = 1;
open col_names;
the_loop: LOOP
IF i > num_rows THEN
CLOSE col_names;
LEAVE the_loop;
END IF;
FETCH col_names
INTO col_name;
-- Here I would like to perform a query for each column
select count(*), col_name from name_of_my_table group by col_name;
-- Then I was thinking of making an if/ else condition to check
-- if I get more than 1 result per column, implying that
-- not all rows have the same value for this column.
SET i = i + 1;
END LOOP the_loop;
CLOSE col_names;
END//
DELIMITER ;
call test;
What this outputs is the count and the column name of the last column found, which does make sense.
I am not sure if what I am trying to do is possible with Mysql only, I can easily do that in PHP but I am wondering if I can do that with a single query as well.
Try the below query.
select if(count(distinct(Name))=1,Name,null), if(count(distinct(Lastname))=1,Lastname,null), if(count(distinct(Age))=1,Age,null)
from your_table;

premature loop exits in store procedure function works correctly but not runs for all variable

i have a single table with duplicate id.i created a table with distinct id and count of that id i want to compare and update based on that id
+----+-------+
| id | value |
+----+-------+
| 1 | 1:a |
| 1 | 1 |
| 1 | 2:b |
+----+-------+
expected output
+----+-------+
| id | value |
+----+-------+
| 1 | 1:a 1 |
| 1 | 2:b |
+----+-------+
what i have tried is i have written a procedure which executes perfectly but it prematurely exist the loop. i don't know why it exits can anyone guide me
DELIMITER //
CREATE PROCEDURE value_merge()
BEGIN
DECLARE v_val INTEGER DEFAULT 0;
DECLARE i INTEGER DEFAULT 1;
DECLARE row_cou INTEGER DEFAULT 0;
DECLARE colan INTEGER DEFAULT 0;
DECLARE mm_num varchar(20) DEFAULT "";
DECLARE merge_value varchar(132000) DEFAULT "";
DEClARE merge_cursor CURSOR FOR
SELECT Material_Number,cou FROM zz_plant_data_given_table_form_work_bhai where flag='y';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_val = 1;
OPEN merge_cursor;
get_loop: LOOP
FETCH merge_cursor INTO mm_num,row_cou;
IF v_val = 1 THEN
LEAVE get_loop;
END IF;
while i<=row_cou do
SELECT Basic_Data_Text INTO merge_value FROM `zz_plant_data_given_table_form_work` where Material_Number=mm_num and seq=row_cou;
set colan=ROUND ((LENGTH(merge_value)- LENGTH( REPLACE (merge_value, ":", "") )) / LENGTH(":"));
IF colan>0 THEN
set row_cou=row_cou-1;
ELSE
update `zz_plant_data_given_table_form_work` set sts='y' where Material_Number=mm_num and seq=row_cou;
set row_cou=row_cou-1;
update zz_plant_data_given_table_form_work set Basic_Data_Text=concat(Basic_Data_Text,' ',merge_value) where Material_Number=mm_num and seq=row_cou;
END IF;
END while;
update zz_plant_data_given_table_form_work_bhai set flag='' where Material_Number=mm_num;
END LOOP get_loop;
CLOSE merge_cursor;
END//
DELIMITER
;
This two sentences fires the NOT FOUND handler, so I suggest use another type of query to get merge_value
Change it for:
set merge_value = ( SELECT Basic_Data_Text
FROM `zz_plant_data_given_table_form_work`
where Material_Number=mm_num
and seq=row_cou
limit 1 ); /* to prevent errors*/

How do I store each date between 2 sets of dates in a separate column

I have a table A like so:
id | valid_from_date | valid_to_date |
1 | 2015-05-01 23:15:01 | 2015-05-02 10:20:23 |
2 | 2015-07-13 10:25:02 | 2015-07-14 18:20:45 |
I want to be able to get all the dates between valid_from_date & valid_to_date per id, in the below format, which I then intend to put in a new table:
id | date |
1 | 2015-05-01 |
1 | 2015-05-02 |
2 | 2015-07-13 |
2 | 2015-07-14 |
What would be the best way to achieve this?
Thank you.
I don't know it's best way or not....but it gives Required output....
create tableb as:
CREATE TABLE `tableb` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`dates` date DEFAULT NULL,
`aid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
Now,
insert into tableb(aid,dates) select id,date(valid_from_date) as dates
from tablea union select
id,date(valid_to_date) as dates from tablea order by id;
May be Stored procedure is right option :
CREATE PROCEDURE `test_procedure`()
BEGIN
DECLARE aid INT;
DECLARE avalid_from_date date;
DECLARE avalid_to_date date;
DECLARE tempdt date;
DECLARE done INT DEFAULT FALSE;
DECLARE getdates CURSOR FOR SELECT * from tablea;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN getdates;
read_loop: LOOP
FETCH getdates INTO aid,avalid_from_date,avalid_to_date;
IF done THEN
LEAVE read_loop;
END IF;
set tempdt=date(avalid_from_date);
WHILE (tempdt <= date(avalid_to_date)) do
insert into tableb(dates,aid) values( tempdt,aid);
set tempdt=tempdt+INTERVAL 1 DAY;
end while;
END LOOP;
CLOSE getdates;
END
Now,Just call test_procedure;
All the output rows will be inserted in tableb.

Select Inside Insert Statement - MySQL Cursors

I tried some, but I couldn't find the solution, somehow I managed to get this result.
Here is the query:
DELIMITER ##
CREATE PROCEDURE test1(start_date DATE,end_date DATE)
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE a INT;
DECLARE present INT;
DECLARE total INT;
-- Declare the cursor
DECLARE id CURSOR
FOR
SELECT staff_id FROM ost_staff;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
-- Open the cursor
DROP TEMPORARY TABLE IF EXISTS reports;
CREATE TEMPORARY TABLE IF NOT EXISTS reports
(
staff_id INT(10),
present INT(10),
total INT(10)
);
OPEN id;
read_loop: LOOP
FETCH id INTO a;
IF done THEN
LEAVE read_loop;
END IF;
INSERT INTO reports(staff_id,present,total)
SELECT (COUNT(I.interval_start)) AS present, DATEDIFF(DATE_ADD(end_date,INTERVAL 1 DAY),start_date) AS total
FROM effort_frequency E
RIGHT OUTER JOIN time_intervals I ON I.interval_start = E.log_date
AND E.staffid=a AND E.log_date BETWEEN start_date AND end_date
LEFT OUTER JOIN ost_holidays H ON H.holiday_date = I.interval_start
WHERE DATE_FORMAT(I.interval_start,'%a') = 'Sun' OR H.holiday_date = I.interval_start OR E.total_effortspent IS NOT NULL;
-- Close the cursor
END LOOP;
CLOSE id;
END ##
I got the below result:
+----------+-----------------+
| staff_id | present | total |
+----------+---------+-------+
| (NULL) | 23 | 24 |
| (NULL) | 22 | 24 |
+----------+---------+-------+
I'm getting (NULL) for staff_id, How can I get the staff_id's there ?
I tried using declared variable 'a' in insert statement, but at that time I got only staff_id, I didn't get the other 2 fields, I can't get the staff_id from the select inside insert statement coz there is some problem.
Now what i need is I need to insert the staff_id from the variable 'a' into that temporary table.
note: I'm really new to this stored procedure, but somehow managed till here, Its good if I get some detail on how to use the Select inside Insert including the solution for this.
Try this -
SELECT a, (COUNT(I.interval_start)) AS present, DATEDIFF(DATE_ADD(end_date,INTERVAL 1 DAY),start_date) AS total
Your INSERT requires three fields, but your SELECT statement only selects two: present and total .
Try:
SELECT E.staffid, (COUNT(I.interval_start))
AS present, DATEDIFF(DATE_ADD(end_date,INTERVAL 1 DAY),start_date) AS total

MySQL add timestamp values

I have a table in MySQL with this format: (time = timestamp on insert)
id | tid | uid | time
31 | 1 | 14 | 2011-05-19 05:42:37 //start timestamp)
41 | 1 | 14 | 2011-05-19 07:18:42 //stop timestamp)
45 | 1 | 14 | 2011-05-19 07:18:49 //start timestamp)
46 | 1 | 14 | 2011-05-19 07:28:42 //stop timestamp)
What I need is to make a select that adds the time differences like this
(41 - 31) + (46 - 45) (i'm using the id's instead of the actual time values to better understand what I need to do )
something like SELECT (something that does this) AS TotalTimeSpent WHERE tid = '1'
If you insist on using this table layout (and I really hope you change your mind, it is truly horrific), you can do it with cursors in a stored procedure.
Pseudo-code would be something like this:
CREATE PROCEDURE gettotaltime()
BEGIN
DECLARE total, curr, prev DATETIME;
DECLARE odd INT DEFAULT 1;
DECLARE done INT DEFAULT 0;
DECLARE c CURSOR FOR SELECT time FROM tbl;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN c;
read_loop: LOOP
FETCH c INTO curr;
IF odd=0 THEN
SET total=dateadd(total,datediff(curr,prev)); -- or something similar, I forget
END IF;
SET prev=curr;
SET odd=1-odd;
IF done THEN
LEAVE read_loop;
END IF;
END LOOP;
CLOSE c;
SELECT total;
END;