Wählen Sie den ersten Wert, falls vorhanden, andernfalls den Standardwert

233764
Panzercrisis

Eine Instanz von SQL Server gegeben, sich vorstellen, es gibt eine Tabelle mit dem Namen Configuration, der drei Spalten: ID, Name, und Data. Es sollten keine doppelten Zeilen für vorhanden sein Name.

Stellen Sie sich nun vor, Sie möchten die Daten auswählen, die für eine bestimmte Konfiguration gespeichert sind. Wenn diese Konfiguration jedoch keine Zeile in der Tabelle enthält, möchten Sie stattdessen nur einen Standardwert auswählen.

Das ist also was ich verwendet habe:

SELECT CASE
         WHEN EXISTS(SELECT 1
                     FROM   Configuration
                     WHERE  Name = 'NameOfConfiguration')
           THEN (SELECT Data
                 FROM   Configuration
                 WHERE  Name = 'NameOfConfiguration')
         ELSE 'Default Value'
       END 

Aber das sieht schlecht aus, also habe ich überlegt, wie ich den EXISTS()Anruf verkürzen, den Code etwas klarer machen, doppelten Code entfernen und möglicherweise sogar beschleunigen könnte. Dies ist, was ich gefunden habe bisher:

SELECT CASE
         WHEN COUNT(1) > 0
           THEN MIN(Data)
         ELSE 'Default Value' 
       END
FROM   Configuration
WHERE  Name = 'NameOfConfiguration' 

Ist das letztere Code-Snippet der richtige Weg, um diese Art von Dingen im Allgemeinen zu bewerkstelligen? Leider erfordert SQL Server, dass ein Aggregatfunktionsaufruf oder eine GROUP BYKlausel auf Letzteres angewendet wird und FIRST()in SQL Server offensichtlich nicht vorhanden ist. Ich beschränke diesen Aufruf nicht MIN()auf irgendeinen Datentyp. Gibt es eine bessere Möglichkeit, dies zu schreiben?

Antworten
30

7 Antworten auf die Frage

24
Serpiton

Die kürzere Abfrage, die mir einfällt, ist

SELECT COALESCE(MAX(Data), 'Default Value')
FROM   Configuration
WHERE  Name = 'NameOfConfiguration';

Wenn die Konfiguration vorhanden ist, dauert es. Wenn die Konfiguration nicht vorhanden MAXist, generiert das Gerät einen NULLWert (der MAX von nichts ist NULL), der mit dem Standardwert zusammengeführt wird


Es gibt einen generischeren Ansatz, der sich um die Aussagen von Martin Smith in seinem Kommentar kümmert, ist jedoch ausführlicher (unter der Annahme, dass der Konfigurationsname eindeutig ist).

WITH Param AS (
  SELECT ID, Data
  FROM   Configuration
  WHERE  Name = 'NameOfConfiguration'
  UNION ALL
  SELECT NULL, 'Default Value'
)
SELECT TOP 1
       Data
FROM   Param
ORDER BY ID DESC

Der IDStandardwert ist so eingestellt NULL, dass keine magische Zahl verwendet wird.
Aus dem BOL :NULL Werte werden als niedrigst mögliche Werte behandelt.

IMO sollte eine generelle Lösung nur verwendet werden, wenn eine bestimmte Lösung nicht möglich ist. Dies scheint jedoch nicht der Fall zu sein. Auch in einem generischen Fall muss der Standardwert ein gültiger Wert für den Datentyp der Konfigurationswerte sein, andernfalls UNIONwird ein Konvertierungsfehler zurückgegeben.

Dies ist zwar im Wesentlichen das Gleiche wie die letzte Abfrage in der Frage mit der zusätzlichen Einschränkung, dass nicht zwischen einer in der Tabelle gespeicherten NULL-Zeile und überhaupt einer Zeile unterschieden wird. Es hat immer noch das Problem, dass "MIN" / "MAX" nicht mit allen Datentypen funktioniert. Martin Smith vor 5 Jahren 1
11
200_success

Die Abfrage fühlt sich gequält an, da SQL mit Datensätzen arbeiten soll. Sie verwenden SQL wie eine normale Programmiersprache, versuchen, Tabellen in Skalare umzuwandeln und spezielle Werte in die Abfrage einzubetten, anstatt sie als Daten zu speichern.

Ich empfehle, dass Sie eine ConfigurationDefaultsTabelle erstellen und füllen, die Ihrer ConfigurationTabelle entspricht.

CREATE TABLE Configuration
( ID INTEGER IDENTITY PRIMARY KEY
, Name VARCHAR(123) UNIQUE
, Data VARCHAR(123)
);

CREATE TABLE ConfigurationDefaults
( Name VARCHAR(123) PRIMARY KEY
, Data VARCHAR(123)
);

INSERT INTO ConfigurationDefaults VALUES ('NameOfConfiguration', 'Default Value');

Erstellen Sie dann eine Ansicht, die die beiden zusammenführt, sodass die ConfigurationEinstellungen die Standardeinstellungen überschreiben.

CREATE VIEW EffectiveConfiguration AS
    SELECT COALESCE(Cfg.Name, Def.Name) AS Name
        , CASE WHEN Cfg.Name IS NULL THEN Def.Data ELSE Cfg.Data END AS Data
        FROM Configuration AS Cfg
            FULL OUTER JOIN ConfigurationDefaults AS Def
                ON Cfg.Name = Def.Name;

Sobald diese Infrastruktur eingerichtet ist, ist die Abfrage eine Freude zum Lesen und Schreiben!

SELECT Data FROM EffectiveConfiguration WHERE Name = 'NameOfConfiguration';
5
Malachi

Warum wählen Sie nicht einfach das Feld aus der Tabelle aus, in dem Name gleich ist, NameOfConfigurationund verwenden Sie dann ein isnull?

