I have a prepared statement which is designed to insert row if a row didn't previously exist. If only one row is to be done it works fine. If there's two, the first row is added and the second ignored. If the first row exists and the second doesn't, the second is added and all following rows fail.
Effectively the insert works once and it's as if the IF NOT EXISTS doesn't update the binding of the new parameters.
Here is the sample code:
$dbConn = 'mssql:host=' . $server . ';dbname=' . $base;
$dbh = new PDO( $dbConn, $user, $pass);
// Go through each course code and add it
// Ignore if it already exists
$stmt = $dbh->prepare('IF NOT EXISTS (SELECT * FROM _ExamCoursesTEMP
WHERE ExamCourseCode = :examCode AND CourseCode = :courseCode )
BEGIN
INSERT INTO _ExamCoursesTEMP ( ExamCourseCode, CourseCode ) VALUES ( :examCode2, :courseCode2 )
END');
$counter = 0;
foreach( $courseCodes as $courseCode )
{
$stmt->bindParam(':examCode', $examCode );
$stmt->bindParam(':courseCode', $courseCode );
$stmt->bindParam(':examCode2', $examCode );
$stmt->bindParam(':courseCode2', $courseCode );
$updateCount = $stmt->execute();
$counter++;
}
The first updateCount returns 1 and all the rest are empty.
I'm not sure if this is an issue with my code, the prepared statement bindings though PDO or a quirk of mssql.
Any help would be appreciated.
Thanks.
The second parameter to bindParam is passed by reference. Thus, it is only necessary to bind the parameter to the variable once, then change the value assigned to that same variable in your loop.
Related
I have searched for the last few hours on this and have come up empty.
I am using a sample piece of code that I have edited slightly to work as needed. It posts values to a MySQL table, each set to a respective row. What I would like to be able to do is have the code not create a row if the PHP variable does not have a value. I am still relatively new with MySQL but have been really digging in so please if you have an answer, help me understand some of the meaning behind it. Thank you for your help!
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "database";
$col1Val1 = $_POST['col1Val1'];
$col1Val2 = $_POST['col1Val2'];
$col1Val3 = $_POST['col1Val3'];
$col2Val1 = $_POST['col2Val1'];
$col2Val2 = $_POST['col2Val2'];
$col2Val3 = $_POST['col2Val3'];
$col3Val1 = $_POST['col3Val1'];
$col3Val2 = $_POST['col3Val2'];
$col3Val3 = $_POST['col3Val3'];
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
// set the PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// begin the transaction
$conn->beginTransaction();
// our SQL statements
$conn->exec("INSERT INTO tableName(column1, column2, column3)
VALUES ('$col1Val1', '$col2Val1', '$col3Val1'),
('$col1Val2', '$col2Val2', '$col3Val2'),
('$col1Val3', '$col2Val3', '$col3Val3')");
// commit the transaction
$conn->commit();
echo "New records created successfully";
}
catch(PDOException $e)
{
// roll back the transaction if something failed
$conn->rollback();
echo "Error: " . $e->getMessage();
}
$conn = null;
?>
The way that query is currently written with multiple value sets, it's going to insert three rows whether they're empty or not. You need to evaluate each row separately in order to avoid inserting empty ones.
You have two main options.
Prepare an INSERT statement with placeholders for one row of values, iterate your $_POST and only execute the statement with rows that aren't empty. This is what I would suggest. With only a maximum of three rows to insert, the performance hit of executing multiple queries should be minimal.
Build your INSERT statement dynamically, and append a set of values for each of the rows that aren't empty. This is fine too, and it is still possible to construct a prepared statement that way, but for something like this it seems more complicated than necessary.
My suggestion for option 1:
$sql = "INSERT INTO tableName(column1, column2, column3) VALUES (?, ?, ?)";
$statement = $conn->prepare($sql);
for ($i=1; $i <= 3; $i++) {
$row = [];
for ($j=0; $j <= 3; $j++) {
$row[] = $_POST["col{$i}Val{$j}"];
}
if (array_filter($row)) { // check if the row has any non-empty values
$statement->execute($row);
}
}
This could be simplified a bit if you changed the names of your form fields up a bit so that you could get the values from sub-arrays in $_POST.
So thank you to all who gave me some tips. I believe I came up with a solution that will work decently for anyone who comes across the same issue.
What I did was for each set of data that goes to its own row, I created an ID field "row#Data" in the HTML that is defaulted to 0 but changes to 1 if each value is filled out. Then I used the if statement for each row instead of checking each variable.
There may be other ways to do this dynamically but to ensure functionality, this is what I came up with.
if ($row1Data == 1) $conn->exec(INSERT INTO...
if ($row2Data == 1) $conn->exec(INSERT INTO...
if ($row3Data == 1) $conn->exec(INSERT INTO...
...
I'm using bindParam to bind the return value of stored procedure once the statement is executed
But i'm getting zero , i've specified output variable of stored procedure as BIGINT
and i'm binding parameter like below
$sql = "{:retval = CALL sp_testProc()}";
$stmt->bindParam('retval', $proc_pass_val, PDO::PARAM_INT|PDO::PARAM_INPUT_OUTPUT, 4);
bindparam is taking Length of data type as last parameter, i'm passing 4 here, but it returns zero, don't know why
Could anybody help me on this
Thanks in Advance
This is what I've done to make it work, Hope this helps someone.
Note: Procedure defined in MSSQL server
Here I want email into a field inorder to get that in an array, you can omit this line select #myemail as student_email; and you can get the value of #myemail into $myemail
My Procedure:
Alter proc [dbo].[sp_test_success](
#Id int=1,
#myemail varchar(250)=null output
)
AS
BEGIN
select #myemail=rtrim(email) from Student where StudentId=#Id;
select #myemail as student_email;-- i put this to get myemail into array
END
Code:
$dbh = new PDO('sqlsrv:Server=Server;Database=database', 'UID', 'Pwd');
$stmt = $dbh->prepare("{CALL sp_test_success(#Id=:Id,#myemail=:myemail)}");
$Id = 4;
$stmt->bindParam('Id', $Id, PDO::PARAM_INT);
$stmt->bindParam('myemail', $myemail, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT, 500);
$stmt->execute();
$results = array();
do {
$rows= $stmt->fetch();// default fetch type PDO::FETCH_BOTH
$results[]= $rows;
} while ($stmt->nextRowset());
echo "<pre>"; print_r($results);
print "procedure returned $myemail\n"; exit;
Two functions here: http://pastebin.com/CNLBBMZ2 (with intentional error on line 11)
Here is how it's called: http://pastebin.com/VBxvE4D9
I'm having trouble getting my second function to prepare properly after my first prepare fails. I'm using mysqli prepared statements.
For simplicity, let's say I have two boolean functions.
The first function called upon a user form submit creates a record in my database. The second function lists all the records in the database and is called each time the page is loaded regardless of user input.
Here is part of the first function
//connect to database
$dbh = db_connect();
//Transaction to create all the records necessary for this house
$dbh->autocommit(FALSE);
// 1. query to insert a new house table record
if ( $stmt_create_house = $dbh->prepare("
INSERT INTO house (nickname, dateCreated, userID)
VALUES ( ? , ? , ?)
")) {
//bind param
$today = date('Y-m-d');
$stmt_create_house->bind_param( 'sss', $_POST['house-nickname'], $today, $_SESSION['user_id'] );
} else {
$dbh->close();
$create_error_message = "The query to create a new house record was malformed.";
return FALSE; //jumps out of the function
}
... rest of function
If I purposely fail the first prepare statement in the first function, the second completely unrelated function will fail as well. I purposely fail the first statement by typing in a wrong column name or otherwise malformed it just to test.
I want the second function to be able to still pull records from the database even though the first statement failed for whatever reason. Here is the section of code in my second function. This If statement fails when the first prepare statement fails.
//connect to database
$dbh = db_connect();
if ( $stmt_get_houses = $dbh->prepare("
SELECT houseID, nickname, dateCreated
FROM house
WHERE userID = ?
")) {
I can't figure out why failing the prepare in the function affects the second function. I close the $dbh each time I encouter an error. Do I have to clear an error buffer on the $dbh connection even if I close it?
I want to access randomly to a result sets retuned from a Stored Procedure using PDO Mysql and PHP. I found PDOStatement::nextRowset but the access to the result sets seems to be sequential.
EDIT
I'm looking for some like this:
$pdo = new PDO("mysql:host=$server;port=$port;dbname=$dbname;charset=utf8", $user, $pass, array(PDO::ATTR_PERSISTENT => false));
$statement = "CALL FooSP()";
$query = $pdo->prepare($statement);
$query->execute();
$query->resultSet[1][0]["ColumnFoo"] // Second Resultset, first row, column "ColumnFoo"
$query->resultSet[3][1]["ColumnBar"] // third Resultset, second row, columna "ColumnBar"
$query->resultSet[0][0]["Column1"] // first Resultset, first row, columna "Column1"
Can anyone help me?
If You need all resultsets - just prefetch them using next rowset like this:
<?php
$sql = 'CALL multiple_rowsets()';
$stmt = $conn->query($sql);
$fetched_rowsets = array();
do {
$rowset = $stmt->fetchAll(PDO::FETCH_NUM);
if ($rowset)
{
$fetched_rowsets[] = $rowset;
}
# This is important part.
} while ($stmt->nextRowset());
#Now You got the table with all data from all resultsets, fetched in PHP memory.
$fetched_rowsets[1][0]["ColumnFoo"];
$fetched_rowsets[3][1]["ColumnBar"];
$fetched_rowsets[0][0]["Column1"];
?>
Just remember that it can be memory consuming.
I have found many ways to use the exec statement for PDO, but I'm not sure it helps me. My understanding is that I have to use the execute() function for prepared statements. I am updating a row with data from user input, so I would like to use a prepared statement instead of the query() call.
My code is as follows:
$dbh = buildDBConnector();
$sql = "UPDATE tb_users
SET authState=1
WHERE id = ? AND authPass = ?";
$q = $dbh->prepare($sql);
$f = $q->execute(array($id,$authPass));
if($f){
echo '<br />Success<br />';
}else{
echo '<br />Failure<br />';
}
The issue is that the query itself is error free and executes fine, so there is no failure to store in $f. However, I need to know if it actually found the row to update, then successfully updated it. In other words, I need the affected rows. When googling and such, it keeps coming to the exec statement, but from my understanding, exec isn't for prepared statements? Any suggestions?
Try $q->rowCount(). Prepared statements will return the number of affected rows via that method.
A side note: when updating a table with identical values rowCount() will return always 0. This is normal behavior. You can change it yourself since PHP 5.3 by creating a PDO object with following attribute:
<? php
$p = new PDO($dsn, $user, $pass, array(PDO::MYSQL_ATTR_FOUND_ROWS => true));
?>
The rowCount() then will return how many rows your update-query actually found/matched.
$q->rowCount() returns the number of rows affected by the last (executed) SQL statement where $q is the prepared statement which is often called $stmt.
So most users who read this might want something like:
$pdo = new PDO($dsn, $username, $password);
$sql = "UPDATE tb_users SET authState=1 WHERE id = ? AND authPass = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute(array($id, $authPass));
if ($stmt->rowCount()){
echo 'Success: At least 1 row was affected.';
} else{
echo 'Failure: 0 rows were affected.';
}
PDO's rowCount() from prepared statements returns affected rows if it's an UPDATE, DELETE or INSERT statement. Otherwise it returns how many rows are returned from SELECT statement.
para evitar que la actualización retorne 0, deberás añadir algo al final de la cadena de conexión.
Conexion.php
<?php $cadena = "$manejador:host=$servidor;dbname=$dbname";
$cnx = new PDO($cadena, $usuario, $pass, array(PDO::MYSQL_ATTR_FOUND_ROWS => true));
?>
clase.php
<?php
...
global $cnx;
$pre = $cnx->prepare($sql);
$pre->execute($parametros);
$rpta = $pre->rowCount();
return $rpta;
?>
i think PDO rowCount() is useless in MySQL with single UPDATE query. because it always return 0;
ex:
TABLE{id=1, col1="A"}
UPDATE TABLE SET col1="AA" WHERE id=1;
rowCount will return 0;
also
UPDATE TABLE SET col1="AA" WHERE id=999;
rowCount will return 0;
so rowCount() is useless in this case.
i have not tested yet with this query UPDATE TABLE SET col1="AA"