2

PHP内でSybaseSQL-Anywhereデータベースを対象に動的に生成されたSQLクエリを実行しようとすると、次のエラーが発生しました。

Warning: sybase_query(): message: SQL Anywhere Error -680: Invalid expression in WHERE clause of Transact-SQL outer join (severity 16) in /path/to/file.php

SQLクエリ文字列:

SELECT DISTINCT v_InventoryMaster.INV_ScanCode, v_InventoryMaster.INV_ReceiptAlias 
FROM ecrs.v_InventoryMaster
WHERE ( v_InventoryMaster.INV_PK NOT IN (
    SELECT DISTINCT v_InventoryMaster.INV_PK 
    FROM ecrs.v_InventoryMaster, ecrs.StockInventoryLinkDiscounts
    WHERE v_InventoryMaster.INV_PK = StockInventoryLinkDiscounts.ILD_INV_FK
    AND StockInventoryLinkDiscounts.ILD_DIS_FK = 6 
  )
  OR v_InventoryMaster.INV_PK NOT IN (
    SELECT DISTINCT v_InventoryMaster.INV_PK 
    FROM ecrs.v_InventoryMaster, ecrs.StockInventoryLinkDiscounts
    WHERE v_InventoryMaster.INV_PK = StockInventoryLinkDiscounts.ILD_INV_FK
    AND StockInventoryLinkDiscounts.ILD_DIS_FK = 14 
  )
  OR v_InventoryMaster.INV_PK NOT IN (
    SELECT DISTINCT v_InventoryMaster.INV_PK 
    FROM ecrs.v_InventoryMaster, ecrs.StockInventoryLinkDiscounts
    WHERE v_InventoryMaster.INV_PK = StockInventoryLinkDiscounts.ILD_INV_FK
    AND StockInventoryLinkDiscounts.ILD_DIS_FK = 25 
  )
  -- more OR clause subqueries with different ILD_DIS_FK values
)
ORDER BY v_InventoryMaster.INV_ScanCode

私はSQLやデータベースとのインターフェースに完全に慣れていないわけではありませんが、このメッセージは私を困惑させます。句に無効な式があると主張していWHEREますが、クエリがどのように不正に構成されているのかわかりません。私の推測では、エラーにはOR2つの検索結果のとの結合が含まれます。

さらに、これら3つの個別のクエリを実行し、結果を(Excelで)結合することにより、正しい結果のセットを返します。

クエリA:

