Mengapa saya tidak bisa menggunakan pernyataan KASUS untuk melihat apakah ada kolom dan bukan SELECT dari itu?

17

Mengapa sesuatu seperti ini tidak berhasil?

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

Saya hanya memeriksa apakah kolom ada, namun, SQL Server mengeluh tentang Somecoltidak ada. Apakah ada alternatif untuk ini dalam satu pernyataan?

Carson Reinke
sumber
3
Apakah Anda memiliki contoh mengapa Anda ingin melakukan ini? Saya tidak mengerti mengapa Anda ingin menulis kueri yang mencoba memilih dari kolom yang mungkin tidak ada.
Mark Sinkinson
4
SQL Server mengevaluasi bahwa sintaks pernyataan Anda sudah benar sebelum dijalankan. Oleh karena itu semua kolom yang direferensikan harus ada dalam tabel, bahkan jika mereka dibungkus dalam CASEpernyataan.
Mark Sinkinson
@ Markarkinkinson: Nama diperiksa setelah sintaks, tapi ya, SQL Server melakukan itu sebelum benar-benar menjalankan batch.
Andriy M
1
Memilih dari INFORMATION_SCHEMAbisa berfungsi sebagai solusi.
Brilliand
4
@Brilliand sys.columns adalah IMHO jauh lebih baik .
Aaron Bertrand

Jawaban:

43

Permintaan berikut menggunakan ide yang sama seperti dalam jawaban yang luar biasa ini oleh ypercube :

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

Ini berfungsi seperti ini:

  • jika dbo.Customersmemiliki kolom bernama SomeCol, maka SomeColdi SomeCol AS MyTestakan menyelesaikan sebagai dbo.Customers.SomeCol;

  • jika tabel tidak memiliki kolom seperti itu, referensi masih akan valid, karena sekarang akan diselesaikan sebagai dummy.SomeCol: dummykolom dapat dirujuk dalam konteks itu.

Anda dapat menentukan beberapa kolom "cadangan" dengan cara itu. Triknya adalah tidak menggunakan alias tabel untuk kolom tersebut (yang merupakan praktik yang disukai di sebagian besar situasi, tetapi dalam hal ini menghilangkan alias tabel membantu Anda untuk menyelesaikan masalah).

Jika tabel digunakan dalam gabungan dan tabel lainnya memiliki tabel sendiri SomeCol, Anda mungkin perlu menggunakan kueri di atas sebagai tabel turunan sebelum menggunakannya dalam gabungan agar trik tetap berfungsi, seperti ini:

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 ...
;
Andriy M
sumber
1
Saya bertanya-tanya apakah kompiler SQL hanya sedikit rumit. Sangat keren apa yang bisa Anda lakukan.
Max Vernon
9

Salah satu cara untuk melakukan ini adalah dengan memeriksa keberadaan kolom, kemudian membangun SQL dinamis berdasarkan pada apakah kolom itu ada atau tidak.

Tanpa Dynamic SQL, SQL Server akan mencoba untuk mengevaluasi apakah kolom ada atau tidak bahkan sebelum mengeksekusi statment, yang mengakibatkan kesalahan.

Namun, itu berarti Anda akan memiliki 2 pertanyaan untuk ditulis dan berpotensi berubah di masa mendatang. Tapi saya tidak percaya Anda harus benar-benar menargetkan SELECTpernyataan pada kolom yang mungkin tidak ada.

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
Mark Sinkinson
sumber
Ya, masuk akal, bagaimanapun, harus dalam satu pernyataan. Pada akhirnya, saya mencari kemungkinan beberapa fungsi sistem sihir yang tidak ada.
Carson Reinke
4

Anda dapat menggunakan beberapa XML untuk kueri kolom yang mungkin ada di tabel.

Buat XML dari semua kolom per baris dalam tanda silang, terapkan dan ekstrak nilainya menggunakan values()fungsi.

Dalam kueri ini ID dikenal sehingga dapatkan langsung dari tabel. Col1 dan Col2 mungkin ada atau tidak jadi buat mereka menggunakan 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 Fiddle

Mikael Eriksson
sumber
-1

Pendekatan saya hanya berbeda sedikit dari yang lain. Saya lebih suka menggunakan sistem untuk ini dan hanya mendapatkan hitungan karena Anda dapat menetapkan jumlah kolom ke variabel di bagian atas kueri dan kemudian memilih untuk melanjutkan atau tidak berdasarkan itu. Kelemahannya adalah ... jika Anda memiliki nama kolom yang sama di beberapa tabel, Anda tidak yakin bahwa kolom ada di tabel yang ingin Anda kueri. Namun, teknik ini juga berfungsi pada tabel tertentu, karena Anda hanya ingin mendapat hitungan.

'Kesulitan' dengan memintanya secara khusus adalah - masalah yang Anda alami. Secara umum, jika nilai NULL menyebabkan masalah Anda ... temukan cara lain untuk memverifikasi keberadaan. Ini adalah salah satu cara untuk melakukan itu tanpa membahayakan server.

SELECT COUNT(*) FROM sys.columns WHERE sys.columns.name = 'FarmID'
jinzai
sumber
1
Mengapa tidak menggunakan sysobjectsjuga dalam kueri Anda untuk memeriksa apakah tabel spesifik memiliki kolom seperti itu?
ypercubeᵀᴹ
Ya ... saya sebutkan itu bisa dilakukan ... Anda bahkan bisa melakukan hal yang sama pada tabel tertentu yang Anda tanyakan ... Saya hanya menunjukkan format umum untuk menggunakan COUNT karena COUNT tidak kesalahan ketika COUNT NOL dan ... Saya kira saya harus menyebutkan bahwa Anda dapat menetapkannya ke variabel juga. (mis. PILIH COUNT (*) SEBAGAI myVarName ...)
jinzai
1
Saya tidak bisa melihat bagaimana ini akan lebih baik daripada permintaan Mark. SELECT 1 ...juga tidak salah.
ypercubeᵀᴹ
Saya tidak mengatakan itu lebih baik, tetapi ini adalah cara yang jauh lebih sederhana untuk mencapai hasil yang sama. SELECT 1 mungkin tidak salah, tetapi tidak sama dengan COUNT. SELECT mengembalikan SESUATU ... bahkan jika itu NULL. COUNT hanya perlu mengembalikan satu nomor. Cara ini akan lebih cepat dan saya memang menyebutkan bahwa hitungannya bisa digunakan nanti.
jinzai
Jika Anda perlu hitung ok. TapiEXISTS (SELECT ...) biasanya lebih cepat daripada (SELECT COUNT(*) ...), bukan sebaliknya.
ypercubeᵀᴹ
-3

Jika saya memahaminya dengan benar ...

Anda dapat menggunakan kueri sesuatu seperti di bawah ini dan bertindak sesuai berdasarkan hitungan ... Jika jumlah tersebut> 1 maka itu berarti Anda memiliki col di tabel itu, dan jumlah = 0 maka Anda tidak memiliki col di situ meja

Hitung SELECT (*)
DARI INFORMATION_SCHEMA.COLUMNS DI MANA COLUMN_NAME DALAM ('Id')
DAN TABLE_SCHEMA = 'dbo' dan TABLE_NAME = 'UserBase';

Sai
sumber
4
Tidak, Anda tidak mengerti dengan benar. Lihat juga Kasus terhadap INFORMATION_SCHEMA tampilan dari @AaronBertrand
Kin Shah