2009-08-12 9 views
0

Cette question est mieux formulée avec un exemple simple. Pourquoi ne puis-je pas faire cela?Utiliser les éléments composites de sélection dans la clause where

select (lastname + ', ' + firstname) as fullname 
from people 
where fullname = 'Bloggs, Joe' 

à la place que je dois faire ceci:

select (lastname + ', ' + firstname) as fullname 
from people 
where (lastname + ', ' + firstname) = 'Bloggs, Joe' 

qui sent mauvais pour moi.

Plus la requête est complexe, plus ce problème devient grave.

Suivi

Voici un meilleur exemple basé sur le problème du monde réel dont l'origine de la question.

SELECT ClientID, 
     Name, 
     ContractStartDate, 
     ContractDetails.ContractLength, 
     DATEADD(MONTH, ContractDetails.ContractLength, ContractStartDate) 
      as ContractEndDate 
FROM Clients 
LEFT OUTER JOIN ContractDetails 
    ON Clients.ClientID = ContractDetails.ClientID 
WHERE DATEADD(MONTH, ContractDetails.ContractLength, ContractStartDate) 
     > '2009-06-30' 

J'ai réécrit la requête d'utiliser une vue intégrée comme l'a suggéré. Cependant, il contient encore la répétition - mais cette fois d'une jointure.

SELECT ClientID, 
     Name, 
     contractStartDate, 
     ContractDetails.ContractLength, 
     contractEndDate 
FROM (
     SELECT ClientID, 
      Name, 
      ContractStartDate, 
      DATEADD(MONTH, ContractDetails.ContractLength, contractStartdate) 
       AS contractEndDate 
     FROM Clients 
     LEFT OUTER JOIN ContractDetails 
     on Clients.ClientID = ContractDetails.ClientID 
    ) myview 
LEFT OUTER JOIN ContractDetails 
    on myview.ClientID = ContractDetails.ClientID 
WHERE myview.ContractEndDate > '2009-06-30' 
ORDER BY ClientID 

Le point de cette requête est de trouver tous les clients en direct comme à un moment donné dans le temps, où aucune donnée d'état historique est conservé (à savoir le calcul de la date de fin de contrat d'une date de début du contrat connu et durée).

Quelqu'un peut-il trouver un moyen d'éliminer cette répétition?

suivi final

Robin Day m'a aidé avec la chose clé que je manquais ici qui m'a permis en fait de supprimer la duplication. Cependant KM a un point où il dit que le WHERE devrait être sur la vue imbriquée, plutôt que sur le résultat final, ce qui exigerait qu'une partie de la déclaration soit dupliquée (ce que j'essaie d'éviter). Dans ce cas particulier je peux m'en tirer parce que je sais pas il n'y a pas des millions d'enregistrements dans la table ContractDetails, et ne le sera jamais.

SELECT ClientID, 
    Name, 
    ContractStartDate, 
    myview.ContractLength, 
    ContractEndDate 
FROM (
    SELECT ClientID, 
     Name, 
     ContractStartDate, 
     DATEADD(MONTH, ContractDetails.ContractLength, ContractStartdate) 
      AS ContractEndDate, 
     ContractDetails.ContractLength as Length 
    FROM Clients 
    LEFT OUTER JOIN ContractDetails 
    on Clients.ClientID = ContractDetails.ClientID 
) myview 
WHERE myview.ContractEndDate > '2009-06-30' 
ORDER BY ClientID 
+0

Pourquoi le downvote? – tomfanning

+2

Ajoutez ContractLength à la vue imbriquée et vous n'avez plus besoin d'effectuer la jointure en dehors de cette vue. –

Répondre

1

La liste de sélection est une transformation d'une table virtuelle renvoyée par la de, et commande clauses. Les clauses ne sont pas connues de la liste de sélection . De plus, toutes les transformations de colonnes définies dans la clause ne sont pas sargables et forcent SQL à effectuer des analyses de table ou d'index. En d'autres termes, le faire tuerait absolument la performance.

+0

Une réponse concise à la question initiale, en me indiquant qu'il doit simplement être travaillé ailleurs dans la requête. Merci beaucoup. – tomfanning

1

essayer:

select 
    (lastname + ', ' + firstname) as fullname 
    from people 
    where lastname = 'Bloggs' AND firstname='Joe' 