DECLARE @GenericVariableName VARCHAR(MAX)
SET @GenericVariableName = (SELECT TOP (1) Data
                             FROM Configuration 
                             WHERE Name = 'NameOfConfiguration')

SELECT isnull(@GenericVariableName, 'Default Value')

Dies wird viel direkter als die Verwendung der EXISTAussage

4
RubberDuck

Ich denke, dass die erste Abfrage, die Sie ausprobierten, bessere Ergebnisse erzielen würde, da sie nichts zusammenfassen muss. Ich glaube, so würde ich es angehen. Ich würde es einfach umformatieren, damit wir es ein bisschen leichter lesen können.

SELECT CASE 
    WHEN EXISTS(
        SELECT 1 
        FROM Configuration 
        WHERE Name = 'NameOfConfiguration'
    )
    THEN (
        SELECT Data 
        FROM Configuration 
        WHERE Name = 'NameOfConfiguration'
    ) 
    ELSE 'Default Value' 
END

Es gibt jedoch noch ein paar weitere Verbesserungen. Sie müssen diese Spalte als Alias ​​angeben. Andernfalls erhalten Sie einen kilometerlangen Namen. (Abhängig von Ihrem RDBMS. Ich weiß, dass SQL Server es mit der Ausdruckszeichenfolge benennt.)

Das zweite, was ich tun würde, ist eine Variable zu deklarieren 'NameOfConfiguration'. Sie verwenden denselben Parameter an zwei Stellen, daher ist es sinnvoll, eine Variable zu deklarieren. Dies macht es auch einfacher, wenn Sie sich entscheiden, diese Abfrage in einer gespeicherten Prozedur zusammenzufassen.

4
ahains

Wenn Sie über einen statischen Standardwert verfügen, können Sie diesen nur einer Variablen zuweisen und das Überschreiben bedingt machen, wenn eine Zeile gefunden wird. Diese erste Auswahl gibt data1 zurück und die zweite Auswahl gibt den Standardwert zurück, da der Name nicht in der Tabelle enthalten ist.

create table #cfg (name varchar(32) primary key, id int, data varchar(32))
insert #cfg values ('name1', null, 'data1');
insert #cfg values ('name2', null, 'data2');
go

declare @data varchar(32)
set @data='defaultvalue';
select @data=data
from #cfg
where name='name1';
select @data;
go

declare @data varchar(32)
set @data='defaultvalue';
select @data=data
from #cfg
where name='name3';
select @data;
go
3
Paschover

Dies ist eindeutig ein Fall, in dem Sie verwenden möchten ISNULL. Dafür müssen Sie jedoch eine Zeile zurückgeben, falls der Konfigurationsname nicht vorhanden ist. Das wiederum bedeutet, dass Sie einen Left Outer Join benötigen. Sie erstellen dann eine Dummy-Temp-Tabelle mit dem gewünschten Wert und belegen die äußeren Daten auf den tatsächlichen Daten.

select
case when c.Name is null then 'Default Value' else c.Data end [Data]
from
(select 'NameOfConfiguration' [Name]) Target
left outer join Configuration c on c.[Name] = Target.[Name]
Wenn Sie eine Antwort ablehnen, wäre es schön, wenn Sie erklären würden, warum. Paschover vor 5 Jahren 2
können Sie eine Select-Anweisung nicht in der `isnull`-Anweisung verschachteln? Malachi vor 5 Jahren 0
Nein, Sie brauchen dafür eine Reihe zurück. Paschover vor 5 Jahren 0
Ich habe einen Weg gefunden, meine Antwort zu sehen Malachi vor 5 Jahren 0
@Malachi: Ich kann deine Antwort noch nicht kommentieren, deshalb werde ich hier posten. Sie benötigen nicht die oberste 1 in Ihrer Abfrage, da die Tabelle eine Einschränkung enthalten sollte, die die Konfigurationsnamen eindeutig macht. Paschover vor 5 Jahren 0
Ich bin es gewohnt, mit Datenbanken zu arbeiten, die ich nicht erstellt habe, und ich gehe niemals davon aus, dass die Tabellen perfekt sind. :) aber Sie haben Recht Malachi vor 5 Jahren 0
Es ist also nicht mehr eindeutig ein Fall für `ISNULL ()`? 200_success vor 5 Jahren 0
@ 200_success: Ich denke immer noch, dass es die sauberste Lösung ist. Es vermeidet die Verwendung von Aggregatfunktionen wie "MAX", die mit dem gewünschten Datentyp arbeiten können oder nicht. Ich denke nicht, dass die Lösung, die Sie mit der Tabelle für die Hilfseinstellungen vorgeschlagen haben, gut funktioniert. Wenn Sie mehr als einen Eintrag enthalten, führt der vollständige Outer-Join dazu, dass alles dupliziert wird. Aber ich mag die Idee einer Ansicht auf den Originaltisch. Ich glaube, eine Kombination aus meiner Abfrage und Ihrer Ansichtsidee ist wahrscheinlich die optimale Lösung. Paschover vor 5 Jahren 0
1
Sukhdev Singh

Wenn Sie den SQL-Entwickler verwenden, wird der Fehler angezeigt:

ORA-00923: FROM-Schlüsselwort nicht gefunden, wo erwartet 00923. 00000 - "FROM-Schlüsselwort nicht gefunden, wo erwartet"

In diesem Fall müssen Sie from dualam Ende hinzufügen .

SELECT CASE 
    WHEN EXISTS(
        SELECT 1 
        FROM your_table 
        WHERE your_item= 'XXX' 
    )
    THEN (
        SELECT some_value
        FROM your_table 
        WHERE your_item= 'XXX' 
    ) 
    ELSE '0' END from dual;