When I call this stored procedure it shows error: unknown column...
BEGIN
if (
`LastRow.Transaction`=4 and `LastRow.Xre`>1)
then
SELECT
sleep(2);
END if;
end
Please note that sleep(2) is just to demonstrate to do something if condition is true. What would be the proper way to accomplish a test based on value of a specific record? In the above example the table (actually a View) has only one row.
Q: What would be the proper way to accomplish a test based on value of a specific record?
If you mean, based on values in columns stored in one row of a table... it seems like we would need a query that references the table that retrieve the values stored in the row. And then we can have those values available in the procedure.
As an example
BEGIN
-- local procedure variables, specify appropriate datatypes
DECLARE lr_transaction BIGINT DEFAULT NULL;
DECLARE lr_xre BIGINT DEFAULT NULL;
-- retrieve values from columns into local procedure variables
SELECT `LastRow`.`Transaction`
, `LastRow`.`Xre`
INTO lr_transaction
, lr_xre
FROM `LastRow`
WHERE someconditions
ORDER BY someexpressions
LIMIT 1
;
IF ( lr_transaction = 4 AND lr_xre > 1 ) THEN
-- do something
END IF;
END$$
That's an example of how we can retrieve a row from a table, and do some check. We could also do the check with SQL and just return a boolean
BEGIN
-- local procedure variables, specify appropriate datatypes
DECLARE lb_check TINYINT(1) UNSIGNED DEFAULT 0;
-- retrieve values from columns into local procedure variables
SELECT IF(`LastRow`.`Transaction` = 4 AND `LastRow`.`Xre` > 1,1,0)
INTO lb_check
FROM `LastRow`
WHERE someconditions
ORDER BY someexpressions
LIMIT 1
;
IF ( lb_check ) THEN
-- do something
END IF;
END$$
Related
Is there such an opportunity? Let's say, we have a range of integers. We loop over them and find out that they don't exceed 65535. So, now we know that SMALLINT would be a suitable type and are going to create a table with a column of the appropriate type! How can we do this?
I've tried various combinations like:
SET #ty := "INT";
CREATE TABLE tab (
id #ty
);
but they all have failed and ended up complaining on #ty.
So, the other question is whether we can keep in a server script #variable (as INT or as "INT")?
Thank you very much for your time!
By loop over them and find out that they don't exceed 65535 ,if you mean the highest values does not exceed 65535, then we can use max() function to get the highest value. If you mean the number of values within the range, the count() function should be used instead. Next we can perform a condition check to determine which numeric type should be used. The rest is to create the wanted table based on the result from the condition check using PREPARED STATEMENT. To illustrate , we create a table and insert values to simulate the number range. Then use a procedure to create the intended table dynamically.
create table numbers (n int);
insert numbers values(1),(2),(70000);
delimiter //
drop procedure if exists create_table //
create procedure create_table()
begin
declare num_range int;
select max(n) from numbers into num_range; -- supposing you mean the highest values does not exceed 65535
If num_range<65536 then
set #create_tb_stmt=concat('create table `tab` (id smallint);');
else
set #create_tb_stmt=concat('create table `tab` (id int);');
end if;
drop table if exists `tab` ;
PREPARE stmt FROM #create_tb_stmt;
EXECUTE stmt;
end//
call create_table // -- call the procedure to make it happen
I want to execute, in a stored procedure, a certain set of statements if, in table my_table there is exactly one row with value value in column column_name. I have tried the following, but I get a syntax error:
IF ((SELECT COUNT(*) FROM my_table WHERE column_name = value) = 1) THEN
BEGIN
END;
END IF;
For context: In my procedure I create a temporary table at some point, where I store a list of values. Then later on in the procedure, I want to check if a given value is present in that temporary table.
I think you might be better to structure it more like this
BEGIN
DECLARE myCOUNT INTEGER;
SELECT COUNT(*)
INTO myCount
FROM my_table
WHERE column_name=value;
IF (myCount = 1) THEN
-- do stuff
END IF;
END;
I'm not sure what you are trying to do, but I'll guess an "upsert" -- update a record if it exists, otherwise insert a new record.
In any case, if you are trying to ensure that name is unique in my_table, then this is not the right approach at all. Instead, declare a unique index/constraint so the database ensures the data integrity:
create unique index unq_my_table_name on my_table(name);
You can then use insert . . . on duplicate key update to modify the records in the database.
I'm not very familiar with MySQL stored procedures, but am attempting to write one for the first time. In my procedure, I have 2 in parameters and one or both of them could be null. I need to create a cursor to loop over, but my cursor needs to be based on the in parameters. Where if 1 is null and the other isn't, my cursor query is different.
For example:
CREATE PROCEDURE test (IN date timestamp, IN id int(11))
BEGIN
DECLARE cur CURSOR FOR
IF timestamp IS NOT NULL THEN
IF id IS NULL THEN
SELECT columns FROM Table WHERE modified_on <= timestamp
ELSE
SELECT columns FROM Table WHERE userid = id AND modified_on <= timestamp
ELSE
/* Logic here to pull query without the timestamp and where userid matches if the id passed in is not null */
END IF
END
Could someone show me a simple example of how to achieve this?
issue
syntax error, the declare cursor statement requires to be associated with exactly one select query :
DECLARE cursor_name CURSOR FOR select_statement
also Table is a reserved keyword and needs to be escaped ( or use a different name ) in :
SELECT columns FROM Table
to fix, either create two cursors one for each scenario or embed both query paths in one select query
setup
create table `Table`
(
id integer primary key auto_increment not null,
userid integer not null,
modified_on datetime not null
);
fix
-- option one : combine queries into one
drop procedure if exists test;
delimiter $$
CREATE PROCEDURE test (IN date timestamp, IN id int(11))
BEGIN
DECLARE cur CURSOR FOR SELECT columns FROM `Table` WHERE ( id is null or userid = id ) and modified_on <= timestamp;
-- open and read from cursor..
END$$
delimiter ;
-- option two define two cursors and use conditional logic below to decide which to read from
drop procedure if exists test;
delimiter $$
CREATE PROCEDURE test (IN date timestamp, IN id int(11))
BEGIN
DECLARE cur1 CURSOR FOR SELECT columns FROM `Table` WHERE modified_on <= timestamp;
DECLARE cur2 CURSOR FOR SELECT columns FROM `Table` WHERE userid = id AND modified_on <= timestamp;
-- evaluate if/else logic here to decide which cursor to open and use..
END$$
delimiter ;
note: not sure what you're planning todo for each cursor fetch. depending on your use case, its possible you can do this without a cursor. if this is the case, dont use a cursor and keep the processing closer to the natural sql set-based processing
reference
mysql declare cursor syntax
ansi sql reserved keywords
mysql cursors
Here is my stored procedure:
CREATE DEFINER=`root`#`localhost` PROCEDURE `User_Confirm`(id INT)
BEGIN
UPDATE
`User`
SET
ConfirmedAt = UTC_TIMESTAMP()
WHERE `Id` = id ;
END$$
If I run
`CALL `User_Confirm`(19);`
I get this message saying the whole table has been updated:
1 queries executed, 1 success, 0 errors, 0 warnings
Query: call `User_Confirm`(19)
11 row(s) affected
Execution Time : 0.040 sec
Transfer Time : 0.145 sec
Total Time : 0.186 sec
User 19 does exist, why is this happening
UPDATE
If I run this extraction from the stored procedure only one row gets updated as expect, so it definitely has something to do with the stored procedure:
UPDATE
`User`
SET
ConfirmedAt = UTC_TIMESTAMP()
WHERE `Id` = 19 ;
Your problem is that the parameter name to the stored procedure is the same as the column name. It is a good idea to prefix parameter names with something obvious. Try this:
CREATE DEFINER=`root`#`localhost` PROCEDURE `User_Confirm`(param_id INT)
BEGIN
UPDATE `User`
SET ConfirmedAt = UTC_TIMESTAMP()
WHERE `Id` = param_id ;
END$$
probably self referencing on id, try:
.... WHERE `USER`.`Id` = id ;
I think MySQL is ignoring your input argument, because the name matches the name of a column in one of the tables referenced in the UPDATE statement.
Let me rephrase that...
you think your predicate is of the form:
WHERE col = input_argument
But MySQL is actually seeing your predicate as:
WHERE col = col
or, as
WHERE input_argument = input_argument
The latter will be true for EVERY row in the table, the former will be true for every row that has a a non-null value stored in col. (A simple test will reveal whether it's the column reference takes precedence, or a variable reference takes precedence. I do know that in Oracle, DML statements in PL/SQL blocks, column references take precedence over PL/SQL variables, and there is NO warning.)
A quick test would be rename the input argument, so that it doesn't match any column name.
Another workaround may be to use a MySQL user variable, (be careful of implicit datatype conversions), e.g.
CREATE PROCEDURE `User_Confirm`(parameterId INT)
BEGIN
SET #argId := parameterId;
UPDATE `User`
SET ConfirmedAt = UTC_TIMESTAMP()
WHERE `Id` = #argId ;
END$$
MySQL is somethings* not case sensitive, would that be the reason?
I have a trigger in which I want to have a variable that holds an INT I get from a SELECT, so I can use it in two IF statements instead of calling the SELECT twice. How do you declare/use variables in MySQL triggers?
You can declare local variables in MySQL triggers, with the DECLARE syntax.
Here's an example:
DROP TABLE IF EXISTS foo;
CREATE TABLE FOO (
i SERIAL PRIMARY KEY
);
DELIMITER //
DROP TRIGGER IF EXISTS bar //
CREATE TRIGGER bar AFTER INSERT ON foo
FOR EACH ROW BEGIN
DECLARE x INT;
SET x = NEW.i;
SET #a = x; -- set user variable outside trigger
END//
DELIMITER ;
SET #a = 0;
SELECT #a; -- returns 0
INSERT INTO foo () VALUES ();
SELECT #a; -- returns 1, the value it got during the trigger
When you assign a value to a variable, you must ensure that the query returns only a single value, not a set of rows or a set of columns. For instance, if your query returns a single value in practice, it's okay but as soon as it returns more than one row, you get "ERROR 1242: Subquery returns more than 1 row".
You can use LIMIT or MAX() to make sure that the local variable is set to a single value.
CREATE TRIGGER bar AFTER INSERT ON foo
FOR EACH ROW BEGIN
DECLARE x INT;
SET x = (SELECT age FROM users WHERE name = 'Bill');
-- ERROR 1242 if more than one row with 'Bill'
END//
CREATE TRIGGER bar AFTER INSERT ON foo
FOR EACH ROW BEGIN
DECLARE x INT;
SET x = (SELECT MAX(age) FROM users WHERE name = 'Bill');
-- OK even when more than one row with 'Bill'
END//
CREATE TRIGGER clearcamcdr AFTER INSERT ON `asteriskcdrdb`.`cdr`
FOR EACH ROW
BEGIN
SET #INC = (SELECT sip_inc FROM trunks LIMIT 1);
IF NEW.billsec >1 AND NEW.channel LIKE #INC
AND NEW.dstchannel NOT LIKE ""
THEN
insert into `asteriskcdrdb`.`filtre` (id_appel,date_appel,source,destinataire,duree,sens,commentaire,suivi)
values (NEW.id,NEW.calldate,NEW.src,NEW.dstchannel,NEW.billsec,"entrant","","");
END IF;
END$$
Dont try this # home
`CREATE TRIGGER `category_before_ins_tr` BEFORE INSERT ON `category`
FOR EACH ROW
BEGIN
**SET #tableId= (SELECT id FROM dummy LIMIT 1);**
END;`;
I'm posting this solution because I had a hard time finding what I needed. This post got me close enough (+1 for that thank you), and here is the final solution for rearranging column data before insert if the data matches a test.
Note: this is from a legacy project I inherited where:
The Unique Key is a composite of rridprefix + rrid
Before I took over there was no constraint preventing duplicate unique keys
We needed to combine two tables (one full of duplicates) into the main table which now has the constraint on the composite key (so merging fails because the gaining table won't allow the duplicates from the unclean table)
on duplicate key is less than ideal because the columns are too numerous and may change
Anyway, here is the trigger that puts any duplicate keys into a legacy column while allowing us to store the legacy, bad data (and not trigger the gaining tables composite, unique key).
BEGIN
-- prevent duplicate composite keys when merging in archive to main
SET #EXIST_COMPOSITE_KEY = (SELECT count(*) FROM patientrecords where rridprefix = NEW.rridprefix and rrid = NEW.rrid);
-- if the composite key to be introduced during merge exists, rearrange the data for insert
IF #EXIST_COMPOSITE_KEY > 0
THEN
-- set the incoming column data this way (if composite key exists)
-- the legacy duplicate rrid field will help us keep the bad data
SET NEW.legacyduperrid = NEW.rrid;
-- allow the following block to set the new rrid appropriately
SET NEW.rrid = null;
END IF;
-- legacy code tried set the rrid (race condition), now the db does it
SET NEW.rrid = (
SELECT if(NEW.rrid is null and NEW.legacyduperrid is null, IFNULL(MAX(rrid), 0) + 1, NEW.rrid)
FROM patientrecords
WHERE rridprefix = NEW.rridprefix
);
END
Or you can just include the SELECT statement in the SQL that's invoking the trigger, so its passed in as one of the columns in the trigger row(s). As long as you're certain it will infallibly return only one row (hence one value). (And, of course, it must not return a value that interacts with the logic in the trigger, but that's true in any case.)
As far I think I understood your question
I believe that u can simply declare your variable inside "DECLARE"
and then after the "begin" u can use 'select into " you variable" ' statement.
the code would look like this:
DECLARE
YourVar varchar(50);
begin
select ID into YourVar from table
where ...