2009-05-14 11 views
3

Je dois écrire une requête qui obtient un ensemble d'informations à partir d'une table, mais s'il n'y a pas d'informations pour ce client spécifique, alors utilisez l'ensemble par défaut. Je me demandais s'il y avait un moyen d'éviter la duplication de requêtes dans une instruction if (exists()).SQL if (exists()) duplication de requête

Par exemple:

IF(EXISTS(SELECT * FROM UserTable WHERE Name = @UserName)) 
BEGIN 
    SELECT * FROM UserTable WHERE Name = @UserName)) 
END 
ELSE 
BEGIN 
    SELECT * FROM UserTable WHERE Name = 'Jon Skeet')) 
END 

Les deux premiers (existe et choisit la véritable partie du cas) sont exactement les mêmes. Je veux éviter d'exécuter deux fois la même requête si l'instruction est vraie. Je sais que l'existant s'arrête une fois que la première condition vraie est remplie mais c'est toujours O (n) pire cas.

Une autre option que je connais, est de mettre les informations dans une table temporaire et vérifier si l'information est là, sinon renvoyer les informations par défaut.

Les deux façons de fonctionner, mais quelle est la meilleure façon de le faire? Existe-t-il d'autres moyens de le faire? Y at-il un moyen de faire cela dans la clause WHERE puisque c'est la seule chose qui est différente?

Édition: Correction d'un exemple pour renvoyer une ligne et non un seul élément. Donc, les réponses de dumping du select à une seule variable seraient égales à une table temporaire, je suppose. En outre, de ne pas la colère Jon Skeet, orthographié son nom droit

Répondre

6

Vous pouvez le faire:

SELECT TOP 1 
    UserID 
FROM 
    UserTable 
WHERE 
    Name IN (@UserName, 'John Skeet') 
ORDER BY 
    CASE WHEN Name = 'John Skeet' THEN 2 ELSE 1 END 

(ou utilisez LIMIT ou quelle que soit la méthode utilisée pour votre SGBDR)

Ou vous pouvez le faire:

DECLARE @UserID INT 

SELECT 
    @UserID = UserID 
FROM 
    UserTable 
WHERE 
    Name = @UserName 

IF (@UserID IS NULL) 
    SELECT 
      @UserID = UserID 
    FROM 
      UserTable 
    WHERE 
      Name = 'John Skeet' 

SELECT @UserID AS UserID 

Ou ceci:

SELECT 
    COALESCE(T2.UserID, T1.UserID) 
FROM 
    UserTable T1 
LEFT OUTER JOIN UserTable T2 ON 
    T2.Name = @UserName 
WHERE 
    T1.Name = 'John Skeet' 

Ou ceci:

SELECT 
    UserID 
FROM 
    UserTable 
WHERE 
    Name = @UserName 

IF (@@ROWCOUNT = 0) -- MS SQL Server specific, your RDBMS method will vary 
    SELECT 
      UserID 
    FROM 
      UserTable 
    WHERE 
      Name = 'John Skeet' 
+0

C'est beaucoup d'options! – JerSchneid

+0

J'aime la condition de recherche pour le nom ;-) – sl3dg3

1

Vous pouvez sélectionner dans une variable vérifier la variable si elle revient run null la requête

par défaut obtenir quelque chose comme ceci:

Declare @uID int; 

SELECT @uID = UserID FROM UserTable WHERE Name = @UserName 

IF (@uID is null) 
begin 
'Select default here' 
End 

select @uID 
3

Que diriez-vous:

DECLARE @UserID int; 
SELECT @UserID = UserID FROM UserTable WHERE Name = @UserName 

IF(@UserID IS NULL) 
BEGIN 
    SELECT @UserID=UserID FROM UserTable WHERE Name = 'Jon Skeet')) 
END 

SELECT @UserID 
+0

Cela fonctionne pour sélectionner une seule variable, qu'en est-il une ligne complète (fixe l'exemple dans la question)? Ce serait le même que d'utiliser une table temporaire. Considérez-vous qu'une table temporaire est bonne pour cela ou est-ce que cela ajouterait trop de complexité dont vous n'avez pas besoin? –

+0

Je ne pense pas que vous ayez besoin d'une table temporaire (mais je n'aurais pas peur d'eux non plus). J'aime la première réponse de @Tom H si vous voulez faire un SELECT * – JerSchneid

+0

Cela fonctionne pour n'importe quel nombre de colonnes nommées, par ex. 'SELECT @userid = userid, @username = nom d'utilisateur FROM ...'. Il existe également des variables de table dans les éditions ultérieures de SQL Server, mais contrairement à SELECT INTO #temp, vous devez les définir explicitement. –

0

Vous ne dites pas quel SGBD vous utilisez, mais ce n'est clairement pas Oracle (qui est celui que je connais). Cependant, je me attends à votre SGBD vous permettra de faire quelque chose comme ce pseudo-code:

SELECT UserID FROM UserTable WHERE Name = @UserName 
IF <no data returned by that> 
BEGIN 
    SELECT UserID FROM UserTable WHERE Name = 'John Skeet' 
END 

Dans Oracle serait:

BEGIN 
    SELECT UserID INTO v_UserID FROM UserTable WHERE Name = :UserName; 
EXCEPTION 
    WHEN NO_DATA_FOUND THEN 
    SELECT UserID INTO v_UserID FROM UserTable WHERE Name = 'John Skeet'; 
END 
0

À moins que vous allez définir un ensemble de variables pour chaque champ que vous retournerez (et il n'y a qu'un seul enregistrement) Je pense que le moyen le plus efficace est de faire l'appel deux fois. La table temporaire coûterait probablement plus cher.

1

Si vous êtes préoccupé par les performances, je pense que vous n'avez probablement pas besoin d'être - la plupart des SGBD devraient optimiser la duplication. Si vous êtes toujours préoccupé par les performances, exécutez les deux façons pour voir si votre SGBD a un problème avec elle.

Si vous êtes préoccupé par le maintien de code des sections de code en double, la lisibilité, etc., alors étudiez les réponses ci-dessus dans cet esprit. Vous pouvez trouver que la duplication est votre moins pire option. C'est dommage que SQL ne soit pas plus malléable.