2011-08-31 8 views
9

一部のSQLサーバーのクエリに問題があります。私は、 "Attibute_Name"と "Attibute_Value"フィールド(varcharに格納されている任意のタイプ)のテーブルを持っていることがわかります。 (ええと...知っている)datetimeへの変換はWHERE句でのみ失敗しますか?

特定の属性のすべての日付は、「YYYY-MM-DD hh:mm:ss」形式で格納されているようです(100%ここでは、レコード)のので、私は問題もなく、このコードを実行することができます

select /*...*/ CONVERT(DATETIME, pa.Attribute_Value) 
from 
    ProductAttributes pa 
    inner join Attributes a on a.Attribute_ID = pa.Attribute_ID 
where 
    a.Attribute_Name = 'SomeDate' 

をしかし、私は次のコードを実行した場合:

select /*...*/ CONVERT(DATETIME, pa.Attribute_Value) 
from 
    ProductAttributes pa 
    inner join Attributes a on a.Attribute_ID = pa.Attribute_ID 
where 
    a.Attribute_Name = 'SomeDate' 
    and CONVERT(DATETIME, pa.Attribute_Value) < GETDATE() 

を私は次のエラーが発生します。変換するとき 、変換に失敗しました文字列からの日付および/または時刻。

どのようにしてwhere句で失敗し、選択したもので失敗しますか?私はデータベース(PK)に格納されている実際のATTRIBUTE_IDそれは問題なく動作しますを使用することによりATTRIBUTE_NAME

場合は代わりのフィルタリング:

もう一つの手がかり。答えを

select /*...*/ CONVERT(DATETIME, pa.Attribute_Value) 
from 
    ProductAttributes pa 
    inner join Attributes a on a.Attribute_ID = pa.Attribute_ID 
where 
    a.Attribute_ID = 15 
    and CONVERT(DATETIME, pa.Attribute_Value) < GETDATE() 

更新 みんなありがとう。誰もが問題を理解するのに役立つ何かを指摘したので、正解を実際に選ぶのは難しいと感じました。それは間違いなく実行の順序と関係がありました。 WHERE句が最初に実行され、次にSELECTが実行されたため、最初のクエリが正しく機能していました。 同じ理由で2番目のクエリが失敗しました(属性がフィルタされなかったため、同じWHERE句を実行している間に変換に失敗しました)。 IDがインデックス(PK)の一部であるため、3番目のクエリが機能したため、優先順位が高くなり、最初にその条件の結果がドリルダウンされました。

ありがとうございます!

+0

'sOmeDaTe'と呼ばれる別の属性はありませんしながら、

そのノートオン

、EAVを取り除くことをお勧めでしょうか?おそらくそれはあなたの結合を台無しにするでしょう。 – YetAnotherUser

+1

'WHERE'節で述語の短絡評価や保証された順序付けを仮定しているようです。これは保証されていません。そのようなカラムにデータ型を混在させた場合、それらを扱う唯一の安全な方法は 'CASE'式です。 –

+0

PHAとは何ですか? PHAがPAとは異なるテーブルである場合、PHAのデータには変換不可能なレコードが存在するように見えますが、PAの場合と異なります。 – N0Alias

答えて

2

変換がWHERE句にある場合は、投影に表示されるよりも多くのレコード(値)が評価される可能性がありますリスト。これについては、別の文脈で話しました。T-SQL functions do no imply a certain order of executionOn SQL Server boolean operator short-circuitを参照してください。あなたのケースはもっと簡単ですが似ていますし、最終的に根本的な原因は同じです。SQLのような宣言言語を扱う際には、命令実行命令を想定しないでください。

最も良い解決策は、データをサニタイズし、列の種類をDATETIMEまたはDATETIME2型に変更することです。 すべて他の回避策には1つの欠点または別の回避策があるため、正しいことを行う方がよい場合があります。

更新

詳しく見て(申し訳ありませんが、私は@VLDBだと唯一のセッション間でSO覗い)後、私はあなたが本来の形のないセマンティクス(attribute_value缶BEA文字列、とEAVストアを持って実現日付、intなど)。私の意見では、あなたの最善の策は、クライアントにまでsql_variantを格納し、すべての方法で使用することです(つまり、プロジェクトsql_variant)。あなたはクライアントで型を継承することができます。すべてのクライアントAPIはsql_variantから内部型を抽出するメソッドを持っています。Using sql_variant Data(ほとんどすべてのクライアントAPI ... Using the sql_variant datatype in CLR)を参照してください。 sql_variantを使用すると、文字列表現の問題が発生しない複数のタイプを格納できます。SQL_VARIANT_PROPERTYを使用すると、格納された値のBaseTypeなどを検査できます。チェック制約のように考えると、データ型の正確性が保証されます。

+0

クライアントですべてのプレゼンテーション、フィルタリング、および比較を行っていない限り、私は 'SQL_VARIANT' *を使用することを非常に躊躇します。私たちのEAVシステムでは、それぞれの型の専用の列を優先して、すぐに「SQL_VARIANT」から離れました。さて、各行に2つのNULLがありますが、それに付随する他のすべての不快なものに対処する必要はありません。両側で公正な振る舞いをするために、私はここで制限事項について少しブログしました:http://sqlblog.com/blogs/aaron_bertrand/archive/2009/10/12/bad-habits-to-kick-using-the- wrong-data-type.aspx ...カラムが 'SQL_VARIANT'だった場合はクエリを表示できますか? –

