2

我有一个数据库表 USER_OPTIONS,其中包含应用程序中各种选项中的每一个的字段,它看起来有效如下:

USER_ID | OPTION_1 | OPTION_2 | OPTION_3 | ... | OPTION 20
----------------------------------------------------------
 100    | true     | true     | false    | ... | false 
 101    | true     | true     | false    | ... | false 
 102    | false    | false    | false    | ... | true 
 ... etc etc

然后我的应用程序想要触发一个 AJAX 请求来调整这个表。

通过 AJAX 发送的信息是一组已更改的数据库字段及其新值(JSON 格式),例如

$my_new_settings === {
    'OPTION_1' : 'false',
    'OPTION_2' : 'false'
    'OPTION_20' : 'true'
}

然后我的计划是使用参数化的 SQL 查询,如下所示

$query_string = "UPDATE `USER_OPTIONS` 
               SET ? = ? 
               WHERE  
               `USER_ID` = ? 
               LIMIT 1 ;";

$my_user_id = 100;
$my_field_for_updating = "OPTION_1";
$my_new_field_value = "false";

if ( $query = $mysqli->prepare($query_string) ) {

    $query->bind_param("sss", $my_field_for_updating, $my_new_field_value, $my_user_id);
    $query->execute();
    $query->close();

}

然后遍历关联数组以获取彼此的一对$my_field_for_updating$my_new_field_valuefrom $my_new_settings

然而。

我随后在 php.net 手册中发现了以下内容

(“标记”是用于参数化 SQL 查询中“?”变量的术语)

笔记:

标记仅在 SQL 语句中的某些位置是合法的。例如,它们可以在 INSERT 语句的 VALUES() 列表中使用(以指定行的列值),或者在与 WHERE 子句中的列进行比较以指定比较值时。

但是,它们不允许用于标识符(例如表名或列名),在命名要由 SELECT 语句返回的列的选择列表中,或指定二元运算符(例如 = 等号)的两个操作数。后一个限制是必要的,因为不可能确定参数类型。不允许将标记与 NULL 进行比较?也为空。通常,参数仅在数据操作语言 (DML) 语句中合法,在数据定义语言 (DDL) 语句中不合法。

因此,这破坏了我提出的解决方案。

我的下一个想法是有一个 SQL 查询来更新数据库中的所有字段,但只包含新更新的字段值。但是我发现这需要一个神奇的“什么都不做”变量来分配给我没有数据的字段的“标记”。

$query_string = "UPDATE `USER_OPTIONS` 
                   SET `OPTION_1` = ?,
                   SET `OPTION_2` = ?,
                   SET `OPTION_3` = ?,
                   ...
                   SET `OPTION_20` = ?
                   WHERE  
                   `USER_ID` = ? 
                   LIMIT 1 ;";

$my_user_id = 100;
$my_new_OPTION_1_value = 'false';
$my_new_OPTION_2_value = 'false';
$my_new_OPTION_20_value = 'true';
$magic_do_nothing_value = ????;

if ( $query = $mysqli->prepare($query_string) ) {

    $query->bind_param("sssssssssssssssssssss", 
       $my_new_OPTION_1_value, 
       $my_new_OPTION_2_value,
       $magic_do_nothing_value,
       $magic_do_nothing_value,
       ...
       $magic_do_nothing_value,
       $my_new_OPTION_20_value,
       $my_user_id
    );
    $query->execute();
    $query->close();

}

但是,我不知道$magic_do_nothing_value实际可能是什么(并且发现很难用谷歌搜索)。我尝试的一切要么抛出错误,要么被解释为“错误”。

所以我的第一个问题是:MYSQL 数据库是否接受一个值作为“什么都不做”的信号?

假设没有并继续前进,我的下一个想法是将所有当前值从数据库中拖出给定的USER_ID,使用更改修改这些值$my_new_settings,然后使用所有字段相应地更新数据库。

然而,这似乎是很多不必要的工作,尤其是与我的第一个解决方案相比。

这导致我在这里要求一个更好的!

