SQLSRV 使用数组作为 UPDATE 语句中的 IN 子句

Posted

技术标签:

【中文标题】SQLSRV 使用数组作为 UPDATE 语句中的 IN 子句【英文标题】:SQLSRV using an array for the IN clause in the UPDATE statement 【发布时间】: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它。只能接受一个答案。 【参考方案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

以上是关于SQLSRV 使用数组作为 UPDATE 语句中的 IN 子句的主要内容,如果未能解决你的问题,请参考以下文章

在 PHP 中使用带有 sqlsrv_query 函数的临时表不会产生任何结果

sqlsrv函数 备用查询

SQL Server Driver for PHP之sqlsrv相关函数

我应该在 Oracle Update 语句中使用 Too Many Rows Error 作为例外子句吗?

oracle update语句将一个语句的查询结果作为set值怎么做?【特急】

PHP 和 SQLSRV 没有插入它应该插入的所有行