2009-05-19 5 views
0

Chaque fois un certain temps j'ai un scénario comme celui-ci, et ne peut jamais arriver à la requête la plus efficace pour tirer dans les informations:regroupement T-SQL question

Disons que nous avons une table avec trois colonnes (Un int, B int, C int). Ma requête doit répondre à une question comme celle-ci: "Dites-moi quelle est la valeur de la colonne C pour la plus grande valeur de la colonne B où A = 5." Un scénario réel pour quelque chose comme ceci serait «A» est vos utilisateurs, «B» est la date à laquelle quelque chose est arrivé, et «C» est la valeur, où vous voulez l'entrée la plus récente pour un utilisateur spécifique.

Je finis toujours avec une requête comme ceci:

SELECT 
    C 
FROM 
    MyTable 
WHERE 
    A = 5 
    AND B = (SELECT MAX(B) FROM MyTable WHERE A = 5) 

Qu'est-ce que je manque de le faire en une seule requête (plutôt que de les nicher)? Une sorte de clause 'Ayant'?

Répondre

4

La réponse de BoSchatzberg fonctionne quand vous tenir compte que du 1 résultat où A = 5. Mais je soupçonne que cette question est le résultat d'un cas plus général. Que faire si vous voulez lister l'enregistrement supérieur pour chaque valeur distincte de A?

SELECT t1.* 
FROM MyTable t1 
    INNER JOIN 
    (
     SELECT A, MAX(B) 
     FROM MyTable 
     GROUP BY A 
    ) t2 ON t1.A = t2.A AND t1.B = t2.B 
+0

Comme le PO n'a besoin que de l'entrée la plus récente, c'est probablement la meilleure façon de le faire. –

+0

Merci - Je commençais juste à penser à un problème très similaire. J'aurais probablement pu le faire moi-même, mais pas aussi vite que de le lire ici ... – RolandTumble

1

Je pense que vous êtes proche (et ce que vous avez travaillerait). Vous pouvez utiliser quelque chose comme ce qui suit:

select C 
    , max(B) 
    from MyTable 
where A = 5 
group by C 
+0

En fait, je ne pense pas que ça va marcher. J'ai exécuté un test dessus dans T-SQL et il m'a donné le "non contenu dans une fonction d'agrégat ou la clause GROUP BY" pour C. –

+0

Je suis assez sûr que cette déclaration fonctionnerait. C'est plutôt simple. Quelqu'un peut-il m'expliquer pourquoi cela ne fonctionnerait pas? – northpole

+0

Ah ... raté le 'groupe par'. Ouaip. Ça fera l'affaire. –

-1

Après un peu de tests, je ne pense pas que cela peut être fait sans elle faire la façon dont vous déjà le faire (par exemple une sous-requête). Puisque vous avez besoin du maximum de B et que vous ne pouvez pas obtenir la valeur de C sans inclure cela dans une clause GROUP BY ou HAVING, une sous-requête semble être la meilleure.

create table #tempints (
    a int, 
    b int, 
    c int 
    ) 

    insert into #tempints values (1, 8, 10) 
    insert into #tempints values (1, 8, 10) 
    insert into #tempints values (2, 4, 10) 
    insert into #tempints values (5, 8, 10) 
    insert into #tempints values (5, 3, 10) 
    insert into #tempints values (5, 7, 10) 
    insert into #tempints values (5, 8, 15) 

    /* this errors out with "Column '#tempints.c' is invalid in the select list because it is not contained in either an 
    aggregate function or the GROUP BY clause." */ 
    select t1.c, max(t1.b) 
    from #tempints t1 
    where t1.a=5 

    /* this errors with "An aggregate may not appear in the WHERE clause unless it is in a subquery contained in a HAVING 
    clause or a select list, and the column being aggregated is an outer reference." */ 
    select t1.c, max(t1.b) 
    from #tempints t1, #tempints t2 
    where t1.a=5 and t2.b=max(t1.b) 

    /* errors with "Column '#tempints.a' is invalid in the HAVING clause because it is not contained in either an aggregate 
function or the GROUP BY clause." */ 
    select c 
    from #tempints 
    group by b, c 
    having a=5 and b=max(b) 

    drop table #tempints 
2

Vous pouvez le faire:

SELECT TOP 1 C 
FROM MyTable 
WHERE A = 5 
ORDER BY b DESC 
+0

Belle solution! Je me demande si c'est le plus efficace ou l'original affiché dans la question. – Vizu

2
-- 
SELECT C 
FROM MyTable 
INNER JOIN (SELECT A, MAX(B) AS MAX_B FROM MyTable GROUP BY A) AS X 
    ON MyTable.A = X.A 
    AND MyTable.B = MAX_B 
-- 
WHERE MyTable.A = 5 

Dans ce cas, la première partie (entre les commentaires) peuvent également être facilement déplacé dans une vue de modularité ou de réutilisation.