Mysqli使用AJAX POST到PHP文件的预准备语句用法

问题描述

我的问题是,PHP MysqLi准备的语句的效率如何?从我的基础阅读中了解到,准备好的语句1)使用绑定输入来帮助安全2)通过某种程度地“预打包”或“准备” SQL查询来加速和“减少”发送到服务器的数据,一旦有数据可用,它就将数据附加到准备好的语句上并执行它。这也有助于在重复插入相同数据(不同值)时“重复”使用同一语句,因为该语句仅准备一次。

现在,我正在构建一个具有多个功能的网站,并且所有(或大多数)功能都使用JQuery和AJAX来获取用户输入,进行一些检查(在JS / JQ或PHP中),发送数据到AJAX URL中指定的PHP文件PHP_AJAX_Handler.PHPPHP文件准备sql statemtns以将数据插入数据库,然后返回JSON成功/失败消息。例如,我的大多数功能都按如下编程;以下是我正在使用的一个文件: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,$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对,然后提供另一组语句来插入CC对。 从我所看到的,对于每个添加新值对的AJAX请求,将再次准备MysqLi语句。然后再次用于下一个请求,依此类推。我想这只是为了实现安全性而给服务器造成大量开销和数据。这对于使用AJAX-POST-PHP开发Web应用程序的其他人是否正确?对我来说,每次准备似乎都不可避免地只能插入一次值?当数据可用时,如何处理一次准备该语句,并且仅重复执行一次?我似乎无法理解准备好的陈述的“效率”因素。

谢谢..我们将感谢一些经验丰富的程序员的建议。

解决方法

您说:

如您所见,PHP文件变得相当长。

是的,但这不是准备好的语句的错。您一定已经从一篇写得不好的教程中学习了PHP开发。此代码不必太长。实际上,它可以大大缩短。

仅修复现有代码即可使其更具可读性。我使用了OOP风格的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",$CT);
        $stmt->execute();
    }
            
    if ($ER) {
        $arr = [
            'PASS' => "FAIL",'MSG' => $ER,];
    }
    echo json_encode($arr);
} else {
    header("location: ../Error_Fail.php");
}

如果表的这两列上都有一个复合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",$DBNAME);
    $CONN->set_charset('utf8mb4'); // always set the charset

    try {
        $stmt = $CONN->prepare("INSERT INTO continental_regions (CONTINENT,$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周围使用某种抽象库。