BIGINTに変換できないデータを含むNVARCHAR列を持つテーブルがあります。私はそれをよく知っており、ISNUMERIC(BB.NVARCHARCOL) = 1
を使用して除外しています。これにもかかわらず、Error converting data type nvarchar to bigint.
を示すデータをクエリしようとすると、エラーが発生します
以下は正常に機能します(SQLによってエラーは報告されません)。
SELECT *
FROM myNormalTable AA INNER JOIN myBadTable BB ON BB.NVARCHARCOL = AA.MYBIGINTCOL
WHERE ISNUMERIC(BB.NVARCHARCOL) = 1
以下はエラーをスローします:
SELECT *
FROM (
SELECT *
FROM myNormalTable AA INNER JOIN myBadTable BB ON BB.NVARCHARCOL = AA.MYBIGINTCOL
WHERE ISNUMERIC(BB.NVARCHARCOL) = 1
) ZZ
WHERE ZZ.MYBIGINTCOL = 1234
このバリエーションもエラーをスローします。
SELECT *
FROM (
SELECT *
FROM myNormalTable AA INNER JOIN
(SELECT CAST(NVARCHARCOL AS BIGINT) NVARCHARCOL FROM myBadTable WHERE ISNUMERIC(NVARCHARCOL) = 1) BB
ON BB.NVARCHARCOL = AA.MYBIGINTCOL
) ZZ
WHERE ZZ.MYBIGINTCOL = 1234
これはエラーにならず、すべてのレコードが正常に返されることに注意してください...
SELECT CAST(NVARCHARCOL AS BIGINT) NVARCHARCOL FROM myBadTable WHERE ISNUMERIC(NVARCHARCOL) = 1
サブクエリでBIGINTをNVARCHARに変換するという解決策を見つけることができました。
SELECT *
FROM (
SELECT *
FROM myNormalTable AA INNER JOIN myBadTable BB ON BB.NVARCHARCOL = CAST(AA.MYBIGINTCOL AS NVARCHAR)
WHERE ISNUMERIC(BB.NVARCHARCOL) = 1
) ZZ
WHERE ZZ.MYBIGINTCOL = 1234
レコードを一時テーブルに挿入した場合:
SELECT CAST(NVARCHARCOL AS BIGINT) NVARCHARCOL INTO #TEMP FROM myBadTable WHERE ISNUMERIC(NVARCHARCOL) = 1
その一時テーブルをサブクエリで正常に使用できます。
SELECT *
FROM (
SELECT *
FROM myNormalTable AA INNER JOIN #TEMP BB ON BB.NVARCHARCOL = AA.MYBIGINTCOL
WHERE ISNUMERIC(BB.NVARCHARCOL) = 1
) ZZ
WHERE ZZ.MYBIGINTCOL = 1234
世界で何が起こっているのですか?SQLは、外部クエリを実行する前に、サブクエリを使用してより小さな結果セットを取得することを拒否しているようです。私が何をしても、テーブル全体を使用する必要があります。SQLServer 2012 Developerエディション
代わりにtry_cast()
を使用してください。
_select *
from (
select *
from myNormalTable AA
inner join #TEMP BB on try_cast(BB.NVARCHARCOL as bigint) = AA.MYBIGINTCOL
--where try_cast(BB.NVARCHARCOL as bigint) is not null /* not neccessary for inner join */
) ZZ
where ZZ.MYBIGINTCOL = 1234
_
SQL Server 2012以降:変換がエラーではなく失敗すると、これらのそれぞれがnull
を返します。
SqlZimはすでに 彼の答え のエラーを回避するための優れた方法を提供しています。ただし、質問とコメントでは、1つのクエリがエラーをスローし、他のクエリがエラーをスローしない理由について、興味を持っているように見えます。私はあなたの問題を再現することができます:
CREATE TABLE dbo.X_BIGINT_TABLE (ID BIGINT NOT NULL);
INSERT INTO dbo.X_BIGINT_TABLE WITH (TABLOCK)
SELECT TOP (1000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values;
CREATE TABLE dbo.X_NVARCHAR_TABLE (ID_NV NVARCHAR(10) NOT NULL);
INSERT INTO dbo.X_NVARCHAR_TABLE WITH (TABLOCK)
SELECT TOP (999) CAST(ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS NVARCHAR(10))
FROM master..spt_values
UNION ALL
SELECT 'ZOLTAN';
このクエリは正常に機能します。
SELECT *
FROM dbo.X_BIGINT_TABLE BI
INNER JOIN dbo.X_NVARCHAR_TABLE NV ON BI.ID = NV.ID_NV
WHERE ISNUMERIC(NV.ID_NV) = 1;
このクエリはエラーをスローします。
SELECT *
FROM (
SELECT *
FROM dbo.X_BIGINT_TABLE BI
INNER JOIN dbo.X_NVARCHAR_TABLE NV ON BI.ID = NV.ID_NV
WHERE ISNUMERIC(NV.ID_NV) = 1
) ZZ
WHERE ZZ.ID = 500;
メッセージ8114、レベル16、状態5、行25
データ型nvarcharをbigintに変換中にエラーが発生しました。
SQL Serverクエリオプティマイザーは、変更がクエリの最終結果に影響を与えない限り、十分な見積もりコストでクエリプランを見つけようとするクエリの要素を並べ替えることができます。概念を説明するために、2番目のクエリをリファクタリングできる1つの可能な方法を見ていきましょう。明確にするために、これはこの例でクエリオプティマイザーが実行する実際の段階的なプロセスではありません。クエリから始めます。
SELECT *
FROM (
SELECT *
FROM dbo.X_BIGINT_TABLE BI
INNER JOIN dbo.X_NVARCHAR_TABLE NV ON BI.ID = NV.ID_NV
WHERE ISNUMERIC(NV.ID_NV) = 1
) ZZ
WHERE ZZ.ID = 500;
述語を押し下げます。
SELECT *
FROM (
SELECT *
FROM dbo.X_BIGINT_TABLE BI
INNER JOIN dbo.X_NVARCHAR_TABLE NV ON BI.ID = NV.ID_NV
WHERE BI.ID = 500 AND ISNUMERIC(NV.ID_NV) = 1
) ZZ;
派生テーブルは不要になったので、削除してください。
SELECT *
FROM dbo.X_BIGINT_TABLE BI
INNER JOIN dbo.X_NVARCHAR_TABLE NV ON BI.ID = NV.ID_NV
WHERE BI.ID = 500 AND ISNUMERIC(NV.ID_NV) = 1
BI.ID = NV.ID_NV
がわかっているので、Z.ID
にもNV.ID_NV
にフィルターを適用できます。
SELECT *
FROM dbo.X_BIGINT_TABLE BI
INNER JOIN dbo.X_NVARCHAR_TABLE NV ON BI.ID = NV.ID_NV
WHERE BI.ID = 500 AND ISNUMERIC(NV.ID_NV) = 1 AND NV.ID_NV = 500
両方の結合列で単一の値にフィルタリングするため、結合をINNER JOIN
として実装する必要はなくなりました。 CROSS JOIN
に書き換えることができます。
SELECT *
FROM
(
SELECT *
FROM dbo.X_BIGINT_TABLE BI
WHERE BI.ID = 500
)
CROSS JOIN
(
SELECT *
FROM dbo.X_NVARCHAR_TABLE NV
WHERE ISNUMERIC(NV.ID_NV) = 1 AND NV.ID_NV = 500
);
2番目のクエリのクエリプランを見ると、最終結果が最終的に変換されたクエリに非常に類似していることがわかります。
参考のために、フィルター述語のテキストを次に示します。
CONVERT_IMPLICIT(bigint,[SE_DB].[dbo].[X_NVARCHAR_TABLE].[ID_NV] as [NV].[ID_NV],0)=(500)
AND isnumeric(CONVERT_IMPLICIT(varchar(20),[SE_DB].[dbo].[X_NVARCHAR_TABLE].[ID_NV] as [NV].[ID_NV],0))=(1)
SQL Serverが述語のCONVERT_IMPLICIT
部分をisnumeric
部分の前に評価すると、エラーが発生します。
一般的な規則として、SQLクエリを記述するときは、暗黙の操作順序に依存しないようにします。今日は正常に機能するクエリがあるかもしれませんが、データがテーブルに追加された場合、または別のクエリプランが選択された場合、エラーが発生し始めます。もちろん、例外があります(一種)。実際には、通常、CASE
ステートメントのさまざまな部分が記述された順序で評価されますが、それでも予期しない エラーが発生する可能性があります 。クエリの一部に余分なTOP
を追加して、特定の順序の操作を促すこともできます。次のクエリについて考えてみます。
SELECT *
FROM (
SELECT TOP (9223372036854775807) *
FROM dbo.X_BIGINT_TABLE BI
INNER JOIN dbo.X_NVARCHAR_TABLE NV ON BI.ID = NV.ID_NV
WHERE ISNUMERIC(NV.ID_NV) = 1
) ZZ
WHERE ZZ.ID = 500;
あなたと私はTOP
がクエリの結果を変更しないことを知っていますが、オプティマイザが9223372036854775807を超える行を派生テーブルが返さないという保証はないため、TOP
を評価する必要があります。技術的には、そのクエリで最初の9223372036854775807行を要求してから、ID
が500以外の行をフィルターで除外します。ID = 500
述語を派生テーブルにプッシュすると結果が変わるため、SQL Serverは実行しませんそれ。この例では、クエリはエラーなしで実行され、フィルタリングは最後に行われます。