web-dev-qa-db-ja.com

CTEエラー(nvarcharから数値)

ビジネスオブジェクトで一時テーブルを使用できないようですが、CRSを使用してSSRSストアドプロシージャをBOストアドプロシージャに変換しています。

私はこのクエリを持っています:

;WITH cte1
AS
( 
    SELECT  cv.issue  
          , cv.customfield  
          , CAST(STRINGVALUE AS NUMERIC) AS priority_num  
    --INTO    #temp_priority_val  
    FROM    proddb1.customfieldvalue cv WITH (NOLOCK)  
            INNER JOIN proddb1.customfield e WITH (NOLOCK)
                 ON  cv.CUSTOMFIELD = e.id 
                 AND e.cfname = 'Issue Priority')  

,cte2
AS
( 
    SELECT  a.ISSUE  
          , f.customvalue priority_num  
    --INTO    #temp_priority  
    FROM    cte1 a 
            INNER JOIN proddb1.customfieldoption f WITH (NOLOCK)
                 ON  a.CUSTOMFIELD = f.CUSTOMFIELD        
                 AND CAST(a.priority_num AS NUMERIC) = f.id) 

SELECT * FROM cte2 

Error converting data type nvarchar to numeric.この実行でエラーが発生しました。 priority_num列は、最初のCTEのCASTであるnvarchar列です。一時テーブルを持つ元のステートメント内でこれを実行すると、完全に正常に動作します。それはCTEスコープのものでなければなりませんか?

5
Andy

SQL Serverが正しいデータ型を使用しない(または精度が一致しない)列の内容を評価する順序を簡単に制御することはできません。 nvarchar型の列を数値にキャストしようとすると、数値以外のすべての値を考慮から除外する必要があるフィルターがある場合でも、SQL Serverはそれらを最初に試すことができます(UserVoiceでの Erlandのこれに関する不満を参照 =)。牛が家に帰るまでCTEをネストできる場合もありますが、SQL Serverはその評価を予想外の場所にプッシュまたはプルします。

集計関数とフルテキスト関数が含まれていない限り( here および here を参照)、CASE式を使用して、変換前にこの評価を強制できます。これは、CTEを使用しないクエリの簡易バージョンです。

SELECT cfv.issue,
  priority_num = CONVERT(NUMERIC(something, something), 
    CASE WHEN ISNUMERIC(cfo.customvalue)=1 THEN cfo.customvalue END)
FROM
  proddb1.customfieldvalue AS cfv
  INNER JOIN proddb1.customfield AS cf
  ON cfv.CUSTOMFIELD = cf.id
  INNER JOIN proddb1.customfieldoption AS cfo
  ON cfv.CUSTOMFIELD = cfo.CUSTOMFIELD
  AND CONVERT(NUMERIC(something, something), 
    CASE WHEN ISNUMERIC(cfo.customvalue)=1 THEN cfo.customvalue END) = cfo.id
  WHERE cf.cfname = 'Issue Priority';

変更する必要がありますsomething, somethingを適切な精度/スケールに設定します(または、小数点以下の桁数が必要ない場合は、INT型のいずれかを使用します)。長さを指定せずにさまざまな型を宣言しないでください- このブログ投稿はvarcharについてですが、実際にはすべての型に適用されます

SQL Server 2012を使用している場合(使用しているSQL Serverのバージョンなどの情報を含めると常に便利です)、これを簡略化できます。

CONVERT(NUMERIC(something, something), 
    CASE WHEN ISNUMERIC(cfo.customvalue)=1 THEN cfo.customvalue END)

これに:

TRY_CONVERT(NUMERIC(something, something), cfo.customvalue)

そして、実際にはより信頼性が高くなります(ISNUMERICは1を返し、特定の型の変換時に失敗する可能性があるため)。 私は2002年にこの方法についてブログを書いた

11
Aaron Bertrand