【问题标题】:SQLSRV using an array for the IN clause in the UPDATE statementSQLSRV 使用数组作为 UPDATE 语句中的 IN 子句
【发布时间】:2021-02-14 23:06:23
【问题描述】:

我想更新一个表,其中列 user_id 具有值,存储在一个数组中,但我收到此错误:

数组([0] => 数组([0] => 42S22 [SQLSTATE] => 42S22 [1] => 207 [代码] => 207 [2] => [Microsoft][ODBC Driver 17 for SQL Server][SQL 服务器]列名“数组”无效。 [消息] => [微软][ODBC 用于 SQL Server 的驱动程序 17][SQL Server]列名“数组”无效。 ))

这是我的代码:

$sql = "
   SELECT usuario_id 
   FROM control_asistencias 
   WHERE 
      ano=".$ano." and 
      mes=".$mes." and 
      dia".$dia."='T' and 
      comida_habitual ='T' 
";
$stmt = sqlsrv_query( $conn, $sql);

if( $stmt === false ) {
   die( print_r( sqlsrv_errors(), true) );
}


$usuarios_comida_habitual = array();
while( $row = sqlsrv_fetch_array( $stmt, SQLSRV_FETCH_ASSOC) ) {
    array_push($usuarios_comida_habitual,$row['usuario_id']);
}

// Una vez que tenemos los usuarios Que han fichado para ese dia hacemos un Update

$sql_update = "
   UPDATE VLD_PRESENCIA 
   SET VLD_PRESENCIA_DIA".$dia." = 'T' 
   WHERE 
      VLD_PRESENCIA_CODUSUARIO IN (".$usuarios_comida_habitual.") AND
      VLD_PRESENCIA_COMIDA_HABITUAL = 'T'
   ";
     
   $stmt2 = sqlsrv_query( $conn2, $sql_update);
   if( $stmt2 === false ) {
       die( print_r( sqlsrv_errors(), true) );
   }
   else
   {
      echo 'eureka';
   }

有没有可能是数组的传递方式不对?或者是否可以设置我想要实现 userid 应该在该数组中的条件?

我找到了这个

 array_walk($usuarios_comida_habitual , 'intval');
 $ids = implode(',', $usuarios_comida_habitual);

我在生成 $sql_update 之前执行此操作

【问题讨论】:

  • 如果您认为这个或任何其他答案是您问题的最佳解决方案,您可以accept它。只能接受一个答案。

标签: php sql sql-server sqlsrv


【解决方案1】:

虽然IN子句使用数组已经有类似answer,你可以尝试使用sqlsrv_prepare()\sqlsrv_execute()组合来准备一个语句并多次执行。但在这两种情况下,您都需要考虑以下几点:

  • 始终尝试在语句中使用参数来防止可能的 SQL 注入问题。
  • 动态使用列名时(可能设计错误),使用sys.columns系统目录视图检查生成的名称,以防止可能的SQL注入问题。

请注意,您的错误原因是您正在尝试构建一个连接字符串文字和 PHP 数组的 SQL 语句。

以下示例基于您的代码并使用参数化查询,是您问题的可能解决方案:

<?php
// SELECT statement
$sql = "
    SELECT usuario_id 
    FROM control_asistencias 
    WHERE ano = ? AND mes = ? AND dia".$dia." = 'T' AND comida_habitual = 'T' 
";
$params = array($ano, $mes);
$stmt = sqlsrv_query($conn, $sql, $params);
if ( $stmt === false ) {
    die( print_r( sqlsrv_errors(), true) );
}
$usuarios_comida_habitual = array();
while ( $row = sqlsrv_fetch_array( $stmt, SQLSRV_FETCH_ASSOC) ) {
    $usuarios_comida_habitual[] = $row['usuario_id'];
}

