IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Développement SQL Server Discussion :

[2005] ROW_NUMBER peu performant


Sujet :

Développement SQL Server

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éprouvé

    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    1 448
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 448
    Par défaut [2005] ROW_NUMBER peu performant
    Bonjour,

    En travaillant sur un query j'ai été surpris de constaté la lenteur d'une approche avec ROW_NUMBER() et j'aimerais comprendre le pourquoi de cette lenteur.

    Le but de mon query est de me permettre de retrouver dans une table d'handicaps de golf, l'entrée la plus récente pour chaque membre.

    La table :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
     
    CREATE TABLE [dbo].[tHandicaps](
    	[Id] [bigint] IDENTITY(1,1) NOT NULL,
    	[IdMember] [bigint] NOT NULL,
    	[EntryDate] [datetime] NOT NULL,
    	[Handicap] [decimal](4, 1) NOT NULL,
    	[OriginClubId] [int] NOT NULL,
    	[Formula] [nvarchar](50) NOT NULL,
    	[Stableford] [tinyint] NULL,
    	[Csa] [tinyint] NULL,
    	[CsaStableford] [tinyint] NULL,
    	[Remark] [nvarchar](200) NOT NULL,
    	[IdCompetition] [int] NULL,
    	[QualificationStatus] [tinyint] NOT NULL CONSTRAINT [DF_tHandicaps_QualificationStatus]  DEFAULT ((3)),
    	[ForeignKey] [bigint] NULL,
    	[TypeOfAdjustement] [tinyint] NOT NULL DEFAULT ((1)),
    	[EventDate] [datetime] NULL,
    	[NbHoles] [tinyint] NULL,
    	[OldHcp] [decimal](4, 1) NOT NULL,
    	[roundID] [int] NULL,
     CONSTRAINT [PK_tHandicaps] PRIMARY KEY CLUSTERED 
    (
    	[Id] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 90) ON [PRIMARY]
    ) ON [PRIMARY]
     
    GO
    ALTER TABLE [dbo].[tHandicaps]  WITH CHECK ADD  CONSTRAINT [FK_tHandicaps_tClubs] FOREIGN KEY([OriginClubId])
    REFERENCES [dbo].[tClubs] ([Id])
    GO
    ALTER TABLE [dbo].[tHandicaps] CHECK CONSTRAINT [FK_tHandicaps_tClubs]
    GO
    ALTER TABLE [dbo].[tHandicaps]  WITH CHECK ADD  CONSTRAINT [FK_tHandicaps_tMembers] FOREIGN KEY([IdMember])
    REFERENCES [dbo].[tMembers] ([Id])
    GO
    ALTER TABLE [dbo].[tHandicaps] CHECK CONSTRAINT [FK_tHandicaps_tMembers]
    Pour un membr, l'entrée la plus récente est celle de "ses" entrées ayant la valeur EntryDate la plus élevée (et en cas d'égalité, ayant l'id le plus élevé parmi les ex aequo).

    J'ai, en plus de l'indexe clustered de la PK, deux indexes sur cette table dont un particulièrement approprié pour ce type de requête :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    CREATE NONCLUSTERED INDEX [IDX_IdMember_EntryDate] ON [dbo].[tHandicaps] 
    (
    	[IdMember] ASC,
    	[EntryDate] ASC
    )
    INCLUDE ( [TypeOfAdjustement]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    Et un autre qui, je le pense, n'a aucun impact dans tout ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    CREATE NONCLUSTERED INDEX [IDX_OriginClubId_roundID_IdMember] ON [dbo].[tHandicaps] 
    (
    	[OriginClubId] ASC,
    	[roundID] ASC,
    	[IdMember] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    Passons aux queries.

    Un query rapide < 1s (testé pour obtenir le dernier handicap des membres 2 et 3) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    WITH OrderedLocalHCP (
    	idMember
    	, lochcp
    	, hcpdate
    )
    AS
    (
    	SELECT 
    		IdMember
    		, Handicap
    		, EntryDate
    	FROM dbo.tHandicaps AS HCP
    	WHERE NOT EXISTS (
    		SELECT TOP(1) 1
    		FROM dbo.tHandicaps AS Hcp2
    		WHERE Hcp2.IdMember = Hcp.IdMember
    		AND	(
    			Hcp2.EntryDate > Hcp.EntryDate
    			OR (
    				Hcp2.EntryDate = Hcp.EntryDate
    				AND
    				Hcp2.id > Hcp.id
    			)
    		)
    	)
    )
    
    SELECT *
    FROM OrderedLocalHCP
    WHERE idMember in (2, 3)
    La version ROW_NUMBER lente > 14s (testé pour obtenir le dernier handicap des membres 2 et 3) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    WITH OrderedLocalHCP (
    	idMember
    	, lochcp
    	, hcpdate
    )
    AS
    (
    	SELECT 
    		IdMember
    		, Handicap
    		, EntryDate
    	FROM (
    		SELECT 
    			IdMember
    			, Handicap
    			, EntryDate
    			, ROW_NUMBER() OVER (PARTITION BY IdMember ORDER BY EntryDate DESC, id DESC) AS Row
    		FROM dbo.tHandicaps AS HCP
    	) AS Ext
    	WHERE Row = 1
    )
    
    SELECT *
    FROM OrderedLocalHCP
    WHERE idMember in (2, 3)

    Voilà.

    Je ne comprends pas pourquoi la formule en ROW_NUMBER() ne semble pas tirer le moindre profit des indexes.
    Car j'aurais pensé que du fait de la partition (PARTITION BY IdMember), la clause "WHERE idMember in (2, 3)" soit prise en compte au niveau même du ROW_NUMBER().

    Merci de partager avec moi vos idées sur le sujet.

  2. #2
    Rédacteur

    Avatar de SQLpro
    Homme Profil pro
    Expert bases de données / SQL / MS SQL Server / Postgresql
    Inscrit en
    Mai 2002
    Messages
    21 999
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Expert bases de données / SQL / MS SQL Server / Postgresql
    Secteur : Conseil

    Informations forums :
    Inscription : Mai 2002
    Messages : 21 999
    Billets dans le blog
    6
    Par défaut
    Pour qu'il soit efficace votre index devrait être (IdMember, EntryDate, id).

    A +
    Frédéric Brouard - SQLpro - ARCHITECTE DE DONNÉES - expert SGBDR et langage SQL
    Le site sur les SGBD relationnels et le langage SQL: http://sqlpro.developpez.com/
    Blog SQL, SQL Server, SGBDR : http://blog.developpez.com/sqlpro
    Expert Microsoft SQL Server - M.V.P. (Most valuable Professional) MS Corp.
    Entreprise SQL SPOT : modélisation, conseils, audit, optimisation, formation...
    * * * * * Expertise SQL Server : http://mssqlserver.fr/ * * * * *

  3. #3
    Membre éprouvé

    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    1 448
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 448
    Par défaut
    Non.

    Mais, aujourd'hui j'ai eu confirmation de ce que je suspectais (avec confirmation de ma troisième conclusion).

    Merci à Adam Haines, et en particulier à son article au titre évocateur What You Will Not Learn In The Class Room.

Discussions similaires

  1. [2005] ROW_NUMBER tueur de vitesse ?!
    Par Sergejack dans le forum Développement
    Réponses: 13
    Dernier message: 07/10/2009, 15h13
  2. Réponses: 2
    Dernier message: 01/08/2006, 10h20
  3. Réponses: 2
    Dernier message: 24/05/2006, 13h30

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo