2017-04-04 2 views
8

ODBC経由で接続されたMSSQLデータベースを使用しています。ネストされたSELECTを使用したクエリでPDO :: bindValue()が失敗する

ネストされたSELECTステートメントを持つクエリでPDO::bindValue()を使用すると、ネストされたSELECT内で値をバインドできません(プライマリSELECTでは問題ありません)。 これは失敗したサンプルコードの一部です:

$stmt = $cmdb->prepare("SELECT ci.CI FROM dbo.cmdb_ci AS ci " . 
         "INNER JOIN dbo.cmdb_model AS m ON m.ModelID = ci.Modelid " . 
         "INNER JOIN dbo.cmdb_class AS c ON c.ClassID = m.Classid " . 
         "WHERE (c.ClassID = :classid) " . 
         "AND (ci.CI IN (SELECT ci2.CI " . 
             "FROM dbo.cmdb_ci AS ci2 " . 
             "INNER JOIN dbo.cmdb_ci_status AS st2 ON st2.CI = ci2.CI " . 
             "WHERE st2.LocationID = :locationid))"); 
$stmt->bindValue("classid", 13); 
$stmt->bindValue("locationid", 1011); 
$stmt->execute(); 
if ($rows = $stmt->fetchAll()) 
    $stmt->closeCursor(); 
foreach ($rows as $row) 
    echo $row["CI"]; 

私が手にエラーがある:

SQLSTATE [22018]:キャスト仕様の無効な文字値:206 [Microsoft]の[SQL Serverネイティブクライアント11.0] [SQL Serverの]オペランドタイプ衝突:テキストはint型と互換性がありません(/builddir/build/BUILD/php-5.4.16/ext/pdo_odbc/odbc_stmt.c:254でのSQLExecute [206])

":locationid"の場合はbindValue()を省略し、 '1011'を直接クエリに挿入すると、呼び出しはエラーなしで正しい結果で完了します。

これはPDOのバグですか、あるいはbindValue()を別の方法で呼び出す必要がありますか?

+0

は、int型またはあなたのテキストになるように設定LocationIDされていますデータベース? – aynber

+1

'$ stmt-> bindValue(" locationid "、1011、PDO :: PARAM_INT);'エラーが示すように、intをテキストでキャストします。 PDOなしのbindValue:PARAM_は文字列になります。テキスト。 – JustOnUnderMillions

+0

LocationIDはintとして定義されています。 – Dandorid

答えて

4

...私はいくつかの理由(a bug in pdo_odbc ?)のためにということを想定して(私はコメントから読む)渡された値が整数であることbindValueを伝えることが...

$stmt->bindValue("locationid", 1011, PDO::PARAM_INT); 

を問題を解決しないとbindValueの3番目のパラメータとして指定しても、パラメータは文字列としてクエリに入力されます。

次に、値の整数をキャストしてというクエリに渡すことで、この問題を回避することを提案します。

"WHERE st2.LocationID = CAST(:locationid, int)))" 

これは非常にエレガントではありませんが、でも、これは動作しない場合は、PDO_ODBC


の修正/パッチを見つけるまでに適切であり得る:最後の行で

(もちろん、一時的な修正として意図されている)さらにエレガントでない解決策があります。

はあなたが書いた:「:locationid」

私はのためにbindValue()を省略した場合、クエリに直接「1011」を挿入し、呼び出しがエラーなしと正しい結果で完了します。

ロケーションIDを直接クエリに配置することができます。

"WHERE st2.LocationID = $locationId))"); 

これは予め消毒(または検証)されなければならない$locationId SQLインジェクションする傾向があるように:位置ID$locationIdに格納されているクエリの最後の行になると仮定

値は正の整数であるので、代わりにそれをエスケープする必要があり、私は簡単に、防弾アプローチを提案する:チェック$locationIdは数字だけで構成されています...

if(! ctype_digit((string) $locationId)) { 
    // location id is invalid 
    // do not proceed ! 
} 
1

ロケーションIDは実際にはMSSQLに文字列として格納され、intではないのでしょうか?引用符を追加すると機能しますか? $ stmt-> bindValue( "locationid"、 "1011"、PDO :: PARAM_STR);

+0

いいえ、それは動作しません。これは、LocationID値がINT(上記参照)として定義されているため無効なSQL文になるためです。 – Dandorid

関連する問題