なぜCASEステートメントを使用して列が存在するかどうかを確認し、その列からSELECTできないのですか


17

なぜこのようなものが機能しないのですか?

SELECT
CASE 
WHEN NULLIF(COL_LENGTH('Customers', 'Somecol'), '') IS NULL THEN NULL
ELSE Somecol
END AS MyTest
FROM Customers;

列が存在するかどうかを確認しているだけですが、SQL Server Somecolは存在しないことについて不平を言っています。単一のステートメントでこれに代わるものはありますか?


3
これを行う理由の例はありますか?存在しない可能性のある列から選択しようとするクエリを作成する理由を理解できません。
マークシンキンソン

4
SQL Serverは、ステートメント構文が正しいと評価してから実行します。したがって、CASEステートメント内でラップされている場合でも、参照されるすべての列はテーブルに存在する必要があります。
マークシンキンソン

@MarkSinkinson:構文の後に名前がチェックさますが、はい、SQL Serverは実際にバッチを実行する前にそれを行います。
アンドリーM 14

1
INFORMATION_SCHEMA回避策として選択できます。
ブリリアンド14

回答:


43

次のクエリではと同じ考えを使用して、この驚くべき答えによってypercube

SELECT x.*
FROM (SELECT NULL AS SomeCol) AS dummy
CROSS APPLY
(
  SELECT
    ID,
    SomeCol AS MyTest
  FROM dbo.Customers
) AS x;

それはこのように動作します:

  • dbo.Customersという名前の列がある場合SomeColSomeColin SomeCol AS MyTestは次のように解決されdbo.Customers.SomeColます。

  • テーブルにはそのような列を持っていない場合、参照は依然として、今、それが解決されますので、有効になりますdummy.SomeColdummy列はその文脈の中で参照することができます。

そのように複数の「スペア」列を指定できます。トリックは、そのような列にテーブルエイリアスを使用しないことです(ほとんどの場合、これは眉をひそめる習慣ですが、この場合、テーブルエイリアスを省略すると問題を解決できます)。

テーブルが結合で使用され、他のテーブルに独自のテーブルがあるSomeCol場合、おそらく、次のようなトリックを機能させるために、結合で使用する前に上記のクエリを派生テーブルとして使用する必要があります。

SELECT ...
FROM
(
  SELECT x.*
  FROM (SELECT NULL AS SomeCol) AS dummy
  CROSS APPLY (
    SELECT
      ID,
      SomeCol AS MyTest
    FROM dbo.Customers
  ) AS x
) AS cust
INNER JOIN ...
;

1
SQLコンパイラはほんの少し複雑なのでしょうか。あなたができることを超クール。
マックスヴァーノン14

9

これを行う1つの方法は、列の存在を確認し、その列が存在するかどうかに基づいて動的SQLを構築することです。

動的SQLがない場合、SQL Serverは、ステートメントが実行される前に列が存在するかどうかを評価しようとし、エラーが発生します。

ただし、2つのクエリを記述し、将来変更する可能性があることを意味します。ただし、SELECT存在しない可能性のある列のステートメントを実際にターゲットにする必要があるとは思わない。

declare @SQL varchar(max)

If exists (select 1 from sys.columns where Name = N'NameOfColumn' and object_id=object_id(N'yourTableName'))
begin
set @SQL = 'select ID, NameOfColumn from yourTableName'
exec(@sql)
end
else
begin
Print 'Column does not exist'
end

はい、しかし、理にかなっていますが、単一のステートメントである必要があります。最終的には、おそらく存在しない魔法のシステム機能を探しています。
カーソンラインケ14

4

一部のXMLを使用して、テーブル内にある列を照会できます。

クロスアプライで行ごとのすべての列からXMLを構築し、values()関数を使用して値を抽出します。

このクエリではIDがわかっているため、テーブルから直接取得します。Col1とCol2が存在する場合と存在しない場合があるため、XMLを使用してそれらを取得します。

select T.ID,
       TX.X.value('(Col1/text())[1]', 'int') as Col1,
       TX.X.value('(Col2/text())[1]', 'int') as Col2
from T
  cross apply (select T.* for xml path(''), type) as TX(X)

SQLフィドル


-1

私のアプローチは他のアプローチとわずかに異なります。これにはシステムを使用し、単純にカウントを取得することをお勧めします。クエリの先頭にある変数に列カウントを割り当ててから、それに基づいて続行するかどうかを選択できるからです。それの欠点は…複数のテーブルに同じ列名がある場合、クエリしたいテーブルに列が存在するかどうか不確かです。ただし、カウントを取得するだけであるため、この手法は特定のテーブルでも機能します。

具体的にそれを求めることに関する「トラブル」は、あなたが経験しているトラブルです。一般に、NULL値が原因で問題が発生する場合は、別の方法で存在を確認します。これは、サーバーを動揺させるリスクなしにそれを行う1つの方法です。

SELECT COUNT(*) FROM sys.columns WHERE sys.columns.name = 'FarmID'

1
sysobjectsクエリで同様にを使用して、特定のテーブルにそのような列があるかどうかをチェックしてみませんか?
ypercubeᵀᴹ

はい...私はそれができると述べました...あなたが照会している特定のテーブルで同じことをすることさえできます... COUNTがゼロのときにCOUNTはエラーにならないので、COUNTを使用するための一般的な形式を示しました...私はすべきだと思います変数に割り当てることもできます。(たとえば、SELECT COUNT(*)AS myVarName ...)
jinzai

1
これがMarkのクエリよりも優れているかわかりません。SELECT 1 ...エラーも発生しません。
ypercubeᵀᴹ

私はそれが良いとは言いませんでしたが、同じ結果を達成するためのはるかに簡単な方法です。SELECT 1はエラーにならないかもしれませんが、COUNTとは異なります。SELECTはNULLであってもSOMETHING ...を返します。COUNTは単一の数値を返すだけです。この方法の方が高速であり、カウントは後で使用できることに言及しました。
jinzai

カウントが必要な場合はOKです。しかしEXISTS (SELECT ...)、通常は、より速く(SELECT COUNT(*) ...)、逆ではありません。
ypercubeᵀᴹ

-3

正しく理解できたら...

以下のようなクエリを使用して、カウントに基づいてそれに応じて動作することができます...カウントが> 1の場合、そのテーブルにcolがあり、カウント= 0の場合、そのcolはありませんテーブル

SELECT count(*)
FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME IN( 'Id')
AND TABLE_SCHEMA = 'dbo' and TABLE_NAME = 'UserBase';


4
いいえ、正しく理解できませんでした。また、チェックINFORMATION_SCHEMAビューに対してケースを @AaronBertrandから
キンシャー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.