【问题标题】:pdo issue with try and transactions尝试和事务的 pdo 问题
【发布时间】:2014-10-18 19:57:56
【问题描述】:

$comp_ids 存在于第二个查询中时,脚本可以正常工作...如果它不存在,则脚本会失败。

我在这里唯一的猜测是,因为我正在使用交易,它忽略了我在第二个查询中的条件 - if( count($comp_ids) > 0 )... 是这样吗?如果是这样,我怎样才能在保持交易功能的同时解决这个问题?我只想在它们都成功的情况下执行这两个查询......或者在这种情况下......第一个成功,因为第二个不应该由于条件而发生。

有什么想法吗?

//find account based off subscription reference
$stmt = $db->prepare("
    SELECT
        accounts.account_id,
        accounts.licenses,
        GROUP_CONCAT(computers.computer_id ORDER BY computers.computer_id ASC) AS comp_ids
    FROM accounts
    LEFT JOIN computers
        ON computers.account_id = accounts.account_id
    WHERE accounts.subscription = :subscription
");

$stmt->bindValue(':subscription', $_POST['SubscriptionReference']);
$stmt->execute();

//result (can only be one or none)
$result = $stmt->fetch(PDO::FETCH_ASSOC);

// if account exists and it is not a test then update it
//if ($result && $_POST['SubscriptionIsTest'] != 'true')
if ($result)
{
    //inserting into two different tables so use a transaction (if either fails both fail)
    try {
        // begin transaction
        $db->beginTransaction();

        // accounts update
        $stmt = $db->prepare("
            UPDATE accounts 
            SET account_email = :account_email,
                licenses = :licenses,
                fname = :fname,
                lname = :lname,
                subscription = :subscription,
                subscription_url = :subscription_url,
                dtEnd = :dtEnd,
                dtNextPeriod = :dtNextPeriod
            WHERE account_id = :account_id  
        ");

        //bindings
        $binding = array(
            'account_email' => $_POST['email'],
            'licenses' => $_POST['SubscriptionQuantity'],
            'fname' => $_POST['firstName'],
            'lname' => $_POST['lastName'],
            'subscription' => $_POST['SubscriptionReference'],
            'subscription_url' => $_POST['SubscriptionCustomerUrl'],
            'dtEnd' => strtotime($_POST['SubscriptionEndDate']),
            'dtNextPeriod' => strtotime($_POST['SubscriptionNextPeriodDate']),
            'account_id' => $result['account_id']
        );
        $stmt->execute($binding);

        // update computers status because license count changed
        // get comp_ids array
        $comp_ids = explode(',', $result['comp_ids']);

        //if there are any comp_ids then...
        if( count($comp_ids) > 0 )
        {
            //build in clause & binding using selected array
            $prefix = $in_clause = '';
            $binding_clause = array();  
            foreach($comp_ids as $key=>$value)
            {
                $in_clause .= $prefix.':selected_'.$key;
                $prefix = ', ';
                $binding_clause[':selected_'.$key] = $value;
            }

            //set counter for loop
            $counter = 0;
            //zero the string
            $update_cond = '';

            //create the case conditions
            foreach($comp_ids as $key)
            {
                $comp_status = $counter < $result['licenses'] ? 1 : 0;
                $update_cond .= "WHEN ".$key." THEN ".$comp_status."\n";
                //inc counter   
                $counter++;
            }

            //since there are comps then update their status
            $stmt = $db->prepare("
                UPDATE computers SET
                    status = CASE computer_id
                    ".$update_cond."
                    ELSE status
                    END
                WHERE computer_id IN(". $in_clause .")  
            ");

            //execute the changes
            $stmt->execute($binding_clause);
        }

        // if we arrive here, it means that no exception was thrown
        // i.e. no query has failed, and we can commit the transaction
        $db->commit();
    }
    catch(Exception $e)
    {
        //error message
        error_log('/fastspring/spc/sub_changed.php: ' .$e->getMessage());

        // roll back the db changes if any
        $db->rollback();

        //failed - send myself an email about this
    }
}
//account does not exist so do something to handle the error
else
{
    //failed - send myself an email about this
}

错误...抱歉,忘记添加了:

SQLSTATE[42000]: Syntax error or access violation: 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 'THEN 1

                    ELSE status
                    END
                WHERE computer_id IN(?)' at line 3

【问题讨论】:

  • 失败怎么办? php错误?数据库错误?宇宙遭受了彻底的存在失败?
  • 对不起,完全以为我已经添加了,但一定忘记了。已更新。

标签: php mysql pdo transactions


【解决方案1】:

您的错误提示您在 THEN 关键字之前有非法语法。您没有说您的 computer_id 值是什么,但如果它们是字符串,那么您可能缺少引号。你应该这样做:

$update_cond .= "WHEN ".$db->quote($key)." THEN ".$comp_status."\n";

但以上只是猜测。这里更普遍的错误是您试图诊断 SQL 中的语法错误而不查看 SQL。当您阅读生成代码的代码时,更难排除故障。

相反,你应该养成dump生成的SQL的习惯,然后语法错误可能一目了然。为了解决这个问题,我通常创建一个 $sql 变量来保存我的 SQL 字符串。然后我可以更容易地回显它。

$sql = "
            UPDATE computers SET
                status = CASE computer_id
                ".$update_cond."
                ELSE status
                END
            WHERE computer_id IN(". $in_clause .")  
        ");
var_dump($sql); // temporary line of code, for troubleshooting
$stmt = $db->prepare($sql);

如果您关注的是 SQL,而不是 PHP,那么对 SQL 进行故障排除通常会容易得多。


你的评论:

您没有按照我上面的建议输出 SQL。这是我通过实验得到的结果,$result['comp_ids'] 是一个空白字符串或 null:

string(114) "
UPDATE computers SET
status = CASE computer_id
WHEN  THEN 1

ELSE status
END
WHERE computer_id IN(:selected_0)  
"

注意无效的语法WHEN THEN。由于双倍空间,很容易注意到!

发生了什么是count($comp_ids) &gt; 0 为真,因为该数组至少有一个元素,即使该元素是一个空白字符串。

【讨论】:

  • 是的,我知道错误在哪里......正如我在原始帖子中所说,当数组存在时它工作得很好。当它没有时,这是可能的,因为我在我的原始查询中留下了这些值......它失败了......问题是第二个查询永远不应该发生,但由于某种原因它是。我认为这与事务的工作方式有关,例如,它忽略了我对该查询是否运行的条件。
【解决方案2】:

好吧,事实证明问题是从我的第一个查询中的 group_concat 开始的。这会将值作为逗号分隔列表返回,但是当我将其分解为普通数组时......如果它是空的,它实际上在数组中创建了一个没有值的键。

我不知道爆炸一个空字符串会以这种方式做出反应,但显然它确实如此。

话虽如此,我对第二个查询进行了这些更改,并且它按预期工作:

//if there are computers then update their status
if($result['comp_ids'] != '')
{
    // get comp_ids into an array
    $comp_ids = explode(',', $result['comp_ids']);

    //build in clause & binding using selected array
    $prefix = $in_clause = '';
    $binding_clause = array();  
    foreach($comp_ids as $key=>$value)
    {
        $in_clause .= $prefix.':selected_'.$key;
        $prefix = ', ';
        $binding_clause[':selected_'.$key] = $value;
    }
... and so on...

【讨论】:

    猜你喜欢
    • 2011-09-20
    • 2017-07-06
    • 1970-01-01
    • 2010-12-27
    • 2011-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多