J'ai une question relative à T-SQL et SQL Server.
Disons que j'ai des commandes de table avec 2 colonnes: p>
Je veux la date du premier ordre pour chaque produit, donc j'effectue ce type de requête: p> J'ai un index sur Maintenant, mon problème est que je veux aussi Pour récupérer le Cela devrait simplement être possible en utilisant le même balayage d'index non clustered, suivi des agrégats de flux, mais je ne peux pas sembler trouver une requête qui le ferait. Aucune idée? P> merci p> p> ProductID code>, y compris les colonnes CustomerID code> et date code> pour accélérer la requête ( ix_orders code>). Le plan de requête ressemble à une analyse d'index non clustere sur ix_orders code>, suivie d'un agrégat de flux (sans trier grâce à l'index). P> CustomerID code> associé à la première commande pour chaque produit (le produit 26 a été commandé pour la première fois le mardi 25, par le client 12). La partie délicate est que je ne veux pas de boucle intérieure dans le plan d'exécution, car cela signifierait une lecture supplémentaire par ProductID code> dans le tableau, qui est hautement inefficace. P>
6 Réponses :
SELECT
o1.productid,
o1.date,
o1.customerid
FROM
Orders o1
JOIN
(select productid, min(date) as orderDate
from Orders
group by productid
) firstOrder
ON o1.productid = firstOrder.productid
This is the best I can come up with though to be honest, I don't know what the performance characteristics of this query are. If it's no good, I'd probably suggest running two queries to get the information you want.
+1: Je pense que vous devez définir un alias pour min (date) à l'intérieur de votre jointure anonyme; Sinon, c'est exactement ce que j'ai eu. Serait bon de savoir s'il y a une meilleure approche à ce sujet.
Cette requête obtient la mauvaise réponse car elle n'inclut pas la date dans la jointure entre O1 et FirstOrder
Vous n'avez pas besoin d'inclure la date dans la jointure. Vous n'avez besoin que de l'ID de produit de la sous-requête, car cela a la date minimale associée à celle-ci. Le retour de la sélection des résultats réels renvoie la date.
declare @Orders table (
ProductId int,
CustomerId int,
Date datetime
)
insert into @Orders values (1,1,'20090701')
insert into @Orders values (2,1,'20090703')
insert into @Orders values (3,1,'20090702')
insert into @Orders values (1,2,'20090704')
insert into @Orders values (4,2,'20090701')
insert into @Orders values (1,3,'20090706')
insert into @Orders values (2,3,'20090704')
insert into @Orders values (4,3,'20090702')
insert into @Orders values (5,5,'20090703')
select O.* from @Orders O inner join
(
select ProductId,
MIN(Date) MinDate
from @Orders
group by ProductId
) FO
on FO.ProductId = O.ProductId and FO.MinDate = O.Date
The estimated query plan for this is useless as I'm mocking it with table variables, but the anonymous inner join should be optimised over a subselect.
Votre sélection doit inclure FO.Minder.
Je n'ai jamais entendu parler d'une jointure "anonyme", j'ai toujours utilisé la table dérivée du terme.
Cela ne fonctionnera pas s'il y a plusieurs rangées avec la même date minimale du même produit. Essayez-le, ajoutez ce code à l'exemple: Insérer dans les valeurs @orders (5,1, '20090703'); Insérer dans les valeurs @orders (5,5, '20090703') i> Vous obtiendrez un produit 5 fois plusieurs fois dans le jeu de résultats.
Mais n'est-ce pas valide? Vous souhaitez que l'ID client du premier produit pour tout produit, mais compte tenu des données, si plusieurs clients commandent le même produit le même jour, je dirais que vous devriez les récupérer. Si un client commande le même produit deux fois, cela ne devrait peut-être pas arriver - vous pourriez faire le O. * distinct code> dans ce cas.
OP dit "Je veux la date du premier ordre pour chaque produit", c'est-à-dire que signifie seulement énumérer le produit une fois. L'OP souhaite également le client qui l'a commandé. Si l'OP utilise des colonnes DateTime avec des temps réelles (pas seulement des jours tels que dans vos données de test), vous pouvez probablement obtenir le premier, car deux commandes simultanées en même temps sont une faible probabilité, mais peut encore être possible. Je penserais que s'il y a une cravate, le même produit commandé en même temps, OP ne veut toujours que une rangée de la série de résultats, mais c'est ma prise.
Je pense que le point est que l'OP veut plus B> que la date du premier ordre pour chaque produit: "Je souhaite également récupérer le CustomerID associé à la première commande pour chaque produit". Dans ce cas, l'OP doit déterminer comment i> ils choisiront une seule réponse lorsque deux sont également valables; Cela pourrait être aussi simple que celui avec la clientèle la plus basse.
Seul l'OP peut répondre à ce qu'ils sont après. Cependant, ils n'ont pas dit qu'ils voulaient la première date pour chaque client. Dans ma solution, je ne renvoie qu'une ligne par produit et je parsemé des cravates en utilisant la clientèle la plus basse.
est ix_orders triés par le producteur, puis cutomerid, puis date ou est-ce le produit, puis la date, puis la clientèle? Si c'est le premier changement à ce dernier.
En d'autres termes, n'utilisez pas ceci: p> Utilisez cela à la place: p> WITH cte AS
(
SELECT ProductID, CustomerID, Date,
ROW_NUMBER() OVER(PARTITION BY ProductID ORDER BY Date ASC) AS row
FROM [Order]
)
SELECT ProductID, CustomerId, Date
FROM cte
WHERE row = 1
ORDER BY ProductID
Ceci gérera des produits qui ont des dates en double:
;WITH MinOrders AS
(SELECT
o.ProductId, o.CustomerId, o.Date
,row_number() over(partition by o.ProductId order by o.ProductId,o.CustomerId) AS RankValue
FROM @Orders o
)
SELECT
m.ProductId, m.CustomerId, m.Date
FROM MinOrders m
WHERE m.RankValue=1
ORDER BY m.ProductId, m.CustomerId
Bien, il n'y a qu'un seul scan d'index, mais cela donne une sorte dans le plan d'exécution de la requête cependant.
Je ne vois pas une façon de le faire bien sans faire une sous-requête ou une fonction de fenêtres (telle que Row_Number, grade), car le maximum ne regarde que dans une colonne.
Cependant, vous pouvez le faire non bien. P>
SELECT
productid,
min(date),
cast(
substring(
min(convert(varchar(23),date,21) + cast(customerid as varchar(20)))
, 24, 44)
as int) customerid
from
orders
group by
productid
MSG 8120, niveau 16, état 1, ligne 51 colonne '@ ordres.productid' n'est pas valide dans la liste Select, car il n'est pas contenu dans une fonction globale ou le groupe par clause.
Oups, j'ai oublié d'ajouter le groupe par clause
in nominalement, le plan de la requête contient En fait, la deuxième opération est presque gratuite, car la page d'index utilisée dans la boucle interne résidera probablement dans le cache Parce qu'il venait d'être utilisé pour la boucle extérieure. p> Voici le résultat du test sur SQL Server 2005 + code>: boucles imbriquées code>. P> Cependant, la boucle externe utilisera une analyse index code> avec agrégate de flux code> et la boucle interne contiendra un Index recherche code> pour le ProductID code> avec un haut code>. P> 1 000 000 code> lignes (avec 100 code> distinct code> ProductID CODE> 'S): P> SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 1 ms.
(ÑÑÑок обÑабоÑано: 100)
Table 'Orders'. Scan count 1, logical reads 5123, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 406 ms, elapsed time = 415 ms.
Besoin de changer d'où: où Oi.productid = OD.Productid. Mon plan de requête a montré une boucle de tri et imbriquée.
Je reçois msg 4104, niveau 16, état 1, ligne 2 L'identifiant multi-pièces "oo.productid" n'a pas pu être lié. I> Pour résoudre le changement lorsque le titre de @shannon suggère.
Droite, j'ai oublié de le réparer. @shannon Code>: Avez-vous créé l'index en tant que @op code> dit? Créer index ix_orders_pdc sur les commandes (productide, date, clientId) code>
Mon indice était (Producteur, Date, CustomerID) Je vérifierai avec (productid, date, CustomerID)
@Quassnoi: déplacer les colonnes de l'index, j'ai perdu le tri. Toujours la boucle avec une recherche, comme vous soulignez votre réponse.