SQL無効な変換は、エラーをスローする代わりにnullを返します
-
29-09-2019 - |
質問
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を返す変換を行う別の方法はありますか?
解決
SQL Serverはブールのオペレーターの短絡を保証しません。 SQL Server Boolean Operatorショートサーキット. 。したがって、すべてのソリューションを使用します ISNUMERIC(...) AND CAST(...)
根本的に欠陥があります(彼らは機能するかもしれませんが、生成された計画に後で依存を任意に失敗させることができます)。トーマスが示唆するように、より良い解決策はケースを使用することです。 CASE ISNUMERIC(...) WHEN 1 THEN CAST(...) ELSE NULL END
. 。しかし、GBNが指摘したように、 ISNUMERIC
「数値」が何を意味するのかを特定することで、そしてそれが戻ると予想される多くの場合、1を返すことができます。
CASE WHEN MyRow NOT LIKE '%[^0-9]%' THEN CAST(MyRow as bigint) ELSE NULL END
しかし、本当の問題は、何百万もの行があり、このようにそれらを検索する必要がある場合、表現は皮肉ではないため、常にエンドツーエンドのスキャンになります(どのように書き直しても)。ここでの実際の問題はデータ純度であり、データが入力される適切なレベルで対処する必要があります。考慮すべきもう1つのことは、この式で永続化された計算列を作成し、nullを排除するフィルタリングされたインデックスを作成することができるかどうかです(つまり、非数値)。それは物事を少しスピードアップするでしょう。
他のヒント
SQL Server 2012を使用している場合は、2つの新しい方法を使用できます。
どちらの方法も同等です。キャストが成功した場合、指定されたデータ型に値キャストを返します。それ以外の場合は、nullを返します。唯一の違いは、変換がSQL Server固有であることです。CASTはANSIです。キャストを使用すると、コードがよりポータブルになります(他のデータベースプロバイダーがtry_castを実装するかどうかはわかりません)
iSnumericは、1.23や5E-04などの空の文字列と値を受け入れるため、信頼できない可能性があります。
そして、あなたはそれがまだ失敗する可能性があるため、どのような順序が評価されるかわからない(SQLは手続き型ではなく宣言的であるため、おそらく句はおそらく左から右に評価されない)
それで:
- あなたは構成されている価値を受け入れたいです それだけ キャラクターの0-9
- キャスト前に適用されるように「番号」フィルターを具体化する必要があります
何かのようなもの:
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
SELECT *
FROM MyTable
WHERE ISNUMERIC(MyRow) = 1
AND CAST(MyRow as float) = 123456789012
is -numeric()関数は、必要なものを提供する必要があります。
SELECT * FROM MyTable
WHERE ISNUMERIC(MyRow) = 1
AND CAST(MyRow as bigint) = 123456789012
そして、トーマスが示唆したようなケースステートメントを追加するには:
SELECT * FROM MyTable
WHERE CASE(ISNUMERIC(MyRow)
WHEN 1 THEN CAST(MyRow as bigint)
ELSE NULL
END = 123456789012
SELECT *
FROM MyTable
WHERE (ISNUMERIC(MyColumn) = 1) AND (CAST(MyColumn as bigint) = 123456789012)
さらに、null値を取得するためにケースステートメントを使用できます。
SELECT
CASE
WHEN (ISNUMERIC(MyColumn) = 1) THEN CAST(MyColumn as bigint)
ELSE NULL
END AS 'MyColumnAsBigInt'
FROM tableName
追加のフィルタリングが必要な場合は、bigintにキャストするのが有効でない数字の場合、is -numericの代わりに以下を使用できます。
patindex( '%[^0-9]%'、mycolumn)= 0
整数の代わりに小数値が必要な場合は、代わりにフロートして再gexを '%[^0-9。]%に変更するためにキャストします。