8

尝试使用 mysqli 创建动态 where 子句时遇到很多错误:

警告:mysqli_stmt::bind_param() 的参数 2 应为参考,在第 319 行的 ... 中给出的值

警告:mysqli_stmt::execute(): (HY000/2031): 没有为第 328 行的准备好的语句中的参数提供数据

警告:mysqli_stmt::bind_result(): (HY000/2031): No data provided for parameters in Prepared statement in ... on line 331

警告:mysqli_stmt::store_result(): (HY000/2014): 命令不同步;您现在无法在 ... 第 332 行运行此命令

我猜想解决这些问题需要进行一些更改,但是如果两个下拉菜单中的一个不相等All或两者不相等All,则会出现错误。

下面是显示下拉菜单和查询(带有动态 where 子句)的代码,具体取决于所选的 n 个选项:

HTML:

学生下拉菜单:

<select name="student" id="studentsDrop">
<option value="All">All</option>
<option value="11">John May</option>
<option value="23">Chris Park</option>
</select>

问题编号 下拉菜单

<select name="question" id="questionsDrop">
<option value="All">All</option>
<option value="123">1</option>
<option value="124">2</option>
<option value="125">3</option>
</select>

PHP/MYSQLI:

      function StudentAnswers()
        {


    /*BELOW IS THE QUERY WHERE I AM TRYING TO RETRIEVE DATA DEPENDING ON THE ASSESSMENT CHOSEN AND
    THEN DEPENDING ON OPTIONS CHOSEN IN STUDENT AND QUESTION NUMBER DROP DOWN MENU */

        $selectedstudentanswerqry = "
        SELECT
        StudentAlias, StudentForename, StudentSurname, q.SessionId, QuestionNo, QuestionContent, o.OptionType, q.NoofAnswers, GROUP_CONCAT( DISTINCT Answer
        ORDER BY Answer SEPARATOR ',' ) AS Answer, r.ReplyType, QuestionMarks, 
        GROUP_CONCAT(DISTINCT StudentAnswer ORDER BY StudentAnswer SEPARATOR ',') AS StudentAnswer, ResponseTime, MouseClick, StudentMark
        FROM Student s
        INNER JOIN Student_Answer sa ON (s.StudentId = sa.StudentId)
        INNER JOIN Student_Response sr ON (sa.StudentId = sr.StudentId)
        INNER JOIN Question q ON (sa.QuestionId = q.QuestionId)
        INNER JOIN Answer an ON q.QuestionId = an.QuestionId
        LEFT JOIN Reply r ON q.ReplyId = r.ReplyId
        LEFT JOIN Option_Table o ON q.OptionId = o.OptionId
        ";

        // Initially empty
        $where = array('q.SessionId = ?');
        $parameters = array($_POST["session"]);
        $parameterTypes = 'i';

        // Check whether a specific student was selected
        if($_POST["student"] !== 'All') {
            $where[] = 'sa.StudentId = ?';
            $parameters[] =& $_POST["student"];
            $parameterTypes .= 'i';
        }

        // Check whether a specific question was selected
        // NB: This is not an else if!
        if($_POST["question"] !== 'All') {
            $where[] = 'q.QuestionId = ?';
            $parameters[] =& $_POST["question"];
            $parameterTypes .= 'i';
        }

        // If we added to $where in any of the conditionals, we need a WHERE clause in
        // our query
        if(!empty($where)) {
            $selectedstudentanswerqry .= ' WHERE ' . implode(' AND ', $where);
            global $mysqli;
            $selectedstudentanswerstmt=$mysqli->prepare($selectedstudentanswerqry);
            // You only need to call bind_param once
                call_user_func_array(array($selectedstudentanswerstmt, 'bind_param'),
                array_merge(array($parameterTypes), $parameters)); //LINE 319 ERROR 1
        }

    //Add group by and order by clause to query
        $selectedstudentanswerqry .= "
          GROUP BY sa.StudentId, q.QuestionId
          ORDER BY StudentAlias, q.SessionId, QuestionNo
        ";

        // get result and assign variables (prefix with db)
        $selectedstudentanswerstmt->execute(); //LINE 328 ERROR 2

    //bind database fields 
$selectedstudentanswerstmt->bind_result($detailsStudentAlias,$detailsStudentForename,$detailsStudentSurname,$detailsSessionId,$detailsQuestionNo, 
        $detailsQuestonContent,$detailsOptionType,$detailsNoofAnswers,$detailsAnswer,$detailsReplyType,$detailsQuestionMarks,$detailsStudentAnswer,$detailsResponseTime,
        $detailsMouseClick,$detailsStudentMark); //LINE 331 ERROR 3

    //store results retrieved
        $selectedstudentanswerstmt->store_result(); //LINE 332 ERROR 4

    //count number of rows retrieved
        $selectedstudentanswernum = $selectedstudentanswerstmt->num_rows();     

    //output query
        echo "$selectedstudentanswerqry";

        }

        ?>

这是一个演示: 演示

在演示中,从下拉菜单中选择评估并提交。您将看到两个下拉菜单。将它们都设置为All并提交,它将毫无问题地输出查询。在其中一个下拉菜单中选择否,更改All为特定学生或问题,然后提交。现在你会看到错误

VAR转储:

当我选择带有 value 、 student number value和 question number value以及 WHERE CLAUSE的var_dump(array_merge(array($parameterTypes), $parameters)));会话(评估)时的结果:314081WHERE q.SessionId = ? AND sa.StudentId = ? AND q.QuestionId = ?

我得到这个输出:array(4) { [0]=> string(3) "iii" [1]=> string(2) "31" [2]=> string(2) "40" [3]=> string(2) "81" }

4

3 回答 3

1

这是由call_user_func_arrayPHP 5.4 中的行为变化引起的棘手情况(我必须假设):文档

像这样丑陋,它可以这样调用bind_param

$selectedstudentanswerqry .= ' WHERE ' . implode(' AND ', $where);
global $mysqli;
$stmt =$mysqli->prepare($selectedstudentanswerqry);

if (count($where) === 1) {
    $stmt->bind_param($parameterTypes, $parameters[0]);
}
else if (count($where) === 2) {
    $stmt->bind_param($parameterTypes, $parameters[0], $parameters[1]);
}
else if (count($where) === 3) {
    $stmt->bind_param($parameterTypes, $parameters[0], $parameters[1],
       $parameters[2]);
}

我和你一样讨厌这个。我建议切换mysqliPDOwhich 以更好的方式处理变量参数(在我看来,通常具有更好的语法):

$pdo = new PDO('mysql:host=localhost', 'username', 'password');
$stmt = $pdo->prepare($selectedstudentanswerqry);
$stmt->execute($parameters);
$selectedstudentanswernum = $stmt->rowCount();
于 2013-01-31T00:31:07.293 回答
1

警告:mysqli_stmt::bind_param() 的参数 2 应为参考,在第 319 行的 ... 中给出的值

这应该是不言自明的:bind_param 的参数是通过引用传递的,因此必须是变量。您可能忽略了array_merge返回一个新数组,该数组不包含对原始变量的引用,而只包含值。

由于参数未绑定,因此出现以下错误。

一种可能的解决方案是将引用存储在您的$parameters数组中,这些引用甚至会被 array_merge 保留:

$parameters[] =& $_POST["student"];

$parameters[] =& $_POST["question"];

现在的数组项$parameters是对 POST 变量的引用,array_merge结果的数组项也是。

编辑:看起来这不再可能了,请参阅@Explosion Pills

于 2013-01-31T00:16:49.410 回答
0

这仍然不是为 PDO 放弃 mysqli 的充分理由,尤其是当我当时有很好的理由不使用 PDO 时。另一方面,是的,他们不需要强制 mysqli_stmt::bind_param 和 bind_result 方法要求通过引用传递变量,他们可以让它们返回一个数组。但是,这里真正的问题是不再可能调用时传递引用,但 mysqli 仍然需要传递引用,因此任何使用动态参数的人(任何具有直观 SQL 的人)都会被抓住在这个问题上,包括我自己。在花费数小时编写自定义 mysqli 包装类,将数组集转换为动态准备的 SQL 语句之后,我不打算尝试重写它以使用 PDO,特别是因为 PDO 需要将参数命名为绑定的一部分,我必须编写多个函数才能为每种类型的查询构建发送内容的列表。这是违反直觉的,我不会浪费无数小时来模仿我用 mysqli 完成的工作。

此时唯一的问题是我们不能通过引用传递 call_user_func_array() 中的变量,而 mysqli_stmt::bind_* 要求它们通过引用传递...所以我们唯一的选择是 1) 编写一个新的 call_user_func_array()旨在为 bind_* 方法显式工作的函数,其中传递的参数本质上是通过 args 复制到新数组或变量集中,然后在回调中传递这些 byRef;或 2) 重写 mysqli 类以正确的方式进行操作,只需查看传递的内容,执行 SQL,然后按预期返回结果。没有理由我们传递给执行 SQL 的方法必须用结果来破坏,应该像对待 SQL 控制台一样对待它。

因为编码太累了,直到我的眼睛几乎流血,我将采取非建议的选择,将我的 PHP 版本保持在 5.3.10-1,直到我真正以正确的方式解决了问题。对于那些会争论“为了所有这些努力,为什么不把它花在编写你的 PDO 包装器上呢?”...我的回答很简单:我在许多服务器是一台小型计算机的项目中专门使用 PHP 和 MySQL空间和资源非常有限,更不用说 CPU 能力了。PDO 会加载它所能做的一切,而不仅仅是你将要使用的东西,所以在你的资源中为你甚至不会使用的东西做了一些保留,我的项目不能失去这些。

值得庆幸的是,目前保留我的 PHP 版本不会有害。这是许多企业级服务器场实际上所做的,以避免为了“使用当前版本”而不得不降低所有内容,而每两年升级一次所有内容也非常昂贵。

于 2016-04-23T08:41:33.413 回答