0

私は、このトピックについて多くの質問があることを知っています。今、私は別の問題に遭遇しましたが、私自身も同僚も、奇妙な動作の理由が何であるかを知りません.

次のような比較的単純な SQL ステートメントがあります。

SELECT
    CONVERT(DATETIME, SUBSTRING(MyText, CHARINDEX('Date:', MyText) + 8, 16) AS MyDate,
    SomeOtherColumn,
    ...
FROM
    MyTable
INNER JOIN MyOtherTable
    ON MyTable.ID = MyOtherTable.MyTableID
WHERE
    MyTable.ID > SomeValue AND
    MyText LIKE 'Date: %'

これは私のデータベースではなく、私の SQL ステートメントでもありません。また、日時の値を varchar 列に格納する優れたスキーマを作成していないため、その部分は無視してください。

現在直面している問題は、SQL 変換エラー 241 (「文字列から日付や時刻を変換するときに変換に失敗しました。」) です。

これで、クエリ オプティマイザーが、変換の試行後に WHERE 句を使用して結果をフィルター処理する実行計画を変更する可能性があることがわかりましたが、本当に奇妙なことは、すべての WHERE 句を削除してもエラーが発生しないことです。 .

上記のステートメントに次のように 1 行追加しても、エラーは発生しません。

SELECT
    MyText, -- This is the added line
    CONVERT(DATETIME, SUBSTRING(MyText, CHARINDEX('Date:', MyText) + 8, 16) AS MyDate,
    ...

削除するとすぐに、変換エラーが再び発生します。MyText 列の値を変換せずに手動でチェックしても、問題を引き起こす可能性のあるレコードがあることは示されません。

変換エラーの原因は何ですか? SELECT ステートメントの一部として列を選択しても、なぜそれに遭遇しないのですか?

アップデート

ここに実行計画がありますが、役に立たないと思います。 パート1 ここに画像の説明を入力

4

1 に答える 1

1

場合によっては、SQL Server は、プロセスの早い段階で変換操作をプッシュすることにより、積極的に最適化することがあります。(すべきではありません。例として、 SQL Server はConnect で非論理的なエラーを発生させてはならないを参照してください)。

選択するだけの場合:

CONVERT(DATETIME, SUBSTRING(MyText, CHARINDEX('Date:', MyText) + 8, 16)

次に、オプティマイザは、この変換をテーブル/インデックス スキャンまたはシークの一部として実行できると判断します。これは、テーブルからデータを読み取っている時点で (重要なことに、WHERE句フィルタの前または同時に) )。クエリの残りの部分では、変換された値をそのまま使用できます。

選択時:

MyText, -- This is the added line
CONVERT(DATETIME, SUBSTRING(MyText, CHARINDEX('Date:', MyText) + 8, 16)

変換を後で行うことにします。重要なことに、現在の変換は (たまたま) WHERE、変換が試行される前にすべての行をフィルタリングする必要がある節フィルターよりも後で発生します。


これに対処する唯一の安全な方法は、変換が試行される前にフィルタリングが確実に行われるようにすることです。集計を扱っていない場合、CASE式は十分に安全かもしれません:

SELECT CASE WHEN MyText LIKE 'Date: %' THEN CONVERT(DATETIME, SUBSTRING(MyText, CHARINDEX('Date:', MyText) + 8, 16) END

それ以外の場合、より安全なオプションは、クエリを 2 つの個別のクエリに分割し、中間結果を一時テーブルまたはテーブル変数に格納することです (ビュー、CTE、およびサブクエリはカウントされません。オプティマイザーはそのような構成を「見抜く」ことができるためです)。

于 2013-03-05T11:04:34.657 に答える