2010-03-08 6 views
1

Très bien, ce problème est un peu compliqué, alors supportez-moi.Agrégat semi-complexe Sélectionnez déclaration confusion

J'ai une table pleine de données. Une des colonnes de la table est une EntryDate. Il peut y avoir plusieurs entrées par jour. Cependant, je veux sélectionner toutes les lignes qui sont la dernière entrée sur leurs jours respectifs, et je veux sélectionner toutes les colonnes de cette table.

Une des colonnes est une colonne d'identifiant unique, mais ce n'est pas la clé primaire (je ne sais pas pourquoi c'est là, c'est un système assez ancien). Aux fins de démonstration, par exemple la table ressemble à ceci:

create table ExampleTable (
    ID int identity(1,1) not null, 
    PersonID int not null, 
    StoreID int not null, 
    Data1 int not null, 
    Data2 int not null, 
    EntryDate datetime not null 
) 

La clé primaire est PersonID et StoreID, qui définit logiquement l'unicité. Maintenant, comme je l'ai dit, je veux sélectionner toutes les lignes qui sont les dernières entrées ce jour-là (pour chaque combinaison Person-Store). Ceci est assez facile:

--Figure 1 
select PersonID, StoreID, max(EntryDate) 
from ExampleTable 
group by PersonID, StoreID, dbo.dayof(EntryDate) 

Où dbo.dayof() est une fonction simple que les bandes de la composante temporelle d'un datetime. Cependant, cela perd le reste des colonnes! Je ne peux pas simplement inclure les autres colonnes, car alors je devrais les group by, ce qui produirait de mauvais résultats (d'autant plus que ID est unique).

J'ai trouvé une bidouille qui va faire ce que je veux, mais il doit y avoir une meilleure façon - voici ma solution actuelle:

select 
    cast(null as int) as ID, 
    PersonID, 
    StoreID, 
    cast(null as int) as Data1, 
    cast(null as int) as Data2, 
    max(EntryDate) as EntryDate 
into #StagingTable 
from ExampleTable 
group by PersonID, StoreID, dbo.dayof(EntryDate) 

update Target set 
    ID = Source.ID, 
    Data1 = Source.Data1, 
    Data2 = Source.Data2, 
from #StagingTable as Target 
inner join ExampleTable as Source 
    on Source.PersonID = Target.PersonID 
    and Source.StoreID = Target.StoreID 
    and Source.EntryDate = Target.EntryDate 

Cela me récupère les données correctes dans #StagingTable mais, eh bien, Regarde ça! Créer une table avec des valeurs nulles, puis faire une mise à jour pour récupérer les valeurs - il y a sûrement une meilleure façon de faire cela? Une déclaration unique qui va me donner toutes les valeurs la première fois?

Je crois que la jointure correcte sur ce select original (figure 1) ferait l'affaire, comme une auto-jointure ou quelque chose ... mais comment faites-vous cela avec la clause group by? Je ne trouve pas la bonne syntaxe pour exécuter la requête. Je suis assez nouveau avec SQL, il est donc probable que je manque quelque chose d'évident. Aucune suggestion?

(Travailler dans T-SQL, si cela fait une différence)

Répondre

2

Il n'y a pas vraiment moyen "élégant". Lorsque vous avez des requêtes de groupe par groupe comme ceci, vous aurez des sous-requêtes ou des tables temporaires.

Cela fonctionne:

Select ID, A.PersonID, A.StoreID, Data1, Data2, A.EntryDate 
From ExampleTable As A 
Inner Join 
    (select PersonID, StoreID, max(EntryDate) As EntryDate 
    from ExampleTable 
    group by PersonID, StoreID, dbo.dayof(EntryDate)) As B 
    On ExampleTable.PersonID = B.PersonID 
    And ExampleTable.StoreID = B.StoreID 
    And ExampleTable.EntryDate = B.EntryDate 

Vous ne devriez pas être trop bas sur la solution que vous est venu avec bien. En utilisant des tables temporaires jamais semble élégant, mais il est efficace; Je ne serais pas surpris si votre solution originale en deux étapes est plus rapide que ma solution en une seule étape. (vous aurez à tester pour savoir à coup sûr.)

+0

Merci pour le heads up sur la performance. Je l'ai essayé dans les deux sens, et vous avez raison - la méthode de table de temp a terminé 1 seconde plus vite (83 vs 84 secondes au total)! Mais je vais prendre la concision de votre solution sur ce coup de pouce (presque) n'importe quel jour ... –