2011-07-19 2 views
1

Si je tourne cette sous-requête qui sélectionne les personnes de vente et leur prix le plus élevé payé pour tout article qu'ils vendent:SQL: l'amélioration de rejoindre l'efficacité

select *, 
    (select top 1 highestProductPrice 
    from orders o 
    where o.salespersonid = s.id 
    order by highestProductPrice desc) as highestProductPrice 
from salespersons s 

pour cette jointure afin d'améliorer l'efficacité:

select *, highestProductPrice 
from salespersons s join (
     select salespersonid, highestProductPrice, row_number(
      partition by salespersonid 
      order by salespersonid, highestProductPrice) as rank 
     from orders) o on s.id = o.salespersonid 

il touche encore chaque enregistrement de la commande (il énumère toute la table avant de filtrer par SalesPersonID il semble.) Cependant, vous ne pouvez pas le faire:

select *, highestProductPrice 
from salespersons s join (
     select salespersonid, highestProductPrice, row_number(
      partition by salespersonid 
      order by salespersonid, highestProductPrice) as rank 
     from orders 
     where orders.salepersonid = s.id) o on s.id = o.salespersonid 

La clause where dans la jointure provoque un `identificateur multi-parties" à l'ID "n'a pas pu être lié.

Existe-t-il un moyen de joindre le top 1 de chaque groupe de commandes avec une jointure sans toucher à chaque enregistrement dans les ordres?

+0

Que voulez-vous dire «touche chaque enregistrement»? Il va falloir toucher tous les records ** pour savoir lequel doit être le top 1 **. Si vous n'avez pas d'index * existant * dans l'ordre souhaité, il doit être exécuté au moment de l'exécution, à chaque fois (il peut mettre en cache les réponses ou créer un index temporaire réutilisable). –

+0

La requête prend beaucoup moins de temps à s'exécuter lorsque vous filtrez l'énumération uniquement sur les enregistrements pouvant être joints. En d'autres termes, ne classer que les enregistrements que je veux classer, et ne pas classer les enregistrements qui ne se joindraient pas en fonction de salespersonid –

Répondre

2

Essayez

SELECT 
    S.*, 
    T.HighestProductPrice 
FROM 
    SalesPersons S 

    CROSS APPLY 
    (
    SELECT TOP 1 O.HighestProductPrice 
    FROM Orders O 
    WHERE O.SalesPersonid = S.Id 
    ORDER BY O.SalesPersonid, O.HighestProductPrice DESC 
) T 
+0

C'est très intéressant que cela permette à la condition interne de référencer la table externe. Je vais essayer. –

+0

Peut-il vraiment être plus efficace que d'utiliser un sous-select avec le classement? –

1

serait

select s.*, max(highestProductPrice) 
    from salespersons s 
    join orders o on o.salespersonid = s.id 
group by s.* 

ou

select s.*, highestProductPrice 
    from salespersons s join (select salepersonid, 
          max(highestProductPrice) as highestProductPrice 
          from orders o) as o on o.salespersonid = s.id 

travail?

+0

J'ai trop simplifié le select. L'original contient quelques champs pour trouver le plus bas de la collection. Cependant, je pourrais les agréger dans la fonction Max(). Je me demande simplement si sélectionner distinct sur S. * causerait un retard plus important. +1 pour être sur la bonne voie. –