Perché non posso usare un'istruzione CASE per vedere se esiste una colonna e non SELEZIONARE da essa?


17

Perché qualcosa del genere non funziona?

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

Sto solo verificando se la colonna esiste, tuttavia, SQL Server si lamenta di Somecolnon esistere. C'è un'alternativa a questo in una singola affermazione?


3
Hai un esempio del perché vorresti farlo? Non riesco a capire perché vorresti scrivere una query che tenti di selezionare da una colonna che potrebbe non esistere.
Mark Sinkinson,

4
SQL Server valuta che la sintassi dell'istruzione sia corretta prima di eseguirla. Pertanto, tutte le colonne a cui si fa riferimento devono esistere nelle tabelle, anche se racchiuse in CASEun'istruzione.
Mark Sinkinson,

@MarkSinkinson: i nomi vengono controllati dopo la sintassi, ma sì, SQL Server lo fa prima di eseguire effettivamente il batch.
Andriy M,

1
La selezione da INFORMATION_SCHEMApotrebbe funzionare come soluzione alternativa.
Brilliand,

Risposte:


43

La seguente query utilizza la stessa idea di questa incredibile risposta di ypercube :

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

Funziona così:

  • se dbo.Customersha una colonna denominata SomeCol, allora SomeColin SomeCol AS MyTestsi risolverà come dbo.Customers.SomeCol;

  • se la tabella non ha tale colonna, il riferimento sarà comunque valido, perché ora verrà risolto come dummy.SomeCol: le dummycolonne possono essere referenziate in quel contesto.

In questo modo è possibile specificare più colonne "di riserva". Il trucco non è quello di utilizzare l'alias di tabella per tali colonne (che è una pratica accigliata nella maggior parte delle situazioni, ma in questo caso omettere l'alias di tabella ti aiuta a risolvere il problema).

Se la tabella viene utilizzata in un join e l'altra tabella ha una propria SomeCol, probabilmente dovrai utilizzare la query sopra come tabella derivata prima di utilizzarla nel join per far funzionare il trucco, qualcosa del genere:

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
Mi chiedo se il compilatore SQL sia solo un po 'complicato. Fantastico cosa puoi fare.
Max Vernon,

9

Un modo per farlo è verificare l'esistenza delle colonne, quindi creare l'SQL dinamico in base all'esistenza o meno di quella colonna.

Senza Dynamic SQL, SQL Server tenterà di valutare se la colonna esiste o meno prima ancora di eseguire la dichiarazione, causando un errore.

Tuttavia, significa che avrai 2 query da scrivere e potenzialmente modificare in futuro. Ma non credo che dovresti davvero indirizzare le SELECTdichiarazioni su colonne che potrebbero non esistere.

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

Sì, ha senso, tuttavia, deve essere in una singola istruzione. Alla fine, sto cercando probabilmente qualche funzione di sistema magico che non esiste.
Carson Reinke,

4

È possibile utilizzare alcuni XML per eseguire query su colonne che potrebbero trovarsi nella tabella.

Costruire un XML da tutte le colonne per riga in una croce applicare ed estrarre il valore utilizzando la values()funzione.

In questa query l'ID è noto, quindi prendilo direttamente dalla tabella. Col1 e Col2 potrebbero essere lì o meno, quindi farli usando l'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


-1

Il mio approccio differisce solo leggermente dagli altri. Preferisco usare il sistema per questo e ottenere semplicemente un conteggio perché è possibile assegnare il conteggio delle colonne a una variabile nella parte superiore di una query e quindi scegliere di procedere o meno in base a quello. Il rovescio della medaglia è che ... se hai lo stesso nome di colonna in più tabelle, non sei sicuro che la colonna esista nella tabella che desideri interrogare. Tuttavia, la tecnica funziona anche su tabelle particolari, poiché stai solo cercando di ottenere un conteggio.

Il "problema" nel richiederlo in modo specifico è il problema che stai riscontrando. In generale, se un valore NULL causa problemi ... trova un altro modo per verificare l'esistenza. Questo è un modo per farlo senza rischiare di sconvolgere il server.

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

1
Perché non utilizzare sysobjectsanche nella query per verificare se la tabella specifica ha una colonna del genere?
ypercubeᵀᴹ

Sì ... ho detto che si potrebbe fare ... potresti persino fare lo stesso sulla particolare tabella di cui stai interrogando ... Ho appena mostrato il formato generale per l'utilizzo di COUNT perché COUNT non si verifica quando COUNT è ZERO e ... Suppongo che dovrei menziona che puoi assegnarlo anche a una variabile. (es. SELEZIONA CONTEGGIO (*) COME myVarName ...)
jinzai

1
Non riesco a vedere come sarebbe meglio della domanda di Mark. SELECT 1 ...non si sbaglia neanche.
ypercubeᵀᴹ

Non ho detto che era meglio, ma è un modo molto più semplice per ottenere lo stesso risultato. SELECT 1 potrebbe non essere un errore, ma non è la stessa cosa di COUNT. SELECT restituisce QUALCOSA ... anche se è NULL. COUNT deve restituire un solo numero. In questo modo sarebbe più veloce e ho detto che il conteggio può essere utilizzato in seguito.
jinzai,

Se hai bisogno del conteggio ok. Ma di EXISTS (SELECT ...)solito è più veloce di (SELECT COUNT(*) ...), non viceversa.
ypercubeᵀᴹ

-3

Se l'ho capito bene ...

Puoi usare la query in modo simile al seguente e agire di conseguenza in base al conteggio ... Se il conteggio è> 1, significa che hai il col in quella tabella e il conteggio = 0, allora non hai quel col in quello tavolo

SELEZIONA contare (*)
DA INFORMATION_SCHEMA.COLUMNS DOVE COLUMN_NAME IN ('Id')
E TABLE_SCHEMA = 'dbo' e TABLE_NAME = 'UserBase';


4
No, non hai capito bene. Controlla anche il caso rispetto alle informazioni INFORMATION_SCHEMA di @AaronBertrand
Kin Shah,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.