This is my perl code:
my $dbc = DBI->connect('DBI:mysql:test', "entcfg", "entcfg") || die "Could not connect to database: $DBI::errstr";
$dbc->{TraceLevel} = "2"; #debug mode
$dbc->{AutoCommit} = 0; #enable transactions, if possible
$dbc->{RaiseError} = 1; #raise database errors
###sql commands
my $particle_value = $dbc->prepare('CALL particle_test_value(?,?,?,?)');
my $particle_name = $dbc->prepare('CALL particle_test_name(?,?,?,?)');
my $table_test = $dbc->prepare('CALL table_test(?,?,?)');
sub actionMessage {
my ($sh,$msgobj) = #_;
my #result;
my $return_ID;
eval {
$table_test->execute(undef,"value","value"); #new item
$return_ID = $table_test->fetchrow_array(); #get new row id
};
if ($#) {
warn $#; # print the error
}
}
The mySQL transaction is as follows:
CREATE DEFINER=`root`#`localhost` PROCEDURE `table_test`(
v_id INT,
v_name VARCHAR(255),
v_value VARCHAR(255)
)
BEGIN
INSERT INTO test (name,value) VALUES (v_name,v_value);
SELECT LAST_INSERT_ID();
END
If I put $dbc->commit; after the execute or the fetchrow_array,I get a Commands out of sync error.
If I remove the AutoCommit line, the code works but I can't use transactions.
If I try to change AutoCommit during the sub, I get this error:Turning off AutoCommit failed.
Any help would be much appreciated.
You can't extract values from stored procedures like that.
Make table_test a function:
CREATE DEFINER=`root`#`localhost` FUNCTION `table_test`(
v_name VARCHAR(255),
v_value VARCHAR(255)
) RETURNS integer
BEGIN
INSERT INTO test (name,value) VALUES (v_name,v_value);
RETURN LAST_INSERT_ID();
END //
and have $table_test use it like a function:
my $table_test = $dbc->prepare('SELECT table_test(?,?,?)');
edit: MySQL stored procedures can actually return results - the result of a SELECT statement inside the procedure is sent back to the SQL client. You have found a bug in DBD::mysql. The above works as a workaround.
Related
I'd like to have a tricky SQL statement as an Event that runs every couple of minutes.
Currently, I'm doing so with Java, using 3 separate statements that executing sequentiality in a transaction connection.
Q: I don't know how to construct such an SQL statement without Java. If impossible to have a single SQL statement, I'd like to use transaction (as I'm using in Java) and rollback in case of failure in any of those separate statements.
My Case:
I have 3 tables: "Factory", "Plan", "Machine".
I want to do something as below:
1.
WHERE Machines.annualCheck == "TRUE"
SET Machine.status = "IN_ANNUAL_CHECK"
For machines that got updated I need to do the following:
2.1 Update the related factory
WHERE Factory.id == Machine.linkFactoryID
UPDATE Factory.totalActiveMachines = --1
2.2 Delete the upcoming plans that planned to be handled by the related machine
DELETE rows WHERE Plan.willHandleByMachineID = Machine.ID
p.s. I'm using MySQL
Thank you!
Update:
In following to Simonare suggestion, I tired to do the following:
DELIMITER $
CREATE PROCEDURE annualCheck(IN Machine_ID int, IN Factory_ID int)
BEGIN
UPDATE machine_table
SET machine_table.annualCheck = 'IN_ANNUAL_CHECK'
WHERE machine_table.machine_id = Machine_ID;
UPDATE factory_table
SET factory_table.totalActiveMachines = factory_table.totalActiveMachines - 1
WHERE factory_table.factory_id = Factory_ID;
DELETE FROM plan_table WHERE plan_table.assign_to_machine = Machine_ID
END$
DELIMITER $$
BEGIN
SELECT #m_id = machine_id, #f_id = link_factory_id
FROM machine_table
WHERE machine_table.annualCheck = 'TRUE';
END$$
CALL annualCheck(#m_id,#f_id)
I don't know why, but I'm running into syntax errors - one after the other.
It's my first time to use PROCEDURE and DELIMITER. Am I doing it right?
you can use stored procedure
delimiter //
CREATE PROCEDURE myProc (IN Machine_ID int)
BEGIN
UPDATE myTable
SET Machine.status = "IN_ANNUAL_CHECK"
WHERE Machines.annualCheck == "TRUE";
Update the related factory
WHERE Factory.id == Machine.linkFactoryID
UPDATE Factory.totalActiveMachines = totalActiveMachines -1;
DELETE FROM Plan WHERE Plan.willHandleByMachineID = Machine_ID;
END//
then you can execute it either from mysql
CALL simpleproc(#a);
or from Java
It is also possible to create trigger on the Machine table, something like this:
CREATE TRIGGER `TRG_Machines_AfterUpdate` AFTER UPDATE ON `Machine` FOR EACH ROW BEGIN
IF OLD.annualCheck = 'TRUE' AND NEW.annualCheck = 'IN_ANNUAL_CHECK' THEN
UPDATE
Factory
SET
totalActiveMachines = totalActiveMachines - 1
WHERE
id = NEW.linkFactoryID
;
DELETE FROM
Plan
WHERE
willHandleByMachineID = NEW.ID
;
END;
END
So you can just issue normal update:
UPDATE Machine SET annualCheck = 'IN_ANNUAL_CHECK' WHERE annualCheck = 'TRUE'
I have two stored procs which i call from my laravel application.
My laravel application passes in a cID parameter which is then passed to the stored procedure as the "where clause". But it seems something is going astray and possibly my variables arent set up properly.
Also i know that laravel IS passing the correct cID to my stored proc because i enabled the logs for mysql to see if it was passing any params.
Also the stored procedure select statement runs fine as a query if i manually set the ClientID = '';
My stored proc sends ALL clients and cards to the view, totally ignoring the where clause.
Laravel code:
Route::get('/clients/{cID}', function ($cID) {
$details = DB::select('CALL sp_Details(' . DB::raw($cID) . ')');
$cards = DB::select('CALL sp_Cards(' . DB::raw($cID) . ')');
return view('client.show', compact('details','cards'));
});
Any my Stored Proc
CREATE DEFINER=`root`#`localhost` PROCEDURE `sp_Details`(IN cID int )
BEGIN
SET #ClientID = cID;
SELECT
ClientID,
Client_Name
FROM accounts
where #ClientID = cID;
END
Stored Proc #2
CREATE DEFINER=`root`#`localhost` PROCEDURE `sp_Cards`(cID int)
BEGIN
SET #ClientID = cID;
SELECT
ClientID,
Code
FROM cards
where cID = #ClientID;
END
You are using local variables like #ClientID and you are confusing it to column names, must change your code to avoid them and there is no necessary use the local variable:
CREATE PROCEDURE `sp_Details`(IN cID int )
BEGIN
SELECT
ClientID,
Client_Name
FROM accounts
where ClientID = cID;
END
The other proc:
CREATE PROCEDURE `sp_Cards`(cID int)
BEGIN
SELECT
ClientID,
Code
FROM cards
where ClientID = cID;
END
I have written a simple database procedure in mySQL as follows:
DROP PROCEDURE IF EXISTS sp_doSomething;
DELIMITER //
CREATE PROCEDURE sp_doSomething
(IN pVal1 varchar(100),
IN pVal2 int(15)
)
BEGIN
DECLARE vCnt int(5) DEFAULT 0;
DECLARE vID int(15) DEFAULT 0;
DECLARE vTempID int(15) DEFAULT 0;
-- get ID
SELECT id INTO vID FROM T1
WHERE name = pVal1;
-- get count
SELECT count(*) INTO vCnt FROM T1
WHERE owner = vID;
-- get the log
INSERT INTO log select CONCAT('-v1-:', pVal1, ':-v2-:', pVal2);
-- Create basic stuff if it doesn't exist
IF vFolderCnt = 0 THEN
INSERT INTO T1 (`id`, `col1`, `col2`, `col3`)
SELECT null, vID, 'some value', CONCAT(vID,'^1') FROM T1
WHERE owner = 0;
END IF;
commit;
END //
DELIMITER ;
Now, I want to call this procedure in my Play Framework 2.4 application which uses Slick 3.0. It is such a simple thing but I am really struggling with it as there is no proper documentation available. It's very frustrating.
As it's mentioned on Google Group here https://groups.google.com/forum/#!searchin/scalaquery/procedure/scalaquery/BUB2-ryR0bY/EFZGX663tRYJ
I tried calling the procedure by different way. The code compiles but the procedure does not get called at all.
This statement gives an action error.
db.run(sql"{call sp_doSomething('${st.val1}', 1)}")
The following statement compiles fine but does not invoke the procedure.
db.run(sql"{call sp_doSomething('${st.val1}', 1)}".as[Int])
The following statement compiles fine but does not invoke the procedure.
db.run(sqlu"{call sp_doSomething('${st.val1}', 1)}")
Or
db.run(sqlu"{?=call sp_doSomething('${st.val1}', 1)}")
I have granted the Execute permission on the procedure to my DB user and verified it.
Also, I am not sure, whether the COMMIT is required in the procedure or not?
Any help, will be highly appreciated.
I have managed to invoke the stored procedure using old prepareCall method. Here's how I have done it. Hope, it might help someone.
db.withSession {
implicit session => {
val cs = session.conn.prepareCall("{call sp_doSomething(?, ?)}")
cs.setString(1, st.val1)
cs.setLong(2, 1L)
val result = cs.executeUpdate()
}
}
But I would be still interested in invoking the procedure using sql"" or sqlu"".
For me the following works in Slick 3.2:
delimiter //
create procedure Try1()
begin
select userid from TBL_USER where id = "4";
end //
delimiter ;
Then
def runProcByRawSql() : Future[Vector[String]] = {
def runproc = sql"""call Try1()""".as[String]
db.run(runproc)
}
I am Using Mysql DB. my question is : can i create array using sql?
if Yes then how and how to populate this array with output of following query -- "Select column_name1 From tableName".
Help me, Thanks in Advance
As I mentioned in my comment, MySQL does not support arrays by itself. That kind of structures are supported by other programming languages (like PHP, Java, Python, etcetera) and you can write a program capable of connecting to a MySQL database, read data from it and populate arrays (I think PostgreSQL supports an array data type, but I'm not sure).
What you can do is use cursors in a stored procedure to retreive data from a query and store it into variables.
Example:
delimiter $$
create procedure my_procedure()
begin
declare value varchar(100);
declare done int default false;
declare cur cursor for
select column_name1 from your_table;
declare continue handler for not found set done = true;
open cur; -- This line will open the row set and place the cursor
-- on the first row.
loop_data: loop
fetch cur into value; -- This line will fetch the current row
-- into the variable and move the cursor
-- to the next row.
if done then -- If there are no more rows in the
leave loop_data; -- row set, the loop is terminated here
end if; -- and the execution moves to the next
-- instruction after "end loop;"
-- do whatever you need to do with the retrieved value
end loop;
close cur;
end $$
delimiter ;
If you want to use an array in a high level programming language, you can do it using the appropriate methods. Here's an example using Java (read The Java tutorials: JDBC Database access for more info):
public class SomeClass {
/*
Retrieve data from a database and return an array with it.
Parameters:
- conn: Connection to the database.
*/
public String[] getValues(Connection conn) {
String[] ans = new String[10];
int i;
try(
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(
"select column_name1 from your_table limit 10"
);
) {
rs.beforeFirst();
i = 0;
while(rs.next()) {
ans[i] = rs.getString("column_name1");
i++;
}
} catch(SQLException e) {
// Code to handle the SQL exception
}
return ans;
}
}
References:
MySQL reference manual: Cursors
You can use an variable to simply select your row values into a string. Not precisely an array, but it allows you to store all your values into a single variable:
-- load test data
create table tableName (column_name1 varchar(5));
insert into tableName values
('abcde');
insert into tableName values
('fghij');
insert into tableName values
('klmno');
insert into tableName values
('pqrst');
insert into tableName values
('uvwzy');
insert into tableName values
('z');
-- build "array"
set #array := '';
select #array := concat(#array,column_name1) as array
from tableName;
select #array;
I'm new at creating and working with stored procedures.
After spending several hours on trying, reading tutorials (and yes reading all the related questions at stackoverflow :-) ) I'm stuck.
This works fine:
PROCEDURE GetAgent(IN AgentName VARCHAR(50), OUT AgentID SMALLINT(6))
BEGIN
IF EXISTS (SELECT id FROM tbl_lookup WHERE value = AgentName AND cat = 'agent') THEN
SELECT id FROM tbl_lookup WHERE value = AgentName AND cat = 'agent';
ELSE
INSERT INTO tbl_lookup(cat, value) VALUES ('agent', AgentName);
SELECT id FROM tbl_lookup WHERE value = AgentName AND cat = 'agent';
END IF;
END;
When called like:
Call GetAgent("Firefox 3.6.18", #AgentID);
It gives the proper response: "2"
So far so good. So let's get that into another procedure: (GetOS does the same thing, left out tot minimize reading :-)
PROCEDURE SetSessionInfo(IN OsName VARCHAR(50), IN AgentName VARCHAR(50), IN SessionID BIGINT(30), OUT SessionInfoID SMALLINT(6))
BEGIN
DECLARE nw_AgentID SMALLINT;
DECLARE nw_OSID SMALLINT;
CALL GetOs(OsName, #OsID);
SET NW_OSID = #OSID;
CALL GetAgent(AgentName, #AgentID);
SET NW_AgentID = #AgentID;
IF EXISTS (SELECT id FROM tbl_session_info WHERE session = SessionID) THEN
SELECT id AS SessionInfoID FROM tbl_session_info WHERE session = SessionID;
ELSE
INSERT INTO tbl_session_info(session, agent_id, os_id) VALUES (SessionID, GetAgent(AgentName, #AgentID), GetOs(OsName , #OsID));
SELECT id AS SessionInfoID FROM tbl_session_info WHERE session = SessionID;
END IF;
END;
When called with
Call SetSessionInfo("Windows XP", "Firefox 3.6.18", 857264713, #SessionInfoID)
I get the answer "3" (proper response from GetOS), then the procedure stops and does not insert anything.
After installing Toad I saw the reason: an error: "FUNCTION GetAgent does not exist"
Well, it is not a function, it's a procedure.
So basicly, my question:
How do I call another procedure within a stored procedure and set a variable with the result?
This is why you are getting "FUNCTION GetAgent does not exist" error:
INSERT INTO tbl_session_info(session, agent_id, os_id)
VALUES (SessionID, GetAgent(AgentName, #AgentID), GetOs(OsName , #OsID));
You are trying to call GetAgent as a function (while it is a procedure). But you have already got Agent and OS IDs into variables. Just use them:
INSERT INTO tbl_session_info(session, agent_id, os_id)
VALUES (SessionID, NW_AgentID, NW_OSID);