I'm in a problem with the following query ..
After inserting in the table rtoxvta record by record, I need before the end of the loop eliminate the record of the vta table prevously selected.
For that i declare a new variable and assign it the vta id .. but where do I do it?
DROP PROCEDURE IF EXISTS ROWPERROW;
DELIMITER ;;
CREATE PROCEDURE ROWPERROW()
BEGIN
DECLARE n INT DEFAULT 0;
DECLARE indexx INT DEFAULT 0;
#DECLARE aux INT DEFAULT 0;
SELECT COUNT(*) FROM rto INTO n;
SET n = n +1;
SET indexx=0;
WHILE indexx<n DO
INSERT INTO rtoxvta (fecharto, importerto, fechavta, importevta)
SELECT r.fecha, r.importe, v.fecha, v.importe
FROM vta v
left outer join rto r
ON r.id = indexx
WHERE v.fecha >= r.fecha
AND (
(v.importe = r.importe) OR
(v.importe + 2 = r.importe) OR
(v.importe + 1 = r.importe) OR
(v.importe - 1 = r.importe) OR
(v.importe - 2 = r.importe))
LIMIT 1;
SET indexx = indexx + 1;
#DELETE FROM vta WHERE id = aux;
END WHILE;
End;
;;
CALL ROWPERROW();
You'll have to do it as two steps. First get the ID of the record you're going to insert into a variable, then insert that record. Then you can delete the record.
WHILE indexx<n DO
SELECT v.id, r.id INTO #v_id, #r_id
FROM vta v
join rto r
ON r.id = indexx
WHERE v.fecha >= r.fecha
AND r.importe BETWEEN v.importe - 2 AND v.importe + 2
LIMIT 1;
INSERT INTO rtoxvta (fecharto, importerto, fechavta, importevta)
SELECT r.fecha, r.importe, v.fecha, v.importe
FROM vta v
CROSS JOIN rto r
WHERE v.id = #v_id AND r.id = #r_id;
SET indexx = indexx + 1;
DELETE FROM vta WHERE id = #v_id;
END WHILE;
Also, if you're using a column from rto in the WHERE clause, you should use INNER JOIN rather than LEFT OUTER JOIN. The WHERE condition will not match any rows that don't have a match in rto, so there's no point in using an outer join.
Related
I'm trying to perform a simple update in SQL between 2 tables from different DB's. The challenge is that in order for the value to be updated it must meet certain conditions. I have used the join statements to meet the conditions and when I go to test the value from table B it is not being updated into table A. Here is what I've done so far.
USE [dbo]
GO
CREATE PROCEDURE
(
#User_ID = INT,
#Batch_ID VARCHAR(32)
)
DECLARE #locid int
SELECT #locid
FROM OtherDB.dbo.User AS UL
WHERE UL.User_ID = #User_Id
and User_Type = 1;
UPDATE M
SET
M.Number = W.Number
FROM dbo.tableA AS W
JOIN dbo.tableB AS B ON B.ID = W.ID
JOIN dbo.tableC AS C ON C.ToolA = B.ToolA
JOIN dbo.tableD as D ON D.Zone = W.Zone_Name
JOIN OtherDB.dbo.tableMax AS M ON M.LID = #locid
AND M.Tool = C.Other_Tool
AND M.Zone = D._Other_Zone
AND M.Station = W.Station
WHERE W.User_ID = #User_ID
AND W.Batch_ID = #Batch_ID
SET NOCOUNT OFF;
The update statement is updating table M in otherDB, not table A on the current database.
You might revise the stored procedure to be
Update A
Set A.Number = W.Number
From dbo.tableA A
....
I try to create a function, that returns the summary on the n-th row, where n defined by the order a query result.
This is what I have:
CREATE DEFINER=`root`#`localhost` FUNCTION `fn_inventory_stock_on_note`( fIngredient int, fInventory int, fNote int ) RETURNS float
DETERMINISTIC
BEGIN
DECLARE tStock float;
DECLARE tNoteRow int;
DECLARE tDummy int;
DECLARE rStock float;
DECLARE rRow int;
SET tNoteRow = 0;
SET #rRow := 0;
SELECT #rRow := #rRow + 1, b.ID
INTO tNoteRow, tDummy
FROM fn_inventory_book b
LEFT JOIN fn_inventory_book_in bi ON b.id = bi.bookid
LEFT JOIN fn_inventory_book_out bo ON b.id = bo.bookid
LEFT JOIN fn_dict_transactions t ON b.transactionid = t.id
WHERE b.inventoryid = fInventory
AND ( bi.ingredientid = fIngredient OR bo.ingredientid = fIngredient )
HAVING b.ID = fNote
ORDER BY b.date ASC, t.direction ASC;
SET #rRow := 0;
SET #rStock := 0;
SELECT rStock INTO tStock
FROM ( SELECT #rRow := #rRow + 1 as rRow, #rStock := #rStock + ifnull( if( bi.id is not null, bi.quantity, bo.quantity ), 0 ) * t.direction as rStock
FROM fn_inventory_book b
LEFT JOIN fn_inventory_book_in bi ON b.id = bi.bookid
LEFT JOIN fn_inventory_book_out bo ON b.id = bo.bookid
LEFT JOIN fn_dict_transactions t ON b.transactionid = t.id
WHERE b.inventoryid = fInventory
AND ( bi.ingredientid = fIngredient OR bo.ingredientid = fIngredient )
ORDER BY b.date ASC, t.direction ASC ) as q
WHERE rRow = tNoteRow;
RETURN tStock;
END
Where fIngredient and fInventory is an id for the query's filter, and fNote is a PK from the fn_inventory_book table.
The first select gets the required row's number in the order, which works fine.
My problem is in the second query, which in the subquery creates a table with the same filter and order like the first select, with the same row numbers, and the cumulative quantity of each row. And in the main query I filter that with the row number I got in the first select.
At least this is what should happen. But instead of that, when I run the function like:
select fn_inventory_stock_on_note( 1545, 18, 124167 ) as stock from dual;
It returns NULL, but if I run the second query separately with the same params, I get an amount back.
But not the right number because if I only run the second query's subquery, the row numbers aren't in order (1,2,3,6,7,4,5,8,9,10 instead of 1,2,3,..,10).
What am I doing wrong? Any help will be appreciated.
Feel free to ask if my description isn't clear.
I figured out a solution for my problem, but if anyone has a better one, please share it, because I don't think mine is the right one, although it does the job.
DROP function IF EXISTS `fn_inventory_stock_on_note`;
DELIMITER $$
CREATE DEFINER=`root`#`localhost` FUNCTION `fn_inventory_stock_on_note`( fIngredient int, fInventory int, fNote int, fItem int ) RETURNS float
DETERMINISTIC
BEGIN
DECLARE tStock float;
DECLARE tNoteRow int;
DECLARE rStock float;
DECLARE rRow int;
SET #rRow := 0;
DROP TEMPORARY TABLE IF EXISTS stock_temp;
CREATE TEMPORARY TABLE stock_temp ( num int, BookID int, ItemID int, qty float );
INSERT INTO stock_temp ( SELECT #rRow := #rRow + 1 as num, q.*
FROM ( SELECT b.id as BookID, if( bi.id is not null, bi.id, bo.id ) as ItemID,
ifnull( if( bi.id is not null, bi.quantity, bo.quantity ), 0 ) * t.direction as qty
FROM fn_inventory_book b
LEFT JOIN fn_inventory_book_in bi ON b.id = bi.bookid
LEFT JOIN fn_inventory_book_out bo ON b.id = bo.bookid
LEFT JOIN fn_dict_transactions t ON b.transactionid = t.id
WHERE b.inventoryid = fInventory
AND ( bi.ingredientid = fIngredient OR bo.ingredientid = fIngredient )
ORDER BY b.date ASC, t.direction ASC ) as q );
SELECT num INTO tNoteRow
FROM stock_temp st
WHERE st.BookID = fNote
AND st.ItemID = fItem;
SELECT sum( st.qty ) INTO tStock
FROM stock_temp st
WHERE st.num <= tNoteRow;
RETURN tStock;
END$$
DELIMITER ;
Running into an error when trying to crate a stored procedure in MySQL (5.1).
Whenever I try and run this SQL script, I am presented with:
Error 1064 (42000) At line 3: You have an error in your SQL syntax.
Near 'DECLARE checkExists INT; SET checkExists = 0; SELECT count(*) INTO c' at line 29
From what I understand, declaring a variable this way should be acceptable.
I have included the SQL script below.
DELIMITER //
CREATE PROCEDURE add_movie(
IN movieTitle VARCHAR(100),
IN movieYear INT,
IN movieDirector VARCHAR(100),
IN movieBannerURL VARCHAR(100),
IN movieTrailerURL VARCHAR(100),
IN starFirstName VARCHAR(50),
IN starLastName VARCHAR(50),
IN starDateOfBirth DATE,
IN starPhotoURL VARCHAR(200),
IN genreName VARCHAR(32),
OUT movieAdded INT,
OUT starAdded INT,
OUT genreAdded INT,
OUT movieStarLinkAdded INT,
OUT movieGenreLinkAdded INT)
LANGUAGE SQL
NOT DETERMINISTIC
SQL SECURITY INVOKER
COMMENT 'Adds Movie, Star, Genre and respective links to DB if they do not exist'
BEGIN
SET movieAdded = 0;
SET starAdded = 0;
SET genreAdded = 0;
SET movieStarLinkAdded = 0;
SET movieGenreLinkAdded = 0;
DECLARE checkExists INT;
SET checkExists = 0;
SELECT count(*) INTO checkExists FROM movies m WHERE m.title = movieTitle;
IF(checkExists > 0) THEN
INSERT INTO movies(title, year, director, banner_url, trailer_url)
VALUES (movieTitle, movieYear, movieDirector, movieBannerURL, movieTrailerURL);
SET movieAdded = 1;
END IF;
SET checkExists = 0;
SELECT count(*) INTO checkExists FROM stars s WHERE s.first_name = starFirstName AND s.last_name = starLastName;
IF(checkExists > 0) THEN
INSERT INTO stars(first_name, last_name, dob, photo_url)
VALUES (starFirstName, starLastName, starDateOfBirth, starPhotoURL);
SET starAdded = 1;
END IF;
SET checkExists = 0;
SELECT count(*) INTO checkExists FROM genres g WHERE g.name = genreName;
IF(checkExists > 0) THEN
INSERT INTO genres(name)
VALUES (genreName);
SET genreAdded = 1;
END IF;
SET checkExists = 0;
SELECT count(*) INTO checkExists FROM stars_in_movies sm, stars s, movies m
WHERE m.title = movieTitle AND s.first_name = starFirstName
AND s.last_name = starLastName AND sm.star_id = s.id AND sm.movie_id = m.id;
IF(checkExists > 0) THEN
INSERT INTO stars_in_movies(star_id, movie_id)
VALUES(
SELECT s.id, m.id FROM stars s, movies m WHERE s.first_name = starFirstName
AND s.last_name = starLastName AND m.title = movieTitle);
SET movieStarLinkAdded = 1;
END IF;
SET checkExists = 0;
SELECT count(*) INTO checkExists FROM genres_in_movies gm, genres g, movies m
WHERE m.title = movieTitle AND genre.name = genreName
AND gm.movie_id = m.id AND gm.genre_id = g.id;
IF(checkExists > 0) THEN
INSERT INTO genres_in_movies(genre_id, movie_id)
VALUES(
SELECT g.id, m.id FROM genres g, movies m
WHERE g.name = genreName AND m.title = movieTitle);
SET movieGenreLinkAdded = 1;
END IF;
END //
DELIMITER ;
If someone could help me understand what I am doing incorrectly here, I would greatly appreciate it. Thank you.
Move all DECLARE statements to the start of a BEGIN .. END block, next to BEGIN.
See documentation: https://dev.mysql.com/doc/refman/5.6/en/declare.html
DECLARE is permitted only inside a BEGIN ... END compound statement and must be at its start, before any other statements.
So I have a routine that does (pseudo-code):
$rows = SELECT DISTINCT a, b FROM t1;
foreach($rows as $row) {
SET #i = 0;
UPDATE t1 SET c_index = #i := (#i+1)
WHERE a = $row[a] and b = $row[b] ORDER BY c DESC;
}
The point is to number a subset of rows by the way they are sorted. Works fine, but this is update runs thousands of times so is pretty slow. To speed it up I want to put it in a stored procedure.
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE vA, vB, i INT;
DECLARE cur1 CURSOR FOR
SELECT DISTINCT a, b
FROM t1 WHERE ...;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur1;
WHILE done = 0 DO
FETCH cur1 INTO vA, vB;
IF done = 0 THEN
SET i=0;
UPDATE t1
SET c_index = i:= (i+1)
WHERE a = vA AND b = vB
ORDER BY c DESC;
END IF;
END WHILE;
CLOSE cur1;
END
However, when creating the above procedure MySQL says there is a syntax error at "i := ...". If I use a session variable for 'i' it works (#i). Using a session variable in a stored procedure is not very elegant. How can the above stored procedure be fixed without using a session variable for 'i'?
UPDATED: Since MySQL cursors are not updatable (read-only) there is no much sense of using one in your case. Almost everything you can possibly think of can be expressed just by pure UPDATE statement(s).
If (a, b, c) are unique you can do it this way
UPDATE table1 t JOIN
(
SELECT a, b, c,
(
SELECT 1 + COUNT(*)
FROM table1
WHERE a = t.a
AND b = t.b
AND c > t.c
) rnum
FROM table1 t
) s
ON t.a = s.a
AND t.b = s.b
AND t.c = s.c
SET t.c_index = s.rnum
Here is SQLFiddle demo
or if you happen to have some sort of id (e.g. auto_increment column) then
UPDATE table1 t JOIN
(
SELECT id,
(
SELECT 1 + COUNT(*)
FROM table1
WHERE a = t.a
AND b = t.b
AND c > t.c
) rnum
FROM table1 t
) s
ON t.id = s.id
SET t.c_index = s.rnum
Here is SQLFiddle demo
read_loop:
LOOP
FETCH device_cur INTO device;
IF done1 THEN
LEAVE read_loop;
END IF;
set x = start;
repeat
SET tripcount = 0;
SET trip_previous = 0;
SELECT MAX(distinct(trip)) into tripcount from model_erv40 where date(log_time) = date(x) and device_id = device;
IF tripcount > 0 THEN
set y = 1;
repeat
SET cd = 0;
SELECT IFNULL(MAX(cumulative_distance), 0) into cd from model_erv40 where trip = y and date(log_time) = date(x) and device_id = device;
if cd > 0 then
if trip_previous = 0 then
set cdold = 0;
set cdcorrect = cd;
else
SELECT IFNULL(MAX(cumulative_distance), 0) into cdold from model_erv40 where trip = trip_previous and date(log_time) = date(x) and device_id = device;
set cdcorrect = cd - cdold;
end if;
SELECT users.id, users.verified INTO user_idv, verified FROM users INNER JOIN devices ON users.id = devices.user_id AND devices.id = device;
SELECT IFNULL(attribute_value, '') INTO group_code from user_attributes ua where ua.user_id = user_idv and ua.attribute_id = 1;
INSERT INTO trip_info(trip, user_id, device_id, IsVerified, groupcode, date, cumulative_distance) VALUES (y, user_idv, device, verified, IFNULL(group_code,''), x, ROUND(cdcorrect*0.0006214,2));
SET trip_previous = y;
end if;
set y = y + 1;
until y > tripcount
end repeat;
END IF;
set x = date_add(x, interval 1 day);
until x > enddate
end repeat;
END LOOP;
If i am removing the statement
SELECT g.groupcode INTO group_code FROM users u INNER JOIN
user_attributes ua ON u.id = ua.user_id AND ua.attribute_id = 1 AND
u.id = user_id INNER JOIN groupcode_master g ON ua.attribute_value =
g.groupcode;
then loop works fine and i am getting data for all the device but after adding that afore said statement i am getting data for only one device that means the loop is not continuing but i want to loop through all the device with the above statement. Please Help me ..Thanks in advance.
I have two notes:
...
INNER JOIN user_attributes ua ON u.id = ua.user_id AND ua.attribute_id = 1 AND u.id = user_id
...
'u.id = user_id' - if 'user_id' is a declared variable, it should be renamed because there is a field with the same name 'user_id'
ua.attribute_id = 1 - shouldn't it be in a WHERE clause?