非常感谢您的任何想法,我真的很想听听他们的意见。这是在 PHP 5.2 和一个基本的 MySQL 5.0.92 数据库上运行的。

4

2 回答 2

1

我将简单地动态构建 SQL,然后像您已经完成的那样绑定参数值。这是一个使用类来处理动态数量的参数的示例(取自 PHP 文档的类)。请注意我没有测试过代码,但是逻辑应该是清楚的。

// This class will store the value for each query parameter
class BindParam {
  private $values = array(), $types = '';

  public function add($type, &$value) {
    $this->values[] = $value; 
    $this->types .= $type; 
  } 

  public function get() {
    return array_merge(array($this->types), $this->values); 
  } 
}

$my_new_settings = array(
    'OPTION_1' => 'false',
    'OPTION_2' => 'false',
    'OPTION_20' => 'true',
)


$bind_param = new BindParam();

$query_arr = array("UPDATE `USER_OPTIONS`");

// This loop will build the query and store each parameter value in bin_param instance
foreach($my_new_settings as $field_name => $field_value) {
  $query_arr[] = sprintf('`%s` = ?', $field_name);
  $bind_param->add('s', $field_value);
}

$query_arr[] = 'WHERE `USER_ID` = ?';
$query_arr[] = 'LIMIT 1 ;";

// Put all pieces together, forming an SQL command
$query_string = implode(' ', $query_arr);

$my_user_id = 100;
$my_field_for_updating = "OPTION_1";
$my_new_field_value = "false";

if($query = $mysqli->prepare($query_string)) {
  // Bind all parameter values saved earlier
  call_user_func_array(array($query, 'bind_param'), $bind_param->get())

  $query->execute();
  $query->close();
}
于 2012-08-21T23:55:36.503 回答
0

抱歉,我还没有开始使用 mysli 或 PDO(懒惰我 :))但是当我需要插入或更新 mysql 表时,我会实现这个函数。由于基于 mysqli 的使用,您可能不会直接使用,但可能会给您一个想法。查找表的字段名并将它们与给定的数组键名匹配(即使具有指定的前缀),您还需要在 $sql_condition 和 $exceptions 中指示更新的键字段以过滤要插入的不需要的数据。

在我的使用中,键名以“the_”开头,如果以“t_”开头,则自动忽略。

  function q_put2($data_array=array() ,$table, $exceptions = '', $sql_type = 'insert', $sql_condition = NULL) {
      if($sql_condition!=NULL) $table_columns_filter=$sql_condition;
else $table_columns_filter="";
$table_columns_qs="SHOW COLUMNS FROM $table ;";
$table_columns_rs=mysql_query($table_columns_qs);

while ($row=mysql_fetch_row($table_columns_rs)){
   $table_columns[]=$row[0];
} 

$fields = '';$values = '';
foreach ($data_array as $field => $value) {
if(substr($field,0,4)=="the_")$field=substr($field,4);
    if(substr($field,0,2)!="t_" && !strstr($exceptions,$field) &&  in_array($field,$table_columns)){
        $value = mysql_real_escape_string($value);
        if ($sql_type == 'insert') {
        $fields .= "$field, ";
        $values .= "'$value', ";
        }
        else {
            $fields .= "$field = '$value', ";
        }
    }
}
$fields = preg_replace('/, $/', '', $fields);
$values = preg_replace('/, $/', '', $values);

if ($sql_type == 'insert') {
 $sql = "INSERT INTO $table ($fields) VALUES ($values)";
}
elseif ($sql_type == 'update') {
 if (!isset($sql_condition)) {
   echo 'ERROR: You must enter a sql condition!';
   exit;
 }
 $sql = "UPDATE $table SET $fields WHERE $sql_condition";}
else {
 echo 'ERROR: Invalid input for argument $sql_type: must be "insert" or "update"';
 exit;
}
if ($q_select_rs=mysql_query($sql)) {
return true;
} 
else 
{
return false;
}
}
于 2012-08-22T00:06:32.790 回答