// Multiple UPDATE statements
$sql = "
    UPDATE VLD_PRESENCIA 
    SET VLD_PRESENCIA_DIA".$dia." = 'T' 
    WHERE 
        VLD_PRESENCIA_CODUSUARIO = ? AND 
        VLD_PRESENCIA_COMIDA_HABITUAL = 'T'
";
$usuario = 0;
$params2 = array(&$usuario);
$stmt2 = sqlsrv_prepare($conn2, $sql, $params2);
if ( $stmt2 === false ) {
    die( print_r( sqlsrv_errors(), true) );
}
foreach ($usuarios_comida_habitual as $usuario) {
    if (sqlsrv_execute($stmt2) === false ) {
        die( print_r( sqlsrv_errors(), true) );
    } else {
        echo 'eureka';
    }
}   
?>       

如果要执行单个UPDATE 语句,可以选择以下方法,但同样使用参数化语句:

<?php
// SELECT statement
$sql = "
    SELECT usuario_id 
    FROM control_asistencias 
    WHERE ano = ? AND mes = ? AND dia".$dia." = 'T' AND comida_habitual = 'T' 
";
$params = array($ano, $mes);
$stmt = sqlsrv_query($conn, $sql, $params);
if ( $stmt === false ) {
    die( print_r( sqlsrv_errors(), true) );
}
$usuarios_comida_habitual = array();
while ( $row = sqlsrv_fetch_array( $stmt, SQLSRV_FETCH_ASSOC) ) {
    $usuarios_comida_habitual[] = $row['usuario_id'];
}

// Single UPDATE statement
$usuarios_placeholders = substr(str_repeat(',?', count($usuarios_comida_habitual)), 1);
$sql = "
    UPDATE VLD_PRESENCIA 
    SET VLD_PRESENCIA_DIA".$dia." = 'T' 
    WHERE 
        VLD_PRESENCIA_CODUSUARIO IN (".$usuarios_placeholders.") AND 
        VLD_PRESENCIA_COMIDA_HABITUAL = 'T'
";
$stmt2 = sqlsrv_query($conn2, $sql, $usuarios_comida_habitual);
if ( $stmt2 === false ) {
    die( print_r( sqlsrv_errors(), true) );
} else {
        echo 'eureka';
}   
?>   

【讨论】:

    【解决方案2】:

    考虑一个没有任何循环的 SQL 查询,因为 IN 支持子查询。由于连接跨不同的服务器运行,请考虑将OPENROWSET 替换为ad hoc queries。相应地调整 ODBC 连接字符串参数和 [database].[schema] 标识符。 (注意:默认架构是dbo)。

    $sql = "UPDATE VLD_PRESENCIA 
            SET VLD_PRESENCIA_DIA".$dia." = 'T' 
            WHERE VLD_PRESENCIA_CODUSUARIO 
               IN (SELECT sub.usuario_id 
                   FROM OPENROWSET('SQLNCLI',
                                   'DRIVER={SQL Server};SERVER=ServerName;UID=userID;PWD=password',
                                   'SELECT * FROM [database].[schema].control_asistencias') sub
                   WHERE sub.ano = ? 
                     AND sub.mes = ? 
                     AND sub.dia".$dia." = 'T'
                     AND sub.comida_habitual = 'T') 
            AND VLD_PRESENCIA_COMIDA_HABITUAL = 'T'"; 
    
    $prms = array($ano, $mes);
    $stmt = sqlsrv_query($conn, $sql, $prms);
    
    if( $stmt === false ) {
         die( print_r( sqlsrv_errors(), true));
    }
    

    【讨论】:

    • 看起来很合理,如果$conn$conn2 保持到同一个服务器和数据库的连接。
    • @Zhorov,好收获!我调整了 SQL,假设两个连接都指向同一个服务器,不一定是同一个数据库。
    • 这是一个测试,但是连接在不同的服务器上
    • 然后,考虑ad hoc queries从不同的后端查询。编辑节目OPENROWSET
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-22
    • 1970-01-01
    • 2013-10-16
    • 2015-05-07
    相关资源
    最近更新 更多