+0

私はあなたのポイントを見ます。専用の列/型は、その型のフィールドにすべて入っていることがわかっており、型にCASTは必要ないため、sql_variant EAV構造体を実行して集約すると、キャストの問題が発生する可能性があります。有効な異論。 –

0

私にはデータの問題のようです。異なる2つの方法を使用してデータを選択したときにデータを見て、別個の長さを探してから、異なるセットのアイテムを選択してそれらを目で確認してみてください。ヌルもチェックしますか?

+0

nullをdatetimeに変換するとNULLになります。 –

1

これは、SELECTクエリが処理される順序と関係があります。 WHERE句は、SELECTよりずっと前に処理されます。包含/除外する行を決定する必要があります。名前を使用する句では、すべての行を調べるスキャンを使用する必要がありますが、その一部は有効な日付/時刻データを含んでいませんが、キーはシークにつながる可能性があります。 SELECTリストの変換は最後に実行され、今度は無効な行を変換しようとしていません。日付/時刻データを他のデータと混合するので、日付または数値データを専用の列に正しいデータ型で格納することを検討することができます。一方で、あなたは以下の方法でチェックを延期することができます

SELECT /* ... */ 
FROM 
(
    SELECT /* ... */ 
    FROM ProductAttributes AS pa 
    INNER JOIN dbo.Attributes AS a 
    ON a.Attribute_ID = pa.Attribute_ID 
    WHERE a.Attribute_Name = 'SomeDate' 
    AND ISDATE (pa.Attribute_Value) = 1 
) AS z 
WHERE CONVERT(CHAR(8), AttributeValue, 112) < CONVERT(CHAR(8), GETDATE(), 112); 

しかし、より良い答えが可能な場合は、名前の代わりにAttribute_IDキーを使用することが考えられます。

+1

これは動作するとは限りません。 'SELECT'リスト内の計算スカラーは' WHERE'フィルタの前に評価することができます。たとえば、[この回答](http://stackoverflow.com/questions/5191701/tsql-divide-by-zero-encountered-despite-no-columns-containing-0/5203211#5203211)または[this connect item] (http://connect.microsoft。com/SQLServer/feedback/details/537419/sql-server-should-not-illogical-errors) –

+0

いいえ、これは動作しません。あなたは、宣言(サブクエリ)の順序がhttp://rusanu.com/2011/08/10/t-sql-functions-do-no-imply-certain-のように評価の順序を意味すると仮定しています。実行順序/ QOは、属性名の比較の前にCONVERT *を評価し、変換エラーを引き起こすプランを選択できます。 –

+0

いいえ、「延期しようとする可能性がある」と言わざるを得ないでしょう。より良い答えは、すべてをvarchar列に入れるのではなく、正しいデータ型の専用列にデータを格納することです。 –

7

WHERE節で、ある種の短絡評価または述語の保証された順序付けを想定しているようです。これは保証されていません。そのような列にデータ型を混在させると、それらを扱う唯一の安全な方法はCASEという式になります。

使用(例えば)

CONVERT(DATETIME, 
     CASE WHEN ISDATE(pa.Attribute_Value) = 1 THEN pa.Attribute_Value END) 

ない

CONVERT(DATETIME, pa.Attribute_Value) 
0

私はこの問題は、あなたが(明らかに)データベース内の悪い日があると思います。

WHERE句の日付をチェックしていない最初の例では、a.attribute.Name = 'SomeDate'の日付はすべて有効なので、悪い日付の変換を試みません。

2番目の例では、WHERE句に追加することで、実際にはすべての日付が変換され、悪いものを見つけて属性名が検索されます。

第3の例では、Attribute_Idを使用するように変更すると、おそらくid = 15のものだけを検索するようにクエリ計画が変更され、それらのレコードが有効な日付を持つかどうかが確認されます。だから、

(おそらくAttribute_IdがインデックスとAttribute_nameではないです)、あなたはどこか悪い日があり、それはあなたが実行計画を確認することができます= 15

0

Arttribute_idを持つすべてのレコードではありません。最初のクエリでは、第2の基準(CONVERT(DATETIME, pa.Attribute_Value) < GETDATE())が無効なデータ(日付ではない)を含むすべての行について最初に評価され、第2の基準の場合にはa.Attribute_ID = 15が最初に評価されることがあります。したがって、日付以外の値を持つ行は除外されます。

btwの場合、2番目の方が高速かもしれません。選択リストにAttributesの項目がない場合は、inner join Attributes a on a.Attribute_ID = pa.Attribute_IDを取り除くことができます。あなたは大文字小文字を区別しない照合を使用している場合 - それは遅すぎる:)

+0

テーブルの統計情報を再計算することができます。 'ProductAttributes'に何百万もの行が含まれていると、' CONVERT(DATETIME、pa.Attribute_Value) nad2000

+0

「ANALYZE TABLE」はSQL Serverで正しく発音しません。 –

関連する問題