faire, ce qui devrait être dans un index non filtre basé sur la sortie au format "fullname", filtre basé sur les colonnes.

EDIT
en fonction de votre question révisée:

mis la condition dans la table dérivée de la limiter (et le garder aussi petit que possible). J'ai vu des requêtes beaucoup plus rapides en faisant cela seul. Je suis sûr que le moteur de recherche est assez intelligent pour ne pas faire le DATEADD() deux fois, donc ne vous inquiétez pas à ce sujet.

SELECT ClientID, 
     Name, 
     contractStartDate, 
     ContractDetails.ContractLength, 
     contractEndDate 
FROM (
     SELECT ClientID, 
      Name, 
      ContractStartDate, 
      DATEADD(MONTH, ContractDetails.ContractLength, contractStartdate) 
       AS contractEndDate 
     FROM Clients 
     LEFT OUTER JOIN ContractDetails 
     on Clients.ClientID = ContractDetails.ClientID 
     WHERE DATEADD(MONTH, ContractDetails.ContractLength, contractStartdate) > '2009-06-30' 
    ) myview 
LEFT OUTER JOIN ContractDetails 
    on myview.ClientID = ContractDetails.ClientID 
ORDER BY ClientID 
+0

-1, je pense que vous avez manqué le point - j'ai donné un exemple simpliste. Peut-être trop simple. Le bit aliasé de l'instruction select peut contenir un grand calcul, une sous-requête, etc. Bien sûr, vous pouvez filtrer en utilisant les champs bruts - ce n'était pas le point. – tomfanning

+0

@tomfanning, éditez votre question avec un meilleur exemple. Je réponds à ce que tu demandes, pas à ce que tu penses (je ne peux pas lire dans ton esprit). –

+0

@KM, la question était "pourquoi je ne peux pas faire cette chose spécifique", pas "résoudre ce problème de requête". Néanmoins, j'ai ajouté un problème spécifique au domaine et un remaniement en utilisant une vue intégrée basée sur la réponse de Robin Day. – tomfanning

3

Vous pouvez utiliser une table dérivée/vue imbriquée ...

select 
    fullname 
from 
(
    select 
     (lastname + ', ' + firstname) as fullname 
    from 
     people 
) myview 
where 
    myview.fullname = 'Bloggs, Joe' 

EDIT: Juste pour clarifier, est de montrer le concept que vous demandez au sujet.Dans cet exemple spécifique, votre clause WHERE doit vérifier firstname = 'Joe' et lastname = 'Bloggs' car KM a répondu plutôt que de vérifier le nom complet.

+0

J'aime votre message EDIT, essentiellement répéter ma réponse. Cependant, ma réponse a déjà reçu deux votes vers le bas jusqu'à maintenant ... –

+0

@Robin Day: Vous n'avez pas vraiment répondu à la question, vous venez de fournir un travail pour obtenir l'abstraction qu'il voulait. – MyItchyChin

+0

@CptSkippy, il n'y a aucun moyen –

1

Votre exemple modifié n'est-il pas trop complexe? Quel est le problème avec:

SELECT * 
FROM (
     SELECT ClientID, 
      Name, 
      ContractStartDate, 
      ContractLength, 
      DATEADD(MONTH, ContractDetails.ContractLength, contractStartdate) 
       AS contractEndDate 
     FROM Clients 
     LEFT OUTER JOIN ContractDetails 
     on Clients.ClientID = ContractDetails.ClientID 
    ) myview 
WHERE myview.ContractEndDate > '2009-06-30' 
ORDER BY ClientID 
+0

Si je comprends bien sa logique, cette stratégie a déjà été, euh, recommandée par KM parce que DATEADD() est appelé pour tout dans la table Clients. C'est ce qui ne va pas avec ça. – tomfanning

+0

Votre "suivi final" Est exactement le même que ma suggestion ci-dessus, à l'exception de l'astérisque dans mon instruction SELECT qui supprime simplement une certaine verbosité. –

0

Comment faire pour que cette requête fonctionne avec IN et colonne multiple? Cela fonctionne dans Oracle, mais pas T-SQL.

select (lastname + ', ' + firstname) as fullname 
from people 
where ((lastname, firstname)) IN (('Bloggs', 'Joe')) 
Questions connexes