im trying to make a multiple statement query like this :
// without the second insert the query works fine.
// i need 2 querys to work because later, i'll do inserts on different kind of tables.
// that's why i need 2 querys, not a single query which insert 2 records.
with ZQuery1 do
begin
SQL.Clear;
SQL.Add('insert into client (name,age) values ('+QuotedStr('john')+','+QuotedStr('20')+');');
SQL.Add('insert into client (name,age) values ('+QuotedStr('doe')+','+QuotedStr('21')+');');
ExecSQL;
end;
i got this error message : SQL Error: 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 'insert into client (name,age) values ('doe','21')' at line 2;
i already check the manual, The components TZQuery and TZUpdateSql (from zeos lib ) provide the possibility to execute multiple statements, internally.
EDIT [SOLVED]
Thank you GregD, after run several tests, transactions works fine for me!
that's how i use, to help others in the future.
try
ZConnection.AutoCommit := True;
ZConnection.StartTransaction;
With ZQuery Do
begin
SQL.Clear;
SQL.Add('insert into clients (name,age) values ('+QuotedStr('john')+','+QuotedStr('20')+')');
ExecSQL;
SQL.Clear;
SQL.Add('insert into clients (name,age) values ('+QuotedStr('doe')+','+QuotedStr('21')+')');
ExecSQL;
end;
ZConnection.Commit;
except
ZConnection.Rollback
end;
This is how AutoCommit property actually works in Zeos:
when AutoCommit is True, then the transactions are commited automatically after each executed SQL statement, but you can use the StartTransaction command explicitely to prevent this auto commiting, until you explicitely call Commit.
when AutoCommit is False, you should not call StartTransaction. Then the transaction is started automatically, but it will not commit automatically after every executed statement.
procedure StartTransaction The StartTransaction procedure starts a new transaction within the connected database. It should be only used when AutoCommit property is TRUE. Whenever you try to call it with AutoCommit set to false, an SInvalidOpInNonAutoCommit will be raised. This behaviour is expected, as StartTransaction should be used as a escape to the AutoCommit mode. When you call StartTransaction, the AutoCommit is "turned off", and then, when you call Commit or Rollback, the AutoCommit is "turned on" again. If you're working with AutoCommit set to false, new transactions are created automatically and you choose how you will close them (Commit or Rollback).
procedure Commit Commit current statements to the database. Should be used only in non-AutoCommit mode (where every statement is auto-commited, making this procedure useless) or when you are in AutoCommit mode and want to finish a transaction opened by StartTransaction procedure. Commiting finishes the current transaction, if there's any. If you don't want to save your satatements to the database, you should use the Rollback procedure.
procedure Rollback Rolls back all previous statements in current transaction. Should be used only in non-AutoCommit mode (where every statement is auto-commited, making this procedure useless) or when you are in AutoCommit mode and want to finish a transaction opened by StartTransaction procedure. The Rollback finishes the current transaction, if there's any. If you don't want to loose your satatements, you should use the Commit procedure.
I have no idea about Zeos and multiple statements, but that's not really the problem here. You've caused a major security issue with your query (SQL injection) and a slow method of performing them at all (concatenated strings that can't be cached and reused).
If you properly stop using string concatenation to form your queries, and use parameterized statements instead, you don't need to worry about the multiple statements at all:
with ZQuery1 do
begin
SQL.Clear;
SQL.Add('insert into client (name,age)');
SQL.Add('values (:Name, :Age);'
ParamByName('Name').AsString := 'John';
ParamByName('Age').AsInteger := 20;
ExecSQL;
ParamByName('Name').AsString := 'Doe';
ParamByName('Age').AsInteger :- 21;
ExecSQL;
end;
The query will now run faster (because the DBMS can compile it once and reuse it multiple times (the "caching" I mentioned), you don't have the SQL injection risk any longer, and the multiple statements are no longer needed.
Try this code and let us know if the same problem arises:
with ZQuery1 do
begin
SQL.Clear;
SQL.Add('insert into client (name,age) values ('+QuotedStr('john')+','+QuotedStr('20')+'),('+QuotedStr('doe')+','+QuotedStr('21')+');');
ExecSQL;
end;
This way you can also speed up the MySQL handling of this INSERT query, as it does in one batch and not twice.
EDIT #1:
I'm not an expert in Zeos, but with other languages, you could try to execute the query one by one:
with ZQuery1 do
begin
SQL.Clear;
SQL.Add('insert into client (name,age) values ('+QuotedStr('john')+','+QuotedStr('20')+');');
ExecSQL;
SQL.Clear;
SQL.Add('insert into client (name,age) values ('+QuotedStr('doe')+','+QuotedStr('21')+');');
ExecSQL;
end;
EDIT #2: Transactions
One question on Stackoverflow has many good examples about using transactions in MySQL. Although, the examples are written for PHP, I'm sure you could find some good pointers there. Make sure that your tables on the MySQL server are InnoDB not MyISAM.
I'm not an expert in ZEOS either, but looking at the source, have you set MultiStatements property of TZUpdateSQL to true?
Have you tried TZSQLProcessor? Said that the component was made for such needs ( as in ZSqlProcessor.pas unit):
{**
Implements a unidatabase component which parses and executes SQL Scripts.
}
Related
I am struggling with this compound mysql. I am using the Start Transaction for the first time. So anything will be really helpful.
START TRANSACTION
INSERT
INTO
p_ucourse(
course_name,
course_goal,
course_time,
course_creator_id,
course_status
)
VALUES(
'This Course',
'Goal of this course',
480,
1,
1
);
SET
ucourse_id = LAST_INSERT_ID();
INSERT
INTO
r_ucourse_module(course_id,
module_id,
rank)
VALUES(ucourse_id, 1, 1);
INSERT
INTO
r_ucourse_eu(
course_id,
lu_id,
rank,
afterclass
)
VALUES(ucourse_id, 1, 1, 0);
COMMIT
And it throws up the following error:
#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 'INSERT
INTO
p_ucourse(
course_name,
course_goal,
course_time,' at line 2
I am not being able to get the syntax of the thing.
Missing semi-colon at the end of the first line. Ditto with Commit Try:
START TRANSACTION;
..
..
COMMIT;
See START TRANSACTION, COMMIT, and ROLLBACK Syntax for a few examples.
...
...
as for your other issue, consider the below:
create table MyThings
( id int auto_increment primary key,
thing varchar(100) not null
);
insert MyThings(thing) values ('Fred, the pet Anchovy');
SET ucourse_id = LAST_INSERT_ID(); -- Error 1193: unknown sys var ...
SET #ucourse_id = LAST_INSERT_ID(); -- YIPPIE, not a problem (user variable)
So, the first one above (SET ucourse_id) choked, because it was assumed to be a LOCAL Variable (as it did not have an # sign). The whole thing was not running in a stored proc/function (I assumed). Local Variables need to have life breathed into them with a DECLARE.
but...
declare k int; -- error, can't do this outside of a store proc/func etc
So, one should read up on User Variables vs Local Variables, when and how one can use them.
Local Variables: DECLARE can be used in Stored Procedures, Functions, Events, and Triggers. But they need to occur grouped together at the top only, before any commands and typcially right after BEGIN. Otherwise, other errors will occur.
User Variables: (such as #myBirthday) No DECLARE is used with them. They are used free-wheeling with less restrictions, such as when you are just hacking around outside of Stored procs, functions, events, and triggers (but can certainly be used inside of them). Note, these are the only type of variables that will succeed with PREPARE, such as the PREPARE stmt001 FROM #theSql; part of it. This last fact is not typically figured out until one wastes a lot of time with it.
I have the following MYSQL query:
START TRANSACTION;
SELECT sport_id INTO #a FROM sports WHERE sport_id = 2 FOR UPDATE;
UPDATE sports SET sport_name = 'Table Tennis' WHERE sport_id = #a;
if (#a > 1) then
COMMIT;
ELSE
ROLLBACK;
END IF;
The problem is that it returns an error at the if statement:
#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 'if (#a > 1) then COMMIT' at line 1
I've looked on stack overflow and there is an answer showing a similar query, written in pretty much the same way, but they are using the variable without an # symbol. Removing the # for my query does not resolve the issue.
This is just a test query to try out some transactions using MYSQL, hence why the query seems a little pointless. I'm a little stuck.
MySQL doesn't recognize a statement beginning with the keyword IF as as a valid SQL statement.
The IF statement works only in the context of a compound statement (that is, a block of statements enclosed between BEGIN and END. Currently, the compound statement is only supported in the context of a stored program (stored procedure, function or trigger.)
http://dev.mysql.com/doc/refman/5.5/en/begin-end.html
For testing, try...
DELIMITER //
CREATE PROCEDURE usp_test_transaction()
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
START TRANSACTION;
-- whatever DML operations and SELECT statements you want to perform go here
IF (1=1) THEN
COMMIT;
ELSE
ROLLBACK;
END IF;
END//
DELIMITER ;
CALL usp_test_transaction;
(NOTE: I am not advocating here that transactions be handled inside of stored procedure. My personal preference is to NOT do this, and instead handle transactions at a higher level. But the example above should work; I believe MySQL does support calling START TRANSACTION, COMMIT and ROLLBACK within the context of a stored procedure.)
I have a couple of hundred triggers in mysql db which i'm migrating to Oracle db. There's an statement that i see a lot which i haven't been able to find an equivalent one in oracle.
INSERT INTO $some_kind_of_message._BD_TRIGG$ (dummy) VALUES (value);
i've seen this also in procedures and functions, and it seems to 'return' a message to the aplication who calls the procedure (i think). i'm kind of new in both RDBMS but i've been working with oracle a couple of months.
Is there an equivalent statement to replace the mysql's one in oracle ? Thanks a lot.
EDIT:
This is an example trigger of many. This one is used for login validation.
I'm not very sure about oracle's trigger syntax but that's not the problem now.
The 'case' part is still in mysql syntax. I havent been able to find information in mysql nor oracle documentation. It is not a normal insert, it is some kind of return message that mysql uses (thats what i guess) and i've seen it also in functions and procedures.
¿How can i replace it for oracle to perform the same task?
CREATE OR REPLACE TRIGGER adduser
BEFORE INSERT
ON tbl_users
FOR EACH ROW
DECLARE flag INTEGER;
begin
flag := 1;
/* validate login */
IF(LENGTH(TRIM(:NEW.login)) < 4) THEN
flag := -1;
END IF;
/* valido clave */
IF(flag = 1) THEN
IF(LENGTH(:NEW.clave) < 3) THEN
flag := -2;
END IF;
END IF;
CASE flag
WHEN -1 THEN INSERT INTO $login_less_then_4_characters._BD_TRIGG$ (dummy) VALUES ('error');
WHEN -2 THEN INSERT INTO $pass_less_then_5_characters._BD_TRIGG$ (dummy) VALUES ('error');
ELSE flag := 0;
END CASE;
END;
I think you need RAISE_APPLICATION_ERROR() function/procedure.
Syntax:
raise_application_error(error code, your error message);
Example:
raise_application_error(-20001, 'Login must have 4 characters or more');
In Oracle custom aplication error codes are between -20000 and -20999.
More information here:
Oracle PL/SQL - Raise User-Defined Exception With Custom SQLERRM
Oracle documentation: link
I'm not familiar with MySQL syntax but in Oracle it translates as
Insert into table_name(column_name) VALUES (value);
where dummy is a column in some table or parameter passed to function. You cannot use procedures, triggers in Insert statements in Oracle. You may use Function() in DML statements, e.g. SELECT your_function(dummy)..., INSERT your_function(dummy)... And function returns a value as we all know.
I am trying to find a method to rollback a transaction after it has been executed.
For example:
DECLARE #tsubaki VARCHAR(25);
SET #tsubaki = 'A Transaction';
BEGIN TRANSACTION #tsubaki
UPDATE dbo.Maka SET id = 400, name = 'inu' --notice I didn't put there where clause
COMMIT TRANSACTION
Later on I realize that I updated everything in the databse Maka instead of just the one record I originally intended.
Now I try to write code to roll it back before the update:
DECLARE #tsubaki VARCHAR(25);
SET #tsubaki = 'A Transaction';
ROLLBACK TRANSACTION #tsubaki;
Doesn't work. Bottom line: I am looking for a way to rollback a sql transaction in MS-SQL Server 2008 after the transaction has been commit and the sql has ran.
Thanks in advance.
You can't do that from T-SQL code. You will have to restore to a point in time from the log file. Note that the restore will "undo" everything to a point in time, including your transaction.
In the future you should ALWAYS back up your db before any manual update, small or large. You can also cover yourself with a little trick. Write out your update/delete code like this:
SELECT * FROM dbo.Maka
-- UPDATE dbo.Maka SET id = 400, name = 'inu'
WHERE some_identifier = some_value
Run the SELECT version first which is innocuous and when you can verify the record(s) to be updated select the code from the WHERE clause up to the UPDATE and run it.
I'm having a rather strange problem with MySQL. Trying to create a procedure to update some fields in the database (the code is below).
The problem is with the line that is currently commented. It seems that if no SELECT statements get executed during the procedure MySQL query browser will return an error code of "-1, error executing SQL query".
I tried the same thing in HeidiSQL and the error was "cannot return result set". So I suppose the question is do I always have to select something in the procedure, or is there some other thing I missed.
The query works fine when the comment is removed.
DELIMITER /
DROP PROCEDURE IF EXISTS updateFavourites /
CREATE PROCEDURE updateFavourites(quota INT)
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE artist_id,releases INT;
DECLARE c_artist Cursor FOR
SELECT Artist.id_number,COUNT(Artist.id_number) FROM Artist
JOIN CD ON CD.is_fronted_by = Artist.id_number
GROUP BY Artist.id_number;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000'
SET done=1;
IF quota > 0 THEN
OPEN c_artist;
REPEAT
FETCH c_artist INTO artist_id,releases;
IF NOT done THEN
IF releases >= quota THEN
UPDATE CD SET CD.rating='favourite' WHERE CD.is_fronted_by = artist_id;
END IF;
END IF;
UNTIL done END REPEAT;
CLOSE c_artist;
-- SELECT 'Great success';
ELSE
SELECT CONCAT('\'quota\' must be greater than 0.',' Got (',quota,')');
END IF;
END /
DELIMITER ;
Here's the sql to create the tables and some data:
DROP TABLE IF EXISTS CD;
DROP TABLE IF EXISTS Artist;
CREATE TABLE Artist (
id_number INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
);
CREATE TABLE CD (
catalog_no INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY,
is_fronted_by INT UNSIGNED,
rating ENUM ('favourite','top draw','good','so-so','poor','rubbish'),
CONSTRAINT fk_CD_Artist FOREIGN KEY (is_fronted_by) REFERENCES Artist(id_number) ON UPDATE CASCADE
);
INSERT INTO Artist VALUES(11,'Artist 1');
INSERT INTO Artist VALUES(10,'Artist 2');
INSERT INTO CD VALUES (7,11, 'top draw');
INSERT INTO CD VALUES (650,11,'good');
INSERT INTO CD VALUES (651,11,'good');
INSERT INTO CD VALUES (11,10,'favourite');
Query Browser is not for running scripts, just single query.
I tried your code by moving cursor into each query (except DELIMITER) and pressing Ctrl+Enter.
It created that stored procedure without problem. (just refresh schema on the left).
If you wish creating procedure, use menu "Script"->"Create stored procedure/function".
But better forget about QueryBrowser it is not supported at all (and actunally not useful).
If you have decent hardware and plenty resources, try Workbench 5.2 otherwise use SQLyog
Googling around, there are several reports of the same error, but little information to solve the problem. There's even a bug logged at mysql.com but it appears to have been abandoned without being resolved.
There's another StackOverflow question on the same error, but it's also unresolved.
All it means is that there is no result set from the query. Looking at the source code, it appears that sometimes an error status of MYX_SQL_ERROR is set when the query has no result set. Perhaps this is not an appropriate consequence?
I notice that when I use the mysql command-line client, it yields no error for calling a proc that returns no result set.
update: I tried to revive that MySQL bug report, and provide a good test case for them. They changed the bug from "no feedback" to "verified" -- so at least they acknowledge it's a bug in Query Browser:
[11 Dec 9:18] Sveta Smirnova
Bill,
thank you for the feedback. Verified
as described.
Although most likely this only be
fixed when MySQL Query Browser
functionality is part of MySQL
workbench.
I guess the workaround is to ignore the -1 error, or to test your stored procedures in the command-line mysql client, where the error does not occur.
The comment supposes the issue will disappear as the Query Browser functionality becomes part of MySQL Workbench. This is supposed to happen in MySQL Workbench 5.2. I'll download this beta and give it a try.
MySQL Workbench 5.2 is in Beta, but I would assume MySQL engineering can't predict when the Beta will become GA. Those kinds of predictions are hard enough under standard conditions, but there's a lot of extra uncertainty of MySQL's fate due to the unresolved Oracle acquisition.
update: Okay, I have tried MySQL Workbench 5.2.10 beta. I executed a stored procedure like this:
CREATE PROCEDURE FooProc(doquery SMALLINT)
BEGIN
IF doquery THEN
SELECT * FROM Foo;
END IF;
END
When I CALL FooProc(0) the response is no result set, and the status is simply "OK".
When I CALL FooProc(1) the response is the result of SELECT * FROM Foo as expected.
However, there's another bug related to calling procedures. Procedures may have multiple result sets, so it's hard to know when to close the statement when you execute a CALL query. The consequence is that MySQL Workbench 5.2 doesn't close the statement, and if you try to do another query (either CALL or SELECT) it gives you an error:
Commands out of sync; you can't run this command now.
MySQL doesn't support multiple concurrent open queries. So the last one must be closed before you can start a new one. But it isn't closing the CALL query. This bug is also logged at the MySQL site.
The bug about commands out of sync has been resolved. They say it's fixed in MySQL Workbench 5.2.11.
Try putting BEGIN and END blocks around the multiple statements in the IF block as such:
IF quota > 0 THEN
BEGIN
OPEN c_artist;
REPEAT
FETCH c_artist INTO artist_id,releases;
IF NOT done THEN
IF releases >= quota THEN
UPDATE CD SET CD.rating='favourite' WHERE CD.is_fronted_by = artist_id;
END IF;
END IF;
UNTIL done END REPEAT;
CLOSE c_artist;
END;
ELSE
SELECT CONCAT('\'quota\' must be greater than 0.',' Got (',quota,')');
END IF;