Warum kann ich mit einer CASE-Anweisung nicht feststellen, ob eine Spalte vorhanden ist und nicht mit SELECT?

17

Warum funktioniert so etwas nicht?

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

Ich überprüfe nur, ob die Spalte vorhanden ist. SQL Server beklagt sich jedoch, dass sie Somecolnicht vorhanden ist. Gibt es eine Alternative dazu in einer einzigen Aussage?

Carson Reinke
quelle
3
Haben Sie ein Beispiel dafür, warum Sie dies tun möchten? Ich kann nicht verstehen, warum Sie eine Abfrage schreiben möchten, die versucht, aus einer Spalte auszuwählen, die möglicherweise nicht vorhanden ist.
Mark Sinkinson
4
SQL Server überprüft, ob Ihre Anweisungssyntax korrekt ist, bevor sie ausgeführt wird. Daher müssen alle referenzierten Spalten in den Tabellen vorhanden sein, auch wenn sie in eine CASEAnweisung eingeschlossen sind.
Mark Sinkinson
@ MarkSinkinson: Namen werden nach der Syntax überprüft , aber ja, SQL Server führt dies aus, bevor der Stapel ausgeführt wird.
Andriy M
1
Die Auswahl von INFORMATION_SCHEMAkönnte als Problemumgehung dienen.
Brilliand
4
@Brilliand sys.columns ist meiner Meinung nach viel besser .
Aaron Bertrand

Antworten:

43

Die folgende Abfrage verwendet dieselbe Idee wie in dieser erstaunlichen Antwort von ypercube :

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

Das funktioniert so:

  • Wenn dbo.Customerseine Spalte benannt ist SomeCol, wird SomeColin aufgelöst SomeCol AS MyTestals dbo.Customers.SomeCol;

  • Wenn die Tabelle keine solche Spalte enthält, ist der Verweis weiterhin gültig, da er jetzt wie folgt aufgelöst wird dummy.SomeCol: Auf dummySpalten kann in diesem Kontext verwiesen werden.

Sie können auf diese Weise mehrere "Ersatz" -Spalten angeben. Der Trick besteht nicht darin, den Tabellenalias für solche Spalten zu verwenden (was in den meisten Situationen eine verpönte Praxis ist, aber in diesem Fall hilft Ihnen das Weglassen des Tabellenalias, das Problem zu lösen).

Wenn die Tabelle in einem Join verwendet wird und die andere Tabelle eine eigene hat SomeCol, müssen Sie die obige Abfrage wahrscheinlich als abgeleitete Tabelle verwenden, bevor Sie sie im Join verwenden, damit der Trick funktioniert.

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
quelle
1
Ich frage mich, ob der SQL-Compiler nur ein kleines bisschen kompliziert ist. Super cool was du machen kannst.
Max Vernon
9

Eine Möglichkeit, dies zu tun, besteht darin, zu überprüfen, ob Spalten vorhanden sind, und dann das dynamische SQL basierend darauf zu erstellen, ob diese Spalte vorhanden ist oder nicht.

Ohne Dynamic SQL versucht SQL Server zu bewerten, ob die Spalte vorhanden ist oder nicht, bevor sie überhaupt die Anweisung ausführt, was zu einem Fehler führt.

Dies bedeutet jedoch, dass Sie zwei Abfragen schreiben müssen, die Sie möglicherweise in Zukunft ändern müssen. Aber ich glaube nicht, dass Sie SELECTAussagen auf Spalten ausrichten sollten , die möglicherweise nicht vorhanden sind.

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
quelle
Ja, macht Sinn, muss aber in einer einzigen Aussage stehen. Letztendlich suche ich wahrscheinlich eine magische Systemfunktion, die es nicht gibt.
Carson Reinke
4

Sie können XML verwenden, um Spalten abzufragen, die sich möglicherweise in der Tabelle befinden.

Erstellen Sie eine XML-Datei aus allen Spalten pro Zeile in einem Cross Apply und extrahieren Sie den Wert mithilfe der values()Funktion.

Bei dieser Abfrage ist die ID bekannt, also holen Sie sie sich direkt aus der Tabelle. Col1 und Col2 können vorhanden sein oder nicht, um sie mithilfe von XML zu erhalten.

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-Geige

Mikael Eriksson
quelle
-1

Mein Ansatz unterscheidet sich nur geringfügig von den anderen. Ich bevorzuge es, das System dafür zu verwenden und einfach eine Zählung zu erhalten, da Sie die Spaltenzählung einer Variablen am Anfang einer Abfrage zuweisen können und dann entscheiden, ob Sie fortfahren möchten oder nicht. Der Nachteil dabei ist… Wenn Sie denselben Spaltennamen in mehreren Tabellen haben, sind Sie sich nicht sicher, ob die Spalte in der Tabelle vorhanden ist, die Sie abfragen möchten. Die Technik funktioniert jedoch auch für bestimmte Tabellen, da Sie nur nach einer Zählung suchen.

Das "Problem" bei der konkreten Anfrage ist - das Problem, das Sie haben. Wenn Sie Probleme mit einem NULL-Wert haben, finden Sie im Allgemeinen eine andere Möglichkeit, die Existenz zu überprüfen. Dies ist eine Möglichkeit, ohne den Server aus dem Gleichgewicht zu bringen.

SELECT COUNT(*) FROM sys.columns WHERE sys.columns.name = 'FarmID'
Jinzai
quelle
1
Warum nicht auch das sysobjectsin Ihrer Abfrage verwenden, um zu prüfen, ob die bestimmte Tabelle eine solche Spalte enthält?
ypercubeᵀᴹ
Ja ... ich erwähnte, dass dies getan werden könnte ... Sie könnten sogar dasselbe für die bestimmte Tabelle tun, nach der Sie fragen ... Ich habe nur das allgemeine Format für die Verwendung von COUNT angezeigt, da COUNT keinen Fehler macht, wenn COUNT NULL ist, und ... ich nehme an, dass ich sollte Erwähnen Sie, dass Sie es auch einer Variablen zuweisen können. (zB SELECT COUNT (*) AS myVarName…)
jinzai
1
Ich kann nicht sehen, wie das besser wäre als Marks Frage. SELECT 1 ...Fehler auch nicht.
ypercubeᵀᴹ
Ich habe nicht gesagt, dass es besser ist, aber es ist viel einfacher, das gleiche Ergebnis zu erzielen. SELECT 1 ist möglicherweise kein Fehler, aber es ist nicht dasselbe wie COUNT. SELECT gibt ETWAS zurück ... auch wenn es NULL ist. COUNT muss nur eine einzige Zahl zurückgeben. Dieser Weg wäre schneller und ich habe erwähnt, dass die Zählung später verwendet werden kann.
Jinzai
Wenn du die Zählung brauchst, ok. Ist EXISTS (SELECT ...)aber meist schneller als (SELECT COUNT(*) ...), nicht umgekehrt.
ypercubeᵀᴹ
-3

Wenn ich es richtig verstanden habe ...

Sie können die folgende Abfrage verwenden und entsprechend der Anzahl handeln ... Wenn die Anzahl> 1 ist, bedeutet dies, dass Sie die Spalte in dieser Tabelle und die Anzahl = 0 haben, dann haben Sie diese Spalte darin nicht Tabelle

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

Sai
quelle
4
Nein, du hast es nicht richtig verstanden. Überprüfen Sie auch den Fall gegen INFORMATION_SCHEMA Ansichten von @AaronBertrand
Kin Shah