【问题标题】:SQL Server INSERT query with ODBC & PHP : weird syntax error使用 ODBC 和 PHP 的 SQL Server INSERT 查询:奇怪的语法错误
【发布时间】:2020-01-27 15:52:11
【问题描述】:

我有一个 PHP 网页,在事务中运行一些 SQL 查询,以写入 SQL Server 2008 R2 数据库。生产服务器使用:

  • PHP 5.3 32 位
  • ODBC SQL Server 驱动程序 32 位版本 6.01
  • IIS 7.5

在这种环境下一切正常。

我必须在新的 Windows Server 2016 服务器上升级到 PHP 7.3 64 位,运行 IIS 10 和更新的 SQL Server ODBC 驱动程序(64 位)。我尝试运行同一页面,但其中一个查询不起作用:

INSERT INTO LIGNELIVRAISON 
(ARTICLEID, LCDEID, LIVID, LIGNELIVQTELIV, LIGNELIVDATECREATION, LIGNELIVQTEINITIALE, LIGNELIVMTUNITHT, LIGNELIVMTUNITTVA, LIGNELIVMTUNITTTC, LIGNELIVMTREMISE, LIGNELIVCODELOT, LIGNELIVDATEPEREMPTION) 
VALUES 
(1573, 44551, 35232, 3, CONVERT(datetime, '27/09/2019 13:48:54'), 3, 50, 4.25, 54.25, 0, '???', CONVERT(date, '31/12/2050'));

错误信息是:

代码 37000:[Microsoft][ODBC SQL Server Driver][SQL Server]'*=' 附近的语法错误。

正如我们所见,这个查询中没有字符“*=”。我尝试了多个版本的 PHP(5 或 7)、多个和更新的 ODBC 驱动程序,但是当我通过 odbc_exec() 函数运行此查询时,我总是得到相同的结果。我尝试使用 PDO 或 sqlsrv 函数,但结果是一样的。

如果我尝试在 SQL Server Management Studio 中复制/粘贴此查询,它可以正常工作。

我认为它可能与 ODBC 驱动程序有关,但我无法安装当前正在生产的同一个驱动程序,因为它在 Windows 2016 上不起作用并且它是一个 32 位驱动程序。我尝试运行 32 位版本的 PHP 7.3 无济于事。

部分PHP代码:

$ms->hdebug();
    //$ms->libre('SET IMPLICIT_TRANSACTIONS OFF');
    //$ms->libre('BEGIN TRANSACTION');
    odbc_autocommit($ms->get_lien(), false);

    $id_utilisateur_creation = id_utilisateur_athome($my, $ms);

    // On a ajouté un contrôle en amont mais par sécurité, on ajoute un contrôle ici
    if ($id_utilisateur_creation === FALSE) {
      $id_utilisateur_creation = 1;
    }

    // 25 = Fournisseur matériel
    $id_type_intervenant = 25; 
    $type_facture = 'F';
    // 2 = Location
    $id_type_produit = 2;
    // 5 = Livrée
    $id_etat_commande = 5;
    $franco_port = 'N';
    $tproduitcode = 'LOCA';
    $date = date('d/m/Y');
    $date_heure = date('d/m/Y H:i:s');
    $montant_remise = 0;
    $total_ttc = 0;
    $etat_sejour_suivi = 14;
    // Pour éviter une erreur lors de l'insertion des lignes de livraison, liées a une procédure stockée
    $code_lot_article = '???';
    $date_peremption_article = '31/12/2050';
    $commentaires = addslashes('Commande créée pour le bon de location n° ' . $_POST['id_location']);

    // Récupération des TVA AtHome
    $tva_athome = $ms->lire(
      'TVAID, TVATAUX * 100',
      'TVA',
      "AND TVAVISIBLE = 'O'",
      "TVATAUX ASC"
    );

    $mapping_tva = array();

    foreach($tva_athome as $tva) {
      list($id_tva, $taux_tva) = $tva;
      $mapping_tva[$id_tva] = round($taux_tva, 2);
    }
    // -

    // Etape 1: Création de la commande et lignes de commande
    $id_commande = $ms->ajouter(
      'CDEFRN',
      'UTIID, TPRODUITID, FOURNID, ETATCDEID, PATID,
       CDEFRNDATE, CDEFRNFRANCOPORT, CDEFRNDATECREATION, TProduitCode, CDEFRNCOMMENTAIRE',
      "$id_utilisateur_creation, $id_type_produit, {$_POST['id_fournisseur']}, $id_etat_commande, {$_POST['ipp']},
       CONVERT(date, '$date'), '$franco_port', CONVERT(datetime, '$date_heure'), '$tproduitcode', '$commentaires'"
    );

    $lignes_commande = array();

    // Ajout des lignes d'articles
    foreach($_POST['articles'] as $id_article) {
      if ($_POST['prix'][$id_article] > 0 && $_POST['quantite'][$id_article] > 0) {
        $quantite = $_POST['quantite'][$id_article];
        $taux_tva = $mapping_tva[$_POST['tva'][$id_article]];
        $prix_ht = round($_POST['prix'][$id_article], 2);
        // On calcule les arrondis ici pour ne pas avoir de problème d'arrondi dans AtHome à cause des champs de type money
        $prix_ttc = round($prix_ht * (1 + ($taux_tva / 100)), 2);
        $montant_tva = $prix_ttc - $prix_ht;

        $lignes_commande[$id_article] = $ms->ajouter(
          'LIGNECDEFRN',
          'CDEFRNID, ARTICLEID, LCDEQTECDE, LCDEMTHT, LCDEMTTVA,
           LCDEMTTTC, LCDEMTREMISE, LCDEDATECREATION, LCDEQTEINITIALE',
          "$id_commande, $id_article, $quantite, $prix_ht, $montant_tva,
           $prix_ttc, $montant_remise, CONVERT(datetime, '$date_heure'), $quantite"
        );

        $total_ttc += $prix_ttc;
      }
    }

    $total_ttc = round($total_ttc, 2);

    // Etape 2: Livraison
    $id_livraison = $ms->ajouter(
      'LIVRAISON',
      'CDEFRNID, LIVDATE, LIVDATECREATION',
      "$id_commande, CONVERT(date, '$date'), CONVERT(datetime, '$date_heure')"
    );

    $lignes_livraison = array();

    // Ajout des lignes de livraison
    foreach($_POST['articles'] as $id_article) {
      if ($_POST['prix'][$id_article] > 0 && $_POST['quantite'][$id_article] > 0) {
        $quantite = $_POST['quantite'][$id_article];
        $taux_tva = $mapping_tva[$_POST['tva'][$id_article]];
        $prix_ht = round($_POST['prix'][$id_article], 2);
        // On calcule les arrondis ici pour ne pas avoir de problème d'arrondi dans AtHome à cause des champs de type money
        $prix_ttc = round($prix_ht * (1 + ($taux_tva / 100)), 2);
        $montant_tva = $prix_ttc - $prix_ht;

        $lignes_livraison[$id_article] = $ms->ajouter(
          'LIGNELIVRAISON',
          'ARTICLEID, LCDEID, LIVID, LIGNELIVQTELIV, LIGNELIVDATECREATION,
           LIGNELIVQTEINITIALE, LIGNELIVMTUNITHT, LIGNELIVMTUNITTVA, LIGNELIVMTUNITTTC, LIGNELIVMTREMISE,
           LIGNELIVCODELOT, LIGNELIVDATEPEREMPTION',
          "$id_article, {$lignes_commande[$id_article]}, $id_livraison, $quantite, CONVERT(datetime, '$date_heure'),
           $quantite, $prix_ht, $montant_tva, $prix_ttc, $montant_remise,
           '$code_lot_article', CONVERT(date, '$date_peremption_article')"
        );
      }
    }
public function ajouter($table,$champs,$valeurs)
    {
      if(trim($champs)=="")
      {
        $req="INSERT INTO ".$table." VALUES(".$valeurs.");";
      }
      else
      {
        $req="INSERT INTO ".$table." (".$champs.") VALUES(".$valeurs.");";
      }
      $retour=-1;
      if($this->hdebug)
      {
        $this->log->logguer("INS",$req);
      }
      if($this->debug)
      {
        echo "<debug_sql>".$req."</debug_sql>";
      }
      else
      {
        if($this->calcul_temps)
        {
          $_SESSION["Temps_Execution"]->Debut_Compter("MsSQL");
        }
        //$res = @mssql_query($req,$this->lien) or $this->erreur("insert",$req,0,"");
        //$res = odbc_prepare($this->lien, $req);
        //$res_ex = odbc_execute($res);

        $res = odbc_exec($this->lien, $req);

        if (!$res) {
          //var_dump($req);
          $this->erreur("insert", $req, odbc_error($this->lien), odbc_errormsg($this->lien));
        }

        $id_retour=@odbc_exec($this->lien,"SELECT @@IDENTITY") or $this->erreur("insert",$req,0,"");
        $retour=odbc_result($id_retour, 1);
        if($this->calcul_temps)
        {
          $_SESSION["Temps_Execution"]->Fin_Compter("MsSQL");
        }
        //$retour=mysql_insert_id($this->lien);
      }
      //$retour=0;
      return $retour;
    }

编辑:查询中的 FR 日期时间格式工作正常。我认为问题根本与它无关。

【问题讨论】:

  • 您的查询中的两个 CONVERT 调用似乎都不适用于 SQL Server 2016。不过我没有答案。
  • CONVERT 函数在我的服务器上运行良好。我有一个以前的 INSERT 查询,它使用它并且工作正常。
  • 我们能多看看你的 php 代码吗?
  • 我添加了一些 PHP 代码。
  • 另外:如果我在发送查询的函数中注释if (!$res) { //var_dump($req); $this-&gt;erreur("insert", $req, odbc_error($this-&gt;lien), odbc_errormsg($this-&gt;lien)); } 部分,它工作得很好......但这不是解决方案,我需要能够检查错误。

标签: php sql-server windows odbc


【解决方案1】:

您需要传递正确的日期时间格式(即:MM/dd/yyyy hh:mm:ss)。因为 SQL Server 默认理解美国日期时间标准。

请检查下面的选择查询。

SELECT 
    CONVERT(date, '12/31/2050') LIGNELIVDATEPEREMPTION,
    CONVERT(datetime, '09/27/2019 13:48:54') LIGNELIVDATECREATION

请检查下面的插入脚本以获得您的答案。

INSERT INTO LIGNELIVRAISON 
(ARTICLEID, LCDEID, LIVID, LIGNELIVQTELIV, LIGNELIVDATECREATION, LIGNELIVQTEINITIALE, LIGNELIVMTUNITHT, LIGNELIVMTUNITTVA, LIGNELIVMTUNITTTC, LIGNELIVMTREMISE, LIGNELIVCODELOT, LIGNELIVDATEPEREMPTION) 
VALUES 
(1573, 44551, 35232, 3, CONVERT(datetime, '09/27/2019 13:48:54'), 3, 50, 4.25, 54.25, 0, '???', CONVERT(date, '12/31/2050'));

【讨论】:

  • 之前的 INSERT 请求适用于 FR 日期时间标准。尽管如此,我还是尝试了,但不出所料,我收到了错误代码 22008:日期时间字段溢出。
猜你喜欢
  • 1970-01-01
  • 2014-04-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多