1

In my SQL query I have an INSERT, UPDATE and the a SELECT query all in one single query like so the below query

INSERT INTO questions (user_mail, question_id, question, username, created_at) VALUES (:fid, :qid, :com, :pid, :dat);
UPDATE users SET  activity = activity + 1 WHERE face_id = :fid;
SELECT questions.face_id
       FROM  questions
       WHERE questions.question_id = :qid

I have all those three queries in one single PDO query but there is this weird rule using an UPDATE query with $result = $stmt->fetchAll(); any ideas on how to get around this because my PDO keeps spitting out an error that says

SQLSTATE[HY000]: General error

which is as a result of the combined queries; and please its compulsory I combine the queries like the above or else I would have to change my entire code logic, secondly I already use the $result = $stmt->execute ( $params ); to execute the query but its returning the values from the SELECT query portion that I need to use the $stmt->fetchAll();. Is there another way?

the other way cause another problem

cannot execute query while other unbuffered queries are still active

case PUT_COMMENTS :
            $this->querymultiple ['insert'] = "INSERT INTO talks (face_id, comment, phone_id, created_at) VALUES (:fid, :com, :pid, :dat);";

            $this->querymultiple ['update'] = "UPDATE facelog SET  activity = activity + 1 WHERE face_id = :fid;
                                               UPDATE facelog SET  commentCount = commentCount + 1 WHERE face_id = :fid;";

            $this->querymultiple ['select'] = "SELECT
                                                talks.comment,
                                                talks.face_id,
                                                userlog.gcm_id
                                               FROM
                                                    talks,
                                                    tagged,
                                                    userlog
                                               WHERE
                                                   talks.face_id = :fid
                                                   AND talks.comment = :com
                                                   AND tagged.face_id = talks.face_id
                                                   AND userlog.phone_id = tagged.phone_id";

            $this->params ['insert'] = array (
                    ':' . FACE_ID => $value [FACE_ID],
                    ':' . COMMENT => $value [COMMENT],
                    ':' . DATE => $value [DATE],
                    ':' . PHONE_ID => $value [PHONE_ID] 
            );

            $this->params ['update'] = array (
                    ':' . FACE_ID => $value [FACE_ID] 
            );

            $this->params ['select'] = array (
                    ':' . FACE_ID => $value [FACE_ID],
                    ':' . COMMENT => $value [COMMENT] 
            );

            $this->pdo_query->RunMultipleQuery ( $tag, $this->querymultiple, $this->params, SELECT );
            break;

My RunMultipleQuery() method is as follows

/**
 * This method runs multiple the Queries
 *
 * @param unknown $tag          
 * @param unknown $query            
 * @param unknown $params           
 * @param unknown $query_type           
 */
public function RunMultipleQuery($tag, $query, $params, $query_type) {
    try {
        $stmt = array ();
        $result = array ();
        if (! empty ( $query ) || ! empty ( $params )) {
            $this->pdo_db->pdo_db->beginTransaction ();
            $stmt ['insert'] = $this->pdo_db->pdo_db->prepare ( $query ['insert'] );
            $stmt ['update'] = $this->pdo_db->pdo_db->prepare ( $query ['update'] );
            $stmt ['select'] = $this->pdo_db->pdo_db->prepare ( $query ['select'] );

            $result ['insert'] = $stmt ['insert']->execute ( $params ['insert'] );
            $result ['update'] = $stmt ['update']->execute ( $params ['update'] );
            $result ['select'] = $stmt ['select']->execute ( $params ['select'] );

            $stmt ['insert']->closeCursor ();
            $stmt ['update']->closeCursor ();
            $stmt ['select']->closeCursor ();

            $this->pdo_db->pdo_db->commit ();
        } else {
            $result = null;
        }
    } catch ( PDOException $e ) {
        // For testing, you could use a die and message.
        // die("Failed to run query: " . $ex->getMessage());

        // or just use this use this one to product JSON data:
        // $response["message"] = '0:'.$ex->getMessage() ;
        // die(json_encode($response["message"]));
        $error_message = $e->getMessage ();
        $error_code = $e->getCode ();
        $error_trace = $e->getTrace ();
        Log::LogError ( $error_message, $error_code, $error_trace );
        // roll back transaction
        if ($this->pdo_db->pdo_db->inTransaction ()) {
            $this->pdo_db->pdo_db->rollBack ();
        }
        $result = null;
    }

    if ($query_type == SELECT) {
        // echo 'result = '.$result;
        try {
            if (! empty ( $result ['select'] )) {
                // fetching all the rows from the query
                SendtoClient::Toclient ( $tag, $stmt ['select']->fetchAll () );
            } else {
                SendtoClient::Toclient ( $tag, null );
            }
        } catch ( PDOException $e ) {
            $error_message = $e->getMessage ();
            $error_code = $e->getCode ();
            $error_trace = $e->getTrace ();
            Log::LogError ( $error_message, $error_code, $error_trace );
        }
    }
}
  • Your select query does not have a semi-colon at the end and also, have you seen this post? [Multiple queries in PDO statement](http://stackoverflow.com/questions/6346674/pdo-support-for-multiple-queries-pdo-mysql-pdo-mysqlnd) – Osuwariboy Jul 20 '15 at 14:56
  • I literally see no reason why all 3 queries must be sent together, nor why you would have to change your code logic at all. This is a stupid solution that you're using. Simply start a transaction, issue all queries one by one and then commit. From your POV, it's probably less than 50 characters added all together, but unlike your example - it will work. – N.B. Jul 20 '15 at 15:16
  • yes I have seen the post @Osuwariboy but it doesn't solve my problem, how do i get my selected values with out `$stmt->fetchAll();` or ` $stmt->fetch();` –  Jul 20 '15 at 15:17
  • @N.B. am a little bit offended by term "stupid" that you have used but please do post your solution as an answer below. –  Jul 20 '15 at 15:20
  • 1
    The term stupid isn't used to reflect an attribute of you. The term stupid is used because the solution is bad, and that has nothing with your skills, IQ, abilities or anything. I've made such a comment only to reflect the value of such solution among possible solutions - and that one is simply not good. If you're offended by the term stupid then I apologize, the chosen solution is bad. I'll post an answer to depict what I had in mind. – N.B. Jul 20 '15 at 15:22
  • I don't see any reason you MUST send these 3 queries in a single PDO statement. Can you please explain your reason to me? – STT LCU Jul 20 '15 at 15:24
  • If it were possible, how would you deal with debugging that? What if the second one fails? Or the select fails? What is so wrong with issuing three queries one by one? It's much cleaner to read, debug and maintain. You gain literally **nothing** by combining 3 queries in a string that you send to MySQL. It's not faster. It also doesn't work, which is the most important thing. – N.B. Jul 20 '15 at 15:38

1 Answers1

3

What you should do is isolate your queries in a transaction and issue one by one. I'll post an example how you can do this with prepared statements:

try
{

    $pdo->beginTransaction();

    $stmt['insert'] = $pdo->prepare("INSERT INTO questions (user_mail, question_id, question, username, created_at) VALUES (:fid, :qid, :com, :pid, :dat)");
    $stmt['update'] = $pdo->prepare("UPDATE users SET  activity = activity + 1 WHERE face_id = :fid");
    $stmt['select'] = $pdo->prepare("SELECT questions.face_id FROM  questions WHERE questions.question_id = :qid");

    $stmt['insert']->execute([':fid' => 'your value', ':qid' => 'your value', ':com' => 'your value', ':pid' => 'your value', ':dat' => 'your value']);
    $stmt['update']->execute([':fid' => 'your value']);
    $stmt['select']->execute([':qid' => 'your qid']);

    $rows = $stmt['select']->fetchAll();

    $pdo->commit();
}
catch(\PDOException $e)
{
    echo "Something went terribly wrong: " . $e->getMessage();

    if($pdo->inTransaction())
    {
        $pdo->rollBack();
    }
}

The reason you shouldn't send a string that represents 3 queries in a single PDO function is this: it doesn't work. The second reason is: it's difficult to debug or to maintain something like that. It's much cleaner to have a query for each type of job. The third reason: assuming the three queries could work together, how would you tell which one fails?

Using prepared statements lets you control this flow nicely. Using transactions ensures that everything goes as planned - if something dies along the way, your database won't get corrupted with orphaned data. And the best part - you can catch the exception and inspect its error message to see which query went bad.

I posted a code example, you should modify it with the values you plan to send to your database. From performance POV, it's marginally slower (unless you issue a lot of queries, in which case it's extremely fast).

N.B.
  • 13,688
  • 3
  • 45
  • 55
  • This is really good! thanks for clearing the cob webs for me on how I would have done it. would try your solution right away but I think it already worked. ;) –  Jul 20 '15 at 15:40
  • No problem, I'm glad something got clearer, even if for a bit :) – N.B. Jul 20 '15 at 15:42
  • I just finished implementing your codes but I get a different kind of error which says `cannot execute query while other unbuffered queries are still active` –  Jul 21 '15 at 14:07
  • 1
    [Could this help](http://stackoverflow.com/questions/2066714/pdo-cannot-execute-queries-while-other-unbuffered-queries-are-active)? – N.B. Jul 21 '15 at 14:11
  • yea I found the `closeCursor()`, but not to sound dumb it means i would do it like so `$stmt['insert']->closeCursor()` and for the others to, i.e `update and select` which would come before the `commit();`. would I also have to add this `$pdo->setAttribute (PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);` –  Jul 21 '15 at 14:16
  • Can you try with `$pdo->setAttribute (PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);` and check what happens? It should work. – N.B. Jul 21 '15 at 14:20
  • Did you copy paste my code and do you have any other queries or statements that you ran prior to those 3? – N.B. Jul 21 '15 at 14:51
  • I will update my question with the codes I actually ran. –  Jul 21 '15 at 14:56
  • 1
    After you `$stmt->execute()`, use `$stmt->closeCursor()` on that statement. Then execute the next one. But boy, did you make your code complex.. if I were you, I would keep it really simple. – N.B. Jul 21 '15 at 15:06
  • perfect! Thanks a bizillion. yea it is. LOL! –  Jul 21 '15 at 15:22