7

varchar 列を含むテーブルがあり、特定の数値に一致する値を見つけたいと考えています。したがって、列に次のエントリが含まれているとしましょう (実際には数百万行を除く)。

123456789012
2345678
3456
23 45
713?2
00123456789012

そこで、数値的に 123456789012 であるすべての行について、次のようなステートメントを作成することにしました。

SELECT * FROM MyTable WHERE CAST(MyColumn as bigint) = 123456789012

最初と最後の行が返されるはずですが、「23 45」と「713?2」を bigint に変換できないため、クエリ全体が失敗します。

変換できない値に対して NULL を返す変換を行う別の方法はありますか?

4

6 に答える 6

8

SQL Server はブール演算子の短絡を保証しません。 SQL Server のブール演算子の短絡について を参照してください。したがって、使用するすべてのソリューションISNUMERIC(...) AND CAST(...)には根本的な欠陥があります (それらは機能する可能性がありますが、生成された計画によっては後で任意に失敗する可能性があります)。Thomas が提案するように、より良い解決策は CASE を使用することですCASE ISNUMERIC(...) WHEN 1 THEN CAST(...) ELSE NULL END。しかし、gbn が指摘したように、ISNUMERIC「数値」が何を意味するのかを特定するのが難しいことで有名であり、0 を返すと予想される多くの場合、1 を返します。

CASE WHEN MyRow NOT LIKE '%[^0-9]%' THEN CAST(MyRow as bigint) ELSE NULL END

しかし、実際の問題は、何百万もの行があり、このように検索する必要がある場合、式が SARG 対応ではないため (どのように書き直しても)、常にエンドツーエンドでスキャンすることになります。ここでの本当の問題はデータの純度であり、データが入力される適切なレベルで対処する必要があります。考慮すべきもう 1 つのことは、この式を使用して永続化された計算列を作成し、NULL (つまり、非数値) を排除するフィルター処理されたインデックスを作成できるかどうかです。それは物事を少しスピードアップするでしょう。

于 2010-11-04T19:02:54.767 に答える
4

ISNUMERIC は空の文字列と 1.23 や 5E-04 などの値を受け入れるため、信頼できない可能性があります。

また、評価される順序がわからないため、まだ失敗する可能性があります (SQL は宣言型であり、手続き型ではないため、WHERE 句はおそらく左から右に評価されません)。

そう:

  • 0〜9の文字のみで構成される値を受け入れたい
  • CASTの前に適用されるように、「数値」フィルターを具体化する必要があります

何かのようなもの:

SELECT 
*
FROM
   (
   SELECT TOP 2000000000 *
   FROM MyTable
   WHERE MyColumn NOT LIKE '%[^0-9]%'  --double negative rejects anything except 0-9
   ORDER BY MyColumn 
   ) foo
WHERE 
    CAST(MyColumn as bigint) = 123456789012 --applied after number check

編集:失敗する簡単な例。

CREATE TABLE #foo (bigintstring varchar(100))
INSERT #foo (bigintstring )VALUES ('1.23')
INSERT #foo (bigintstring )VALUES ('1 23')
INSERT #foo (bigintstring )VALUES ('123')

SELECT * FROM #foo
WHERE
   ISNUMERIC(bigintstring) = 1 
   AND 
   CAST(bigintstring AS bigint) = 123
于 2010-11-04T18:49:57.200 に答える
1
SELECT * 
    FROM MyTable 
    WHERE ISNUMERIC(MyRow) = 1
        AND CAST(MyRow as float) = 123456789012
于 2010-11-04T18:42:20.503 に答える
0

ISNUMERIC()関数は、必要なものを提供するはずです。

SELECT * FROM MyTable
WHERE ISNUMERIC(MyRow) = 1
AND CAST(MyRow as bigint) = 123456789012

そして、Thomasが提案したようなcaseステートメントを追加するには:

SELECT * FROM MyTable
WHERE CASE(ISNUMERIC(MyRow)
        WHEN 1 THEN CAST(MyRow as bigint)
        ELSE NULL
      END = 123456789012

http://msdn.microsoft.com/en-us/library/ms186272.aspx

于 2010-11-04T18:40:28.303 に答える
0
SELECT *
FROM MyTable
WHERE (ISNUMERIC(MyColumn) = 1) AND (CAST(MyColumn as bigint) = 123456789012)

さらに、ヌル値を取得するために CASE ステートメントを使用できます。

SELECT 
    CASE
        WHEN (ISNUMERIC(MyColumn) = 1) THEN CAST(MyColumn as bigint)
        ELSE NULL
    END AS 'MyColumnAsBigInt'
FROM tableName

追加のフィルタリングが必要な場合は、bigint へのキャストが有効でない数値に対して、ISNUMERIC の代わりに次を使用できます。

PATINDEX('%[^0-9]%',MyColumn)) = 0

整数ではなく小数値が必要な場合は、代わりに float にキャストし、正規表現を '%[^0-9.]%' に変更します。

于 2010-11-04T18:40:55.137 に答える