I'm writing stored procedures for the first time, and it's not working. The trouble is I can't see a reason for it not to work.
I'm running it through phpmyadmin 2.8.2.4. My MySQL version is 5.1. Here is the first part of the query:
create procedure under_user_type (in original_post int, out user_type int, out user_id longtext)
begin
if exists (
select *
from wp_postmeta as pm
where pm.post_id = original_post
and pm.meta_key = '_tdomf_original_poster_id'
) then
set user_type = 0;
select pm.meta_value
into user_id
from wp_postmeta as pm
where pm.post_id = original_post
and pm.meta_key = '_tdomf_original_poster_id';
elseif exists ( ...
I get the error:
#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 '' at line 9
Line 9 corresponds to that first select statement in the if exists ( ... ) then portion.
update:
I get the same error if I use:
create procedure under_user_type (in original_post int, out user_type int, out user_id longtext)
begin
if 1=1 then begin
set user_type = 0;
select pm.meta_value
into user_id
from wp_postmeta as pm
where pm.post_id = original_post
and pm.meta_key = '_tdomf_original_poster_id';
end;
Update Again:
Running the examples on this MySQL documentation page also gives me the "check your syntax near ''" error. I tried removing all tabs from the query but that did nothing.
Update a third time:
I can run this query:
create procedure blah()
begin
end;
but not this query:
create procedure blah2()
begin
if 1=1 then
begin
end;
end if;
end;
as I get the same error.
The following works for me:
create procedure blah2()
begin
declare s int;
if exists(select 1) then
set s=1;
end if;
end;
I think your problem (in the very first example, before you try doing all of your experimentation) is that you didn't declare the variable user_type, so MySQL prints out a (very generic) error when it encounters a variable name that it's never seen before on line 9.
(As for all of your other examples, you should not do if ... then begin ... end; The proper syntax is if ... then ... elseif ... else ... end if)
The error is on line 9, when it encounters the first semi-colon.
You need to set the DELIMITER to something other than semi-colon if you're going to use semi-colons inside the body of your procedure.
Here's how to do that for your simplest example:
DELIMITER $$
create procedure blah2()
begin
if 1=1 then
begin
end;
end if;
end $$
DELIMITER ;
The problem is that PHPMyAdmin must be trying to split up the queries itself every time it sees a semicolon. This is what the MySQL CLI does, which is why it's necessary to change the delimiter when creating a stored procedure on the command line. However, my version of PHPMyAdmin (2.8.2.4) doesn't allow me to change the delimiter, so I just wound up with a bunch of unhelpful error messages.
I wound up writing a script on the remote server with a textarea and submit button that would pass the contents of the textarea to mysqli::multi_query. This function lets mysql handle the delimiters rather than trying to split it up itself, and so creating the procedure worked in this respect.
Another, easier route would be to use MySQL Workbench to connect to the database, possibly through ssh if the database only allows connections from localhost.
I don't think MySQL can handle "if exists".
You could try
declare testMe integer default (select ID from ... where ...);
if testMe is not NULL then
...
Related
We want to run a simple SQL statement (dropping and creating an index), but only if the old index has not already been deleted. After looking up the syntax for IF in DB2, I came up with this:
IF EXISTS (SELECT indname FROM SYSCAT.INDEXES WHERE INDNAME = 'TEST_CREATE_INDEX_OLD')
THEN
DROP INDEX TEST_CREATE_INDEX_OLD;
create index TEST_CREATE_INDEX_NEW on example_table
(
id,
another_field
);
END IF;
When run with either SQuirrel (already setup to run with db2) or via command line, this script results in an error:
An unexpected token "IF EXISTS (SELECT indname FROM SYSCAT.INDEX" was
found following "BEGIN-OF-STATEMENT". Expected tokens may include:
"".. SQLCODE=-104, SQLSTATE=42601, DRIVER=4.23.42 SQL Code:
-104, SQL State: 42601
So - what am I doing wrong? Am I missing something, or is there another way to achieve my goal (check for $thing in database, execute appropriate query) that so far has not occured to me?
If IF statement is valid only in a compound-SQL block (i.e. inside a stored-procedure/routine/function/anonymous-block).
It's not valid standalone as your question shows, and that is why Db2 throws the -104 error.
You can look up the explanation of the sqlcode -104 by looking up SQL0104N in the free online Db2 Knowledge Center at this link.
To be able to use compound-SQL in your Squirrel-SQL tool, you need to configure squirrel to use an alternative statement terminator. Google that. In the examples below, I show a statement terminator of # (to delimit the block).
Here are two different ways to do what you want with Db2-Linux/Unix/Windows, each uses an anonymous block. Other approaches are possible.
In this example the drop-index will only run if the index-name exists in the current schema:
begin
declare v_index_exists integer default 0;
select 1 into v_index_exists
from syscat.indexes
where indname = 'TEST_CREATE_INDEX_OLD';
if v_index_exists = 1
then
execute immediate('drop index TEST_CREATE_INDEX_OLD');
execute immediate('create index TEST_CREATE_INDEX_NEW on example_table ( id, another_field)');
end if;
end#
In this example the drop-index will always run, but the block won't abort if the index does'nt exist (i.e. it will continue and not throw any error).
begin
declare v_no_such_index integer default 0;
declare not_exists condition for sqlstate '42704';
declare continue handler for not_exists set v_no_such_index=1;
execute immediate('drop index TEST_CREATE_INDEX_OLD');
execute immediate('create index TEST_CREATE_INDEX_NEW on example_table ( id, another_field)');
end#
You must use another statement delimiter, if you want to use db2 compound statement.
In Squirrel: Session -> Session Properties -> SQL -> Statement Separator = #.
Indexes in Db2 are fully qualified by 2 SYSCAT.INDEXES columns: INDSCHEMA and INDNAME. So, it's advisable to use both these fields in a SELECT statement on SYSCAT.INDEXEX as in example.
You can't use static DDL statements in a compound statement. Use EXECUTE IMMEDIATE statements instead.
Below is an example emulating UPDATE INDEX for the index in a schema equal to CURRENT SCHEMA special registry set in the session.
BEGIN
IF EXISTS (SELECT indname FROM SYSCAT.INDEXES WHERE INDSCHEMA = CURRENT SCHEMA AND INDNAME = 'TEST_CREATE_INDEX_OLD')
THEN
EXECUTE IMMEDIATE 'DROP INDEX TEST_CREATE_INDEX_OLD';
EXECUTE IMMEDIATE'
create index TEST_CREATE_INDEX_NEW on example_table
(
id,
another_field
)
';
END IF;
END
#
I'm trying to wirte a little log procedure for my database. I create a procedure with this statment:
create procedure prc_wirte_log (
in p_schema varchar(255),
in p_item varchar(255),
in p_message varchar(255)
)
begin
insert into weather.log (`schema`, item, message) values (p_schema, p_item, p_message);
end;
I get the error Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '' at line 7 0.063 sec
Why? The MySQL Workbench means Incomplet Statment: excepting ; after the insert query.
What could I do?
Multistatement procedures (assumed when BEGIN...END is present) require delimiter overrides to prevent the statements they contain from terminating the procedure definition prematurely.
Typically, you need to do something like:
DELIMITER //
CREATE PROCEDURE blah()
BEGIN
statements;
END//
DELIMITER ;
The first example on the documentation here demonstrates this (though the last two on that page seem to repeat your mistake.
If you are using WorkBench or similar tool just right click on StoredProcedures and click Create stored procedure the tool will create default structure like below and you could write your logic and hit on apply. Ensure to use semicolon at the end of the last statement (just before END).
CREATE PROCEDURE `new_procedure` ()
BEGIN
select * from tasks;
END
The Problem: I have many delete lines in a PostgreSQL script where I am deleting data related to the same item in the database. Example:
delete from <table> where <column>=180;
delete from <anothertable> where <column>=180;
...
delete from <table> where <column>=180;
commit work;
There are about 15 delete statements deleting data that references <column>=180.
I have tried to replace the 180 with a variable so that I only have to change the variable, instead of all the lines in the code (like any good programmer would do). I can't seem to figure out how to do it, and it's not working.
NOTE: I am very much a SQL novice (I rarely use it), so I know there's probably a better way to do this, but please enlighten me on how I can fix this problem.
I have used these answers to try and fix it with no luck: first second third. I've even gone to the official PostgreSQL documentation, with no luck.
This is what I'm trying (these lines are just for testing and not in the actual script):
DO $$
DECLARE
variable INTEGER:
BEGIN
variable := 101;
SELECT * FROM <table> WHERE <column> = variable;
END $$;
I've also tried just delcaring it like this:
DECLARE variable INTEGER := 101;
Whenever I run the script after replacing one of the numbers with a variable this is the error I get:
SQL Error [42601]: ERROR: query has no destination for result data
Hint: If you want to discard the results of a SELECT, use PERFORM instead.
Where: PL/pgSQL function inline_code_block line 6 at SQL statement
Can someone tell me where I'm going wrong? It would be nice to only have to change the number in the variable, instead of in all the lines in the script, and I just can't seem to figure it out.
As #Vao Tsun said, you must define a destination to your SELECT statement. Use PERFORM otherwise:
--Test data
CREATE TEMP TABLE my_table (id, description) AS
VALUES (1, 'test 1'), (2, 'test 2'), (101, 'test 101');
--Example procedure
CREATE OR REPLACE FUNCTION my_procedure(my_arg my_table) RETURNS VOID AS $$
BEGIN
RAISE INFO 'Procedure: %,%', my_arg.id, my_arg.description;
END
$$ LANGUAGE plpgsql;
DO $$
DECLARE
variable INTEGER;
my_record my_table%rowtype;
BEGIN
variable := 101;
--Use your SELECT inside a LOOP to work with result
FOR my_record IN SELECT * FROM my_table WHERE id = variable LOOP
RAISE INFO 'Loop: %,%', my_record.id, my_record.description;
END LOOP;
--Use SELECT to populate a variable.
--In this case you MUST define a destination to your result data
SELECT * INTO STRICT my_record FROM my_table WHERE id = variable;
RAISE INFO 'Select: %,%', my_record.id, my_record.description;
--Use PERFORM instead of SELECT if you want to discard result data
--It's often used to call a procedure
PERFORM my_procedure(t) FROM my_table AS t WHERE id = variable;
END $$;
--DROP FUNCTION my_procedure(my_table);
My goal is pretty straightforward - if table has rows, drop it.
Despite the fact that currently there are several similar answers none of them worked for me.
DB2 Drop table if exists equivalent
Suggested solution:
IF EXISTS (SELECT name FROM sysibm.systables WHERE name = 'mylib.mytable') THEN
DROP TABLE mylib.mytable;END IF;
Result:
SQL State: 42601 Vendor Code: -199 Message: [SQL0199] Keyword IF not expected.
Valid tokens: ( CL END GET SET CALL DROP FREE HOLD LOCK OPEN WITH ALTER BEGIN
Drop DB2 table if exists
Suggested solution:
--#SET TERMINATOR #
begin
declare statement varchar(128);
declare continue handle for sqlstate '42710' BEGIN END;
SET STATEMENT = 'DROP TABLE MYLIB.MYTABLE';
EXECUTE IMMEDIATE STATEMENT;
end #
Result:
Message: [SQL0104] Token HANDLE was not invalid. Valid tokens: HANDLER or, if replace handle with handler:
Message: [SQL0199] Keyword STATEMENT not expected. Valid tokens: SQL PATH RESULT SCHEMA CURRENT CONNECTION DESCRIPTOR.
From answer about views
Suggested solution:
DROP TABLE MY_TABLE ONLY IF EXISTS source.
Result:
Message: [SQL0104] Token ONLY was not invalid. Valid tokens: RESTRICT CASCADE
So, I wonder if an alternate solution exists. CL solution is also interesting.
I'm assuming you may want to do this more than once, so a procedure might be in order.
CREATE or replace PROCEDURE DROP_LIVE_TABLE
(in #table varchar(10)
,in #library varchar(10)
)
BEGIN
declare #stmt varchar(100);
declare #cnt int;
IF exists( select * from systables
where sys_dname = #library
and sys_tname = #table
and table_type in ('P','T')
) THEN
SELECT int(sum(number_rows))
INTO #cnt
from SYSTABLESTAT
where sys_dname = #library
and sys_tname = #table
;
IF #cnt > 0 THEN
set #stmt = 'DROP TABLE '||#library||'.'||#table||' CASCADE';
execute immediate #stmt;
END IF;
END IF;
RETURN;
END;
The CASCADE keyword causes any dependent objects such as indexes, logical files, views, or such to be deleted as well.
Here is a CL answer to this question:
PGM PARM(&FILENAME)
DCL VAR(&FILENAME) TYPE(*CHAR) LEN(10)
DCL VAR(&NUMRECS) TYPE(*DEC) LEN(10 0)
RTVMBRD FILE(&FILENAME) NBRCURRCD(&NUMRECS)
IF COND(&NUMRECS > 0) THEN(DLTF +
FILE(&FILENAME))
OUT: ENDPGM
This solution would have trouble if the physical file has dependencies such as indexes or logical files. Those dependencies would have to be deleted first.
The solution by #danny117 on the other hand does not work in all environments. For example I was unable to coerce it to work in SQuirreL client. But it does work in i Navigator. It also works in RUNSQLSTM, but I was unable to determine how to make it work with unqualified table references. If the tables are unqualified, RUNSQLSTM uses the default collection from DFTRDBCOL. The CURRENT_SCHEMA special register does not return the value from DFTRDBCOL.
Here is the if table has rows drop it solution using a compound statement:
begin
if( exists(
select 1 from qsys2.systables
where table_schema = 'MYLIB'
and table_name = 'MYTABLE'
)) then
if( exists(
select 1 from mylib.mytable
)) then
drop table mylib.mytable;
end if;
end if;
end;
I am guessing at the reason you would want to do this, but if it is to allow creation of a new table, then best way may be with a CREATE OR REPLACE TABLE if you are at IBM i v7.2 or greater.
If all you want to do is make sure you have an empty table, TRUNCATE (v7.2+) or DELETE may be better options.
Drop table if exists using atomic statement.
BEGIN ATOMIC
IF( EXISTS(
SELECT 1 FROM TABLES
WHERE TABLE_SCHEMA = 'MYLIB'
AND TABLE_NAME = 'MYTABLE'
)) THEN
DROP TABLE MYLIB/MYTABLE;
END IF;
END;
try this:
BEGIN
IF EXISTS (SELECT NAME FROM QSYS2.SYSTABLES WHERE TABLE_SCHEMA = 'YOURLIBINUPPER' AND TABLE_NAME = 'YOURTABLEINUPPER') THEN
DROP TABLE YOURLIB.YOURTABLE;
END IF;
END ;
This is a simple trigger I'm trying to create:
CREATE TRIGGER add_item_id BEFORE INSERT ON products
FOR EACH ROW
BEGIN
DECLARE max_id INTEGER;
SELECT MAX(item_id) INTO #max_id FROM products;
SET NEW.item_id = #max_id + 1;
END;
I tried it both on phpMyAdmin SQL window and mysql prompt and get the same error as below:
#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 '' at line 4
delimiter //
CREATE TRIGGER add_item_id BEFORE INSERT ON products
FOR EACH ROW
BEGIN
DECLARE max_id int;
SELECT MAX(item_id) INTO max_id FROM products;
SET NEW.item_id = max_id + 1;
END//
delimiter ;
Some notes:
If you declare (local variable) max_id, use it. #max_id is a GLOBAL variable. Any #variable can be used without declaring it, but it stays with the session as long as the session lives.
Your code is fine, you are just missing the delimiter changes. Without delimiter //, MySQL sees the CREATE TRIGGER statement ending at ..FROM PRODUCTS;, which makes it invalid
You could also do:
CREATE TRIGGER add_item_id
BEFORE INSERT
ON products
FOR EACH ROW
BEGIN
SET NEW.item_id = 1 + ( SELECT MAX(item_id)
FROM products
) ;
END;
Note: you can declare auto_incremented fields in almost all RDBMS.