SELECT DISTINCT v_InventoryMaster.INV_ScanCode, v_InventoryMaster.INV_ReceiptAlias 
FROM ecrs.v_InventoryMaster
WHERE ( v_InventoryMaster.INV_PK NOT IN (
    SELECT DISTINCT v_InventoryMaster.INV_PK 
    FROM ecrs.v_InventoryMaster, ecrs.StockInventoryLinkDiscounts
    WHERE v_InventoryMaster.INV_PK = StockInventoryLinkDiscounts.ILD_INV_FK
    AND StockInventoryLinkDiscounts.ILD_DIS_FK = 6 
  )
ORDER BY v_InventoryMaster.INV_ScanCode

クエリB

SELECT DISTINCT v_InventoryMaster.INV_ScanCode, v_InventoryMaster.INV_ReceiptAlias 
FROM ecrs.v_InventoryMaster
WHERE ( v_InventoryMaster.INV_PK NOT IN (
    SELECT DISTINCT v_InventoryMaster.INV_PK 
    FROM ecrs.v_InventoryMaster, ecrs.StockInventoryLinkDiscounts
    WHERE v_InventoryMaster.INV_PK = StockInventoryLinkDiscounts.ILD_INV_FK
    AND StockInventoryLinkDiscounts.ILD_DIS_FK = 14 
  )
ORDER BY v_InventoryMaster.INV_ScanCode

クエリC

SELECT DISTINCT v_InventoryMaster.INV_ScanCode, v_InventoryMaster.INV_ReceiptAlias 
FROM ecrs.v_InventoryMaster
WHERE ( v_InventoryMaster.INV_PK NOT IN (
    SELECT DISTINCT v_InventoryMaster.INV_PK 
    FROM ecrs.v_InventoryMaster, ecrs.StockInventoryLinkDiscounts
    WHERE v_InventoryMaster.INV_PK = StockInventoryLinkDiscounts.ILD_INV_FK
    AND StockInventoryLinkDiscounts.ILD_DIS_FK = 25 
  )
ORDER BY v_InventoryMaster.INV_ScanCode

私が望む結果を明確にするために:

ここに画像の説明を入力してください

に関するSybaseのドキュメントerror -680は、次のように記載されています。

Transact-SQL構文を使用するクエリのWHERE句の式には、NULLを提供するテーブルの列と、別のテーブルの列を参照するサブクエリまたは式との比較が含まれています。

  1. 元のSQLクエリの何が無効ですか?

  2. 文書化された説明はどういう意味ですか?

  3. 元のSQLクエリを編集して目的の結果を得るにはどうすればよいですか?

このクエリは動的に生成されたため、OR句間のステートメントを変更する方法を知りたいことに注意してください。

ステートメント構造:

  v_InventoryMaster.INV_PK NOT IN (
    SELECT DISTINCT v_InventoryMaster.INV_PK 
    FROM ecrs.v_InventoryMaster, ecrs.StockInventoryLinkDiscounts
    WHERE v_InventoryMaster.INV_PK = StockInventoryLinkDiscounts.ILD_INV_FK
    AND StockInventoryLinkDiscounts.ILD_DIS_FK = value -- value dynamically chosen by user
  )
4

2 に答える 2

2

「NOTIN」クエリは、特に例のように3回以上適用する場合、非常にコストがかかります。これは、「NOTIN」クエリを使用する場合とは少し異なる方法でスピンします。私は左結合を行い、NULLである(つまり、見つからない)エントリを探しますが、基準を持つエントリの少なくとも1つを持っています...

SELECT DISTINCT 
      vIM.INV_ScanCode, 
      vIM.INV_ReceiptAlias 
   FROM
      ecrs.v_InventoryMaster vIM
         LEFT JOIN ecrs.StockInventoryLinkDiscounts SILD6
            ON vIM.INV_PK = SILD6.ILD_INV_FK
            AND SILD6.ILD_DIS_FK = 6 
         LEFT JOIN ecrs.StockInventoryLinkDiscounts SILD14
            ON vIM.INV_PK = SILD14.ILD_INV_FK
            AND SILD14.ILD_DIS_FK = 14 
         LEFT JOIN ecrs.StockInventoryLinkDiscounts SILD25
            ON vIM.INV_PK = SILD25.ILD_INV_FK
            AND SILD25.ILD_DIS_FK = 25 
   WHERE
          (    SILD6.ILD_INV_FK IS NULL
            OR SILD14.ILD_INV_FK IS NULL
            OR SILD25.ILD_INV_FK IS NULL )
      AND (  case when SILD6.ILD_INV_FK IS NULL THEN 0 ELSE 1 end
           + case when SILD14.ILD_INV_FK IS NULL THEN 0 ELSE 1 end
           + case when SILD25.ILD_INV_FK IS NULL THEN 0 ELSE 1 end ) > 0
   ORDER BY 
      vIM.INV_ScanCode

それぞれの基準はPKエントリに基づくNOTINであったため、在庫マスターを1回だけ使用しました。次に、その外部キーの一致と対応する「DIS_FK」ID(それぞれ6、14、25)の在庫リンク割引(SILDエイリアス)にLEFT-JOINを実行しました。

それで、今、特定の在庫アイテムに対して(SILD)レベルで10の割引があり、ID 1、6、10、11、22、25、何とか、何とかを含むとしましょう...このテーブルは一致するものを見つけるために結合します6と25の両方を同時に(異なるエイリアスを介して)、14の1つを見つけられません。シナリオから、このエントリが必要になります。

これにより、WHERE句が表示されます。この1つの在庫アイテムについて、エントリの少なくとも1つがNULL(つまり、割引14の場合)であり、少なくとも1つのアイテムDID(つまり、6と25)が存在することを確認したいと思います。

ここで、在庫マスターに1、5、12の割引がある場合、6、14、または25のどれもWHERE句の残りを考慮せずに検出されなかったため、無視されます。

必要な数の(SILD)インスタンスを追加し続け、(私が持っているように)別のエイリアスを使用してパターンを継続し、参照しているインスタンスを知ることができます。

WHERE句は、さらに単純化して次のようにすることができます。

   WHERE
      (  case when SILD6.ILD_INV_FK IS NULL THEN 0 ELSE 1 end
           + case when SILD14.ILD_INV_FK IS NULL THEN 0 ELSE 1 end
           + case when SILD25.ILD_INV_FK IS NULL THEN 0 ELSE 1 end ) between 1 and 2

したがって、このようにして、条件が一致する条件は少なくとも1つになりますが、ほとんどの場合、すべての基準より1つ少なくなります。この例では、3つの基準があるため、1つまたは2つが有効で、0が失敗し、3つが失敗します...

6つの基準がある場合、1から5の間...

于 2013-01-13T17:22:17.967 に答える
1

フラグメントを簡単に置き換えることができます。

v_InventoryMaster.INV_PK NOT IN (
    SELECT DISTINCT v_InventoryMaster.INV_PK 
    FROM ecrs.v_InventoryMaster, ecrs.StockInventoryLinkDiscounts
    WHERE v_InventoryMaster.INV_PK = StockInventoryLinkDiscounts.ILD_INV_FK
    AND StockInventoryLinkDiscounts.ILD_DIS_FK = value -- value dynamically chosen by user
  )

に:

 NOT EXISTS (
    SELECT * 
    FROM  ecrs.StockInventoryLinkDiscounts sild
    WHERE sild.ILD_INV_FK = tbl.INV_PK
    AND sild.ILD_DIS_FK = value -- value dynamically chosen by user
    --           ^^^^^ NOTE: this should probably be sild.ILD_DIS_PK
  )

tbl外部クエリの相関名であるため、外部クエリは次のようになります。

SELECT DISTINCT v_InventoryMaster.INV_ScanCode
      , v_InventoryMaster.INV_ReceiptAlias 
FROM ecrs.v_InventoryMaster tbl

また、テーブルは外部クエリにすでに存在し、外部クエリがすでに検出したのとまったく同じ行がチェックされるためecrs.v_InventoryMaster、サブクエリからテーブルを削除したことにも注意してください。

これにより、完全なクエリが次のようになります。

SELECT DISTINCT v_InventoryMaster.INV_ScanCode
      , v_InventoryMaster.INV_ReceiptAlias 
FROM ecrs.v_InventoryMaster tbl
WHERE NOT EXISTS (
    SELECT * 
    FROM  ecrs.StockInventoryLinkDiscounts sild
    WHERE sild.ILD_INV_FK = tbl.INV_PK
    AND sild.ILD_DIS_FK = 6
  )
OR NOT EXISTS (
    SELECT * 
    FROM  ecrs.StockInventoryLinkDiscounts sild
    WHERE sild.ILD_INV_FK = tbl.INV_PK
    AND sild.ILD_DIS_FK = 14
  )
OR NOT EXISTS (
    SELECT * 
    FROM  ecrs.StockInventoryLinkDiscounts sild
    WHERE sild.ILD_INV_FK = tbl.INV_PK
    AND sild.ILD_DIS_FK = 25
  )
;

私の推測では、パーサーは、へのエイリアスされていない参照によって混乱していecrs.v_InventoryMasterます。もう1つの可能性は、範囲テーブルがいっぱいであるということです(サブクエリ用語がたくさんある場合)

于 2013-01-13T17:06:53.187 に答える