Mysqli Prepared statement usage with AJAX POST to PHP file
Mysqli Prepared statement usage with AJAX POST to PHP file
我的问题是,PHP Mysqli 准备语句的效率如何?根据我从基本阅读中了解到的内容,准备好的语句 1) 使用绑定输入帮助提高安全性 2) 加快 'reduce' 数据发送到服务器的速度 'pre-packaging' 或 'preparing' sql 查询到一定程度,一旦数据可用,它只是将数据附加到准备好的语句并执行它。这也有助于 'repeated' 在重复插入相同数据(不同值)时使用相同的语句,因为语句只准备一次。
现在,我正在构建一个具有多种功能的网站,所有(或大部分)功能都使用 JQuery 和 AJAX 来获取用户输入,做一些检查(在 JS/JQ or in PHP), 将数据发送到 PHP 文件 PHP_AJAX_Handler.php 在 AJAX URL 中指定。 PHP 文件准备 SQL 语句将数据插入数据库,然后是 return JSON success/failure 消息。比如我的大部分features/functionality都是这样编程的;下面是一个文件,我用它来 1) 检查现有的大陆-国家对,以及 2) 插入新的大陆-国家对。
HTML:
<input type='text' id='continent'>
<input type='text' id='country'>
<button id='btn1'></button>
<p id='p1'></p>
<p id='p2'></p>
<p id='p3'></p>
JQuery:
$("#btn1")click(function(){
var Cntt = $("#continent").val();
var Ctry = $("#country").val();
$.post("PHP_AJAX_Handler.php",{CN:cntt,CT:ctry},function(DAT)
{ var RET_j = json.PARSE(dat);
if(RET_j.PASS=='FAIL')
{ $('#p1').html(RET_j.PASS);
$('#p2').html(RET_j.MSG1);
}
if(RET_j.PASS=='OKAY')
{ $('#p1').html(RET_j.PASS);
$('#p3').html(RET_j.MSG2);
} }
);
});
PHP_AJAX_Handler.php
<?PHP
session_start();
if( (isset($_POST['CT'])) && (isset($_POST['CN'])))
{ require_once ("golin_2.php");
$CN = $_POST['CN'];
$CT = $_POST['CT'];
$ER = "";
$CONN = mysqli_connect($SERVER, $USER, $PASS, $DBNAME);
If($CONN == FALSE)
{ $ER = $ER . "Err: Conn Could not connect to Databse ".mysqli_connect_errno().' '.mysqli_connect_error();
}
else
{ $SQL_1 = "SELECT * FROM sailors.continental_regions WHERE CONTINENT = ? AND COUNTRY = ?";
if(!($STMT_1 = mysqli_stmt_init($CONN)))
{ $ER = $ER . "Err: Stmt Prepare Failed";
}
else
{ if(!mysqli_stmt_prepare($STMT_1, $SQL_1)) ///FIRST SET of prepared statement lines
{ $ER = $ER . "Err: Stmt Prepare Failed";
}
else
{ if(!mysqli_stmt_bind_param($STMT_1,"ss",$CN, $CT))
{ $ER = $ER . "Err: Stmt Prepare Failed";
}
else
{ if(!(mysqli_stmt_execute($STMT_1)))
{ $ER = $ER . "Err: Stmt_1 Execute Failed";
}
else
{ $RES_1 = mysqli_stmt_get_result($STMT_1);
$NUMROWS_1 = mysqli_num_rows($RES_1);
if($NUMROWS_1>0)
{ $ER = $ER . "Err: duplicate '$CN' '$CT' pair";
}
if($NUMROWS_1==0)
{ $SQL_2 = "INSERT INTO DB.continental_regions (CONTINENT,COUNTRY) values (?, ?)";
if(!($STMT_2=(mysqli_stmt_init($CONN))))
{ $ER = $ER . "Err: Init2 failed";
}
else
{ if(!mysqli_stmt_prepare($STMT_2, $SQL_2)) ///SECOND SET of prepared statement lines
{ $ER = $ER . "Err: Prep2 failed".mysqli_error($CONN);
}
else
{ if(!mysqli_stmt_bind_param($STMT_2,"ss",$CN, $CT))
{ $ER = $ER . "Err: Bind2 failed";
}
else
{
if(!(mysqli_stmt_execute($STMT_2)))
{ $ER = $ER . "Err: Exec failed";
}
else
{ $arr['PASS'] = 'OK';
}
}
}
}
}
}
}
}
}
mysqli_free_result($RES_1);
mysqli_stmt_close($STMT_1);
mysqli_stmt_close($STMT_2);
mysqli_close($CONN);
}
if($ER!=="")
{ $arr['MSG'] = $ER;
$arr['PASS'] = 'FAIL';
}
if($arr['PASS']=="OK")
{ $arr['MSG2'] = "Insert Success";
}
echo json_encode($arr);
}
else
{ header("location: ../Error_Fail.php");
}
?>
如您所见,PHP 文件变得相当长。有一组准备语句检查 CC 对是否已经存在于 table 中,然后另一组准备语句插入 CC 对。
据我所知,对于每个添加一对新值的 AJAX 请求,mysqli 语句都会重新准备。然后再次为下一个请求,依此类推。我想这会为服务器创建大量开销和数据,只是为了实现安全性。对于其他使用 AJAX-POST-PHP 开发 Web 应用程序的人来说也是如此吗?对我来说似乎不可避免的是,对于每个准备,值只能插入一次?如何绕过一次准备此语句,并且只在数据可用时重复执行?我似乎无法理解准备好的语句的 'efficiency' 因素..
谢谢..希望能从一些经验丰富的程序员那里得到一些建议..
你说:
As you can see, the PHP file is turning out to be pretty long.
确实如此,但这不是准备好的语句的错。您一定是通过编写糟糕的教程学习了 PHP 开发。这段代码不需要那么长。事实上,它可以大大缩短。
只需修复您现有的代码即可使其更具可读性。我使用 OOP-style mysqli 并删除了所有这些 if
语句。您应该改为启用错误报告。
<?php
session_start();
if (isset($_POST['CT'],$_POST['CN'])) {
require_once "golin_2.php";
$CN = $_POST['CN'];
$CT = $_POST['CT'];
$ER = "";
$arr = [
'PASS' => "OK",
'MSG2' => "Insert Success",
]; // successful state should be the default outcome
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$CONN = new mysqli($SERVER, $USER, $PASS, $DBNAME);
$CONN->set_charset('utf8mb4'); // always set the charset
// To check existance of data in database we use COUNT(*)
$stmt = $CONN->prepare("SELECT COUNT(*) FROM sailors.continental_regions WHERE CONTINENT = ? AND COUNTRY = ?");
$stmt->bind_param("ss", $CN, $CT);
$stmt->execute();
$NUMROWS = $stmt->get_result()->fetch_row()[0];
if ($NUMROWS) {
$ER .= "Err: duplicate '$CN' '$CT' pair";
} else {
$stmt = $CONN->prepare("INSERT INTO DB.continental_regions (CONTINENT,COUNTRY) values (?, ?)");
$stmt->bind_param("ss", $CN, $CT);
$stmt->execute();
}
if ($ER) {
$arr = [
'PASS' => "FAIL",
'MSG' => $ER,
];
}
echo json_encode($arr);
} else {
header("location: ../Error_Fail.php");
}
如果您在 table 的这两列上有一个复合 UNIQUE 键,那么您可以删除 select 语句。此外,您应该清理您的响应准备。成功状态应该是默认状态,只有在出现问题时才应将其替换为错误消息。
在这个例子中,我删除了一个 SQL 语句。整个事情现在简单多了。
<?php
define('DUPLICATE_KEY', 1062);
session_start();
if (isset($_POST['CT'],$_POST['CN'])) {
require_once "golin_2.php";
$CN = $_POST['CN'];
$CT = $_POST['CT'];
$arr = [
'PASS' => "OK",
'MSG2' => "Insert Success",
]; // successful state should be the default outcome
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$CONN = new mysqli($SERVER, $USER, $PASS, $DBNAME);
$CONN->set_charset('utf8mb4'); // always set the charset
try {
$stmt = $CONN->prepare("INSERT INTO continental_regions (CONTINENT,COUNTRY) values (?, ?)");
$stmt->bind_param("ss", $CN, $CT);
$stmt->execute();
} catch (mysqli_sql_exception $e) {
if ($e->getCode() !== DUPLICATE_KEY) {
// if it failed for any other reason than duplicate key rethrow the exception
throw $e;
}
// if SQL failed due to duplicate entry then set the error message
$arr = [
'PASS' => "FAIL",
'MSG' => "Err: duplicate '$CN' '$CT' pair",
];
}
echo json_encode($arr);
} else {
header("location: ../Error_Fail.php");
}
关于性能。
此示例中的性能没有问题,准备好的语句不会提高或降低性能。我假设您正在尝试将性能与静态 SQL 查询进行比较,但在您的简单示例中应该没有任何区别。当您需要多次执行相同的 SQL 时,与静态查询相比,准备好的语句可以使您的代码更快。
如果您觉得每次写 3 行代码太多,那么您可以创建一个包装函数,将它减少为单个函数调用。公平地说,您应该避免单独使用 mysqli。要么切换到 PDO,要么使用围绕 mysqli 的某种抽象库。
我的问题是,PHP Mysqli 准备语句的效率如何?根据我从基本阅读中了解到的内容,准备好的语句 1) 使用绑定输入帮助提高安全性 2) 加快 'reduce' 数据发送到服务器的速度 'pre-packaging' 或 'preparing' sql 查询到一定程度,一旦数据可用,它只是将数据附加到准备好的语句并执行它。这也有助于 'repeated' 在重复插入相同数据(不同值)时使用相同的语句,因为语句只准备一次。
现在,我正在构建一个具有多种功能的网站,所有(或大部分)功能都使用 JQuery 和 AJAX 来获取用户输入,做一些检查(在 JS/JQ or in PHP), 将数据发送到 PHP 文件 PHP_AJAX_Handler.php 在 AJAX URL 中指定。 PHP 文件准备 SQL 语句将数据插入数据库,然后是 return JSON success/failure 消息。比如我的大部分features/functionality都是这样编程的;下面是一个文件,我用它来 1) 检查现有的大陆-国家对,以及 2) 插入新的大陆-国家对。
HTML:
<input type='text' id='continent'>
<input type='text' id='country'>
<button id='btn1'></button>
<p id='p1'></p>
<p id='p2'></p>
<p id='p3'></p>
JQuery:
$("#btn1")click(function(){
var Cntt = $("#continent").val();
var Ctry = $("#country").val();
$.post("PHP_AJAX_Handler.php",{CN:cntt,CT:ctry},function(DAT)
{ var RET_j = json.PARSE(dat);
if(RET_j.PASS=='FAIL')
{ $('#p1').html(RET_j.PASS);
$('#p2').html(RET_j.MSG1);
}
if(RET_j.PASS=='OKAY')
{ $('#p1').html(RET_j.PASS);
$('#p3').html(RET_j.MSG2);
} }
);
});
PHP_AJAX_Handler.php
<?PHP
session_start();
if( (isset($_POST['CT'])) && (isset($_POST['CN'])))
{ require_once ("golin_2.php");
$CN = $_POST['CN'];
$CT = $_POST['CT'];
$ER = "";
$CONN = mysqli_connect($SERVER, $USER, $PASS, $DBNAME);
If($CONN == FALSE)
{ $ER = $ER . "Err: Conn Could not connect to Databse ".mysqli_connect_errno().' '.mysqli_connect_error();
}
else
{ $SQL_1 = "SELECT * FROM sailors.continental_regions WHERE CONTINENT = ? AND COUNTRY = ?";
if(!($STMT_1 = mysqli_stmt_init($CONN)))
{ $ER = $ER . "Err: Stmt Prepare Failed";
}
else
{ if(!mysqli_stmt_prepare($STMT_1, $SQL_1)) ///FIRST SET of prepared statement lines
{ $ER = $ER . "Err: Stmt Prepare Failed";
}
else
{ if(!mysqli_stmt_bind_param($STMT_1,"ss",$CN, $CT))
{ $ER = $ER . "Err: Stmt Prepare Failed";
}
else
{ if(!(mysqli_stmt_execute($STMT_1)))
{ $ER = $ER . "Err: Stmt_1 Execute Failed";
}
else
{ $RES_1 = mysqli_stmt_get_result($STMT_1);
$NUMROWS_1 = mysqli_num_rows($RES_1);
if($NUMROWS_1>0)
{ $ER = $ER . "Err: duplicate '$CN' '$CT' pair";
}
if($NUMROWS_1==0)
{ $SQL_2 = "INSERT INTO DB.continental_regions (CONTINENT,COUNTRY) values (?, ?)";
if(!($STMT_2=(mysqli_stmt_init($CONN))))
{ $ER = $ER . "Err: Init2 failed";
}
else
{ if(!mysqli_stmt_prepare($STMT_2, $SQL_2)) ///SECOND SET of prepared statement lines
{ $ER = $ER . "Err: Prep2 failed".mysqli_error($CONN);
}
else
{ if(!mysqli_stmt_bind_param($STMT_2,"ss",$CN, $CT))
{ $ER = $ER . "Err: Bind2 failed";
}
else
{
if(!(mysqli_stmt_execute($STMT_2)))
{ $ER = $ER . "Err: Exec failed";
}
else
{ $arr['PASS'] = 'OK';
}
}
}
}
}
}
}
}
}
mysqli_free_result($RES_1);
mysqli_stmt_close($STMT_1);
mysqli_stmt_close($STMT_2);
mysqli_close($CONN);
}
if($ER!=="")
{ $arr['MSG'] = $ER;
$arr['PASS'] = 'FAIL';
}
if($arr['PASS']=="OK")
{ $arr['MSG2'] = "Insert Success";
}
echo json_encode($arr);
}
else
{ header("location: ../Error_Fail.php");
}
?>
如您所见,PHP 文件变得相当长。有一组准备语句检查 CC 对是否已经存在于 table 中,然后另一组准备语句插入 CC 对。 据我所知,对于每个添加一对新值的 AJAX 请求,mysqli 语句都会重新准备。然后再次为下一个请求,依此类推。我想这会为服务器创建大量开销和数据,只是为了实现安全性。对于其他使用 AJAX-POST-PHP 开发 Web 应用程序的人来说也是如此吗?对我来说似乎不可避免的是,对于每个准备,值只能插入一次?如何绕过一次准备此语句,并且只在数据可用时重复执行?我似乎无法理解准备好的语句的 'efficiency' 因素..
谢谢..希望能从一些经验丰富的程序员那里得到一些建议..
你说:
As you can see, the PHP file is turning out to be pretty long.
确实如此,但这不是准备好的语句的错。您一定是通过编写糟糕的教程学习了 PHP 开发。这段代码不需要那么长。事实上,它可以大大缩短。
只需修复您现有的代码即可使其更具可读性。我使用 OOP-style mysqli 并删除了所有这些 if
语句。您应该改为启用错误报告。
<?php
session_start();
if (isset($_POST['CT'],$_POST['CN'])) {
require_once "golin_2.php";
$CN = $_POST['CN'];
$CT = $_POST['CT'];
$ER = "";
$arr = [
'PASS' => "OK",
'MSG2' => "Insert Success",
]; // successful state should be the default outcome
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$CONN = new mysqli($SERVER, $USER, $PASS, $DBNAME);
$CONN->set_charset('utf8mb4'); // always set the charset
// To check existance of data in database we use COUNT(*)
$stmt = $CONN->prepare("SELECT COUNT(*) FROM sailors.continental_regions WHERE CONTINENT = ? AND COUNTRY = ?");
$stmt->bind_param("ss", $CN, $CT);
$stmt->execute();
$NUMROWS = $stmt->get_result()->fetch_row()[0];
if ($NUMROWS) {
$ER .= "Err: duplicate '$CN' '$CT' pair";
} else {
$stmt = $CONN->prepare("INSERT INTO DB.continental_regions (CONTINENT,COUNTRY) values (?, ?)");
$stmt->bind_param("ss", $CN, $CT);
$stmt->execute();
}
if ($ER) {
$arr = [
'PASS' => "FAIL",
'MSG' => $ER,
];
}
echo json_encode($arr);
} else {
header("location: ../Error_Fail.php");
}
如果您在 table 的这两列上有一个复合 UNIQUE 键,那么您可以删除 select 语句。此外,您应该清理您的响应准备。成功状态应该是默认状态,只有在出现问题时才应将其替换为错误消息。
在这个例子中,我删除了一个 SQL 语句。整个事情现在简单多了。
<?php
define('DUPLICATE_KEY', 1062);
session_start();
if (isset($_POST['CT'],$_POST['CN'])) {
require_once "golin_2.php";
$CN = $_POST['CN'];
$CT = $_POST['CT'];
$arr = [
'PASS' => "OK",
'MSG2' => "Insert Success",
]; // successful state should be the default outcome
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$CONN = new mysqli($SERVER, $USER, $PASS, $DBNAME);
$CONN->set_charset('utf8mb4'); // always set the charset
try {
$stmt = $CONN->prepare("INSERT INTO continental_regions (CONTINENT,COUNTRY) values (?, ?)");
$stmt->bind_param("ss", $CN, $CT);
$stmt->execute();
} catch (mysqli_sql_exception $e) {
if ($e->getCode() !== DUPLICATE_KEY) {
// if it failed for any other reason than duplicate key rethrow the exception
throw $e;
}
// if SQL failed due to duplicate entry then set the error message
$arr = [
'PASS' => "FAIL",
'MSG' => "Err: duplicate '$CN' '$CT' pair",
];
}
echo json_encode($arr);
} else {
header("location: ../Error_Fail.php");
}
关于性能。
此示例中的性能没有问题,准备好的语句不会提高或降低性能。我假设您正在尝试将性能与静态 SQL 查询进行比较,但在您的简单示例中应该没有任何区别。当您需要多次执行相同的 SQL 时,与静态查询相比,准备好的语句可以使您的代码更快。
如果您觉得每次写 3 行代码太多,那么您可以创建一个包装函数,将它减少为单个函数调用。公平地说,您应该避免单独使用 mysqli。要么切换到 PDO,要么使用围绕 mysqli 的某种抽象库。