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

Langage SQL Discussion :

Gestion de cache d'un SGBD


Sujet :

Langage SQL

  1. #1
    Membre du Club
    Homme Profil pro
    Développeur Delphi
    Inscrit en
    April 2023
    Messages
    207
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Delphi

    Informations forums :
    Inscription : April 2023
    Messages : 207
    Points : 68
    Points
    68
    Par défaut Gestion de cache d'un SGBD
    Bonjour à tous.tes,
    J'ai mis ce titre là parce que je ne savais pas quoi mettre d'autre.
    En fait je me demande ce qui peut faire que l'exécution d'une procédure stockée devienne de plus en plus lente en cours d'exécution.

    Elle met en oeuvre un cursor qui boucle dans un deuxième.

    En clair, ou pas, le premier boucle sur un SELECT de lignes d'entête.
    Chaque ligne de ce premier cursor sert de clé au deuxième cursor qui SELECT un ensemble de lignes liées par ID à l'entête.
    Un nouvel ID est affecté à l'enreg du premier CURSOR et est donc UPDATé dans tous les enreg du deuxième cursor, qui sont ensuite INSERTés dans une table. Puis l'enreg du premier cursor est INSERTé dans une autre table.
    Ni plus ni moins qu'un système d'historisation.

    Le premier boucle sur plusieurs dizaines de milliers de lignes, et le deuxième sur plusieurs centaines de milliers de lignes.

    Donc, c'est long. Mais mon questionnement concerne le fait que, au fur et mesure du traitement, celui-ci est de plus en plus long.
    En effet, le nombre de lignes "détail" par ligne "principales" est en moyenne le même. Le traitement des premiers enregs sont très rapide, et les derniers sont beaucoup plus lents, et de plus en plus lents pendant le traitement.
    D'où ma question :
    Ne serait-il pas possible de faire une opération quelconque, par exemple après chaque INSERT, pour maintenir le même niveau de performance tout au long du traitement ?
    Est ce nécessaire de préciser le SGBD ? Ou le problème, comme je le pense, est inhérent à tous les SGBD.

    Merci d'avance pour vos observations et, au-moins, d'avoir lu jusqu'au bout

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Responsable Données
    Inscrit en
    January 2009
    Messages
    5 148
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Responsable Données

    Informations forums :
    Inscription : January 2009
    Messages : 5 148
    Points : 12 582
    Points
    12 582
    Par défaut
    Bonjour,
    Sans connaitre ni le code, ni la description des tables, ni le SGBD concerné, difficile de répondre.

    Mais j'ai tout de même nue question: plutôt que d'utiliser 2 curseurs imbriqués, n'est-il pas possible d'utiliser des requêtes de type INSERT INTO ... SELECT ?
    Je pense que ce serait bien plus rapide, surtout s'il faut traiter plusieurs milliers de lignes.

    Tatayo.

  3. #3
    Rédacteur

    Avatar de SQLpro
    Homme Profil pro
    Expert bases de données / SQL / MS SQL Server / Postgresql
    Inscrit en
    May 2002
    Messages
    21 621
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : May 2002
    Messages : 21 621
    Points : 51 958
    Points
    51 958
    Billets dans le blog
    3
    Par défaut
    Citation Envoyé par DOliv Voir le message
    ...
    En fait je me demande ce qui peut faire que l'exécution d'une procédure stockée devienne de plus en plus lente en cours d'exécution.

    Elle met en oeuvre un cursor qui boucle dans un deuxième.
    Vous êtes en train de faire de la programmation spaghetti (itérations avec des boucles dans tous les sens) alors qu'un SGBD Relationnel est fait pour exécuter très rapidement (grâce aux index, au parallélisme et à l'optimiseur) des requêtes ensemblistes !

    Dès lors le comportement de votre code sera strictement linéaire et va devenir de plus en plus lent à la charge...

    Citation Envoyé par DOliv Voir le message
    En clair, ou pas, le premier boucle sur un SELECT de lignes d'entête.
    Chaque ligne de ce premier cursor sert de clé au deuxième cursor qui SELECT un ensemble de lignes liées par ID à l'entête.
    Un nouvel ID est affecté à l'enreg du premier CURSOR et est donc UPDATé dans tous les enreg du deuxième cursor, qui sont ensuite INSERTés dans une table. Puis l'enreg du premier cursor est INSERTé dans une autre table.
    NOTE importante : dans un SGBDR le terme "enregistrement" n'a pas sa place. On parle de lignes (rows).
    A lire : https://sqlpro.developpez.com/cours/sqlaz/erreurs/#L2

    Vous êtes typiquement dans le piège RBAR... Row By Agonizing Row... À lire :
    https://www.red-gate.com/simple-talk...agonizing-row/


    Citation Envoyé par DOliv Voir le message
    Ni plus ni moins qu'un système d'historisation.

    Le premier boucle sur plusieurs dizaines de milliers de lignes, et le deuxième sur plusieurs centaines de milliers de lignes.

    Donc, c'est long. Mais mon questionnement concerne le fait que, au fur et mesure du traitement, celui-ci est de plus en plus long.
    Chaque fois que vous exécutez une requête les données sont mise en cache. Une requête elle même se compose de plusieurs phases d'exécution dont certaines parties peuvent comporter des dizaines d'appel à des tables système... Plus vous avancez dans vos boucles, plus il y aura besoin de mémoire et plus cela deviendra lent....

    Citation Envoyé par DOliv Voir le message
    En effet, le nombre de lignes "détail" par ligne "principales" est en moyenne le même. Le traitement des premiers enregs sont très rapide, et les derniers sont beaucoup plus lents, et de plus en plus lents pendant le traitement.
    Tout à fait symptomatique du RBAR...

    Citation Envoyé par DOliv Voir le message
    D'où ma question :
    Ne serait-il pas possible de faire une opération quelconque, par exemple après chaque INSERT, pour maintenir le même niveau de performance tout au long du traitement ?
    Est ce nécessaire de préciser le SGBD ? Ou le problème, comme je le pense, est inhérent à tous les SGBD.
    NON !!!

    Il faut tout simplement faire une seule requête de tout votre traitement....

    Postez votre code que l'on vous aide !

    A +

    PS : certains SGBDR, comme Microsoft SQL Server, sont dotés d'une historisation automatique des lignes des tables. À lire :
    https://blog.developpez.com/sqlpro/p...r-presentation
    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/ * * * * *

  4. #4
    Membre du Club
    Homme Profil pro
    Développeur Delphi
    Inscrit en
    April 2023
    Messages
    207
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Delphi

    Informations forums :
    Inscription : April 2023
    Messages : 207
    Points : 68
    Points
    68
    Par défaut
    Citation Envoyé par tatayo Voir le message
    Bonjour,
    Sans connaitre ni le code, ni la description des tables, ni le SGBD concerné, difficile de répondre.

    Mais j'ai tout de même nue question: plutôt que d'utiliser 2 curseurs imbriqués, n'est-il pas possible d'utiliser des requêtes de type INSERT INTO ... SELECT ?
    Je pense que ce serait bien plus rapide, surtout s'il faut traiter plusieurs milliers de lignes.

    Tatayo.
    Oui mais là, ce n'est pas possible de faire la même chose avec un INSERT INTO ... SELECT.

  5. #5
    Membre du Club
    Homme Profil pro
    Développeur Delphi
    Inscrit en
    April 2023
    Messages
    207
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Delphi

    Informations forums :
    Inscription : April 2023
    Messages : 207
    Points : 68
    Points
    68
    Par défaut
    Intéressant. Je vais déjà lire tout ça et repenser ma copie. Merci à tous.

  6. #6
    Membre du Club
    Homme Profil pro
    Développeur Delphi
    Inscrit en
    April 2023
    Messages
    207
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Delphi

    Informations forums :
    Inscription : April 2023
    Messages : 207
    Points : 68
    Points
    68
    Par défaut
    Mouai... En fait, j'ai beau fouiller toutes les techniques SQL, même avec des CTE à foison, je ne vois pas bien comment il pourrait être possible de sélecter un ensemble de données issu de deux tables différentes, dont les lignes sont liées par un ID.
    Modifier la valeur des IDs de liaison des lignes d'entête ET, évidemment des lignes de détail liées.
    INSERTé le résultat dans deux nouvelles tables.
    Le Tout dans une seule requête

    Alors je pense à un script SQL, peut-être. Mais pourquoi un script SQL serait-il plus rapide qu'une procédure stockée ?

  7. #7
    Expert éminent sénior
    Homme Profil pro
    Responsable Données
    Inscrit en
    January 2009
    Messages
    5 148
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Responsable Données

    Informations forums :
    Inscription : January 2009
    Messages : 5 148
    Points : 12 582
    Points
    12 582
    Par défaut
    Pourquoi avec une seule requête ?
    Tu peux faire l'historisation avec 2 requêtes, une pour les entêtes et une pour les lignes.
    J'imagine bien qu'il y a un "lien" entre la table des entête et son pendant "historisé".
    Donc ça va donner un truc du genre:
    Code sql : 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
     
    -- On commence par historiser les entêtes
    insert into histo_ent(col1,col2,col3...)
    select col1,col2,col3,...
    from ent
    where ...
     
    - Ensuite on historise les lignes correspondantes
    insert into histo_lig(idHistoEnt,colx,coly,col7,...)
    select histo_ent.ident,colx,coly,colz...
    from histo_ent
    inner join ent on ent.id = histo_ent.idEnt
    inner join lig on lig.idEnt = ent.idEnt
    left outer join histo_lig on histo_lig.idHistoEnt = histo_ent.Id
    where ... -- Les même critères que ci-dessus
    and histo_lig.id is null -- On peut aussi tester l'absence de ligne, pour plus de sécurité

    En choisissant le bon niveau d'isolation bien sûr (serializable ?).
    C'est écrit "un peu à la va vite", mais l'idée est là.

    Tatayo.

  8. #8
    Membre du Club
    Homme Profil pro
    Développeur Delphi
    Inscrit en
    April 2023
    Messages
    207
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Delphi

    Informations forums :
    Inscription : April 2023
    Messages : 207
    Points : 68
    Points
    68
    Par défaut
    Merci Tatayo,
    mais une fois les entêtes INSERTés avec le nouvel ID tu ne connais plus le lien avec leurs lignes détail qui contiennent toujours l'ancien ID.
    Sauf erreur de ma part, la seule solution est de updater le nouvel ID affecté à un entête, à chacune des lignes détails de cet entête, avant de passer à l'entête suivant. Sinon le lien est brisé

  9. #9
    Rédacteur

    Avatar de SQLpro
    Homme Profil pro
    Expert bases de données / SQL / MS SQL Server / Postgresql
    Inscrit en
    May 2002
    Messages
    21 621
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : May 2002
    Messages : 21 621
    Points : 51 958
    Points
    51 958
    Billets dans le blog
    3
    Par défaut
    Tout dépend du SGBDR que vous utilisez... Certains SGBDR ont une clause OUTPUT ou équivalent, qui, lors d'une mise à jour renvoie au gré de l'écriture de cette clause, les données avant ou après la mise à jour...

    On peut aussi passer par une construction XML en sortie d'une requête multitable pour alimenter les deux tables en parsant le XML... Facile à faire avec SQL Server par exemple... Clause FOR XML pour générer une sortie XML de la requête au lieu de tabulaire et ensuite parser le XML deux fois, pour chacune des insertions.

    Donc, sans le SGBDR ciblé et sans le DDL des tables difficile de te répondre !

    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/ * * * * *

  10. #10
    Expert éminent sénior
    Homme Profil pro
    Responsable Données
    Inscrit en
    January 2009
    Messages
    5 148
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Responsable Données

    Informations forums :
    Inscription : January 2009
    Messages : 5 148
    Points : 12 582
    Points
    12 582
    Par défaut
    Citation Envoyé par DOliv Voir le message
    Merci Tatayo,
    mais une fois les entêtes INSERTés avec le nouvel ID tu ne connais plus le lien avec leurs lignes détail qui contiennent toujours l'ancien ID.
    Sauf erreur de ma part, la seule solution est de updater le nouvel ID affecté à un entête, à chacune des lignes détails de cet entête, avant de passer à l'entête suivant. Sinon le lien est brisé
    Tu n'as pas de lien entre un entête et son historique ?
    Quand je mets en place une table d'historique, je garde toujours un lien entre la ligne d'historique et la ligne historisé.

    Si ce lien existe bel et bien, à partir d'un historique d'entête tu peux "remonter" à l'entête d'origine, puis au lignes à historiser et en ayant les identifiant qui vont bien.
    Et là tu as bien l'identifiant de l'entête d'origine et celui de la "version" historisé.

    Tatayo.

  11. #11
    Membre du Club
    Homme Profil pro
    Développeur Delphi
    Inscrit en
    April 2023
    Messages
    207
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Delphi

    Informations forums :
    Inscription : April 2023
    Messages : 207
    Points : 68
    Points
    68
    Par défaut
    Merci SQLpro. Très intéressant, je vais étudier ça.
    Non tatayo, dans mon cas j'aurai peut-être dû parler d'archivage plutôt que d'historisation. Quoi que, dans ma tête, c'est un peu la même chose.
    En fait, en continuant de schématiser, on peut dire que seul le lien ligne "maîtresse" lignes "détails" est conservé. Aucun lien utile entre les tables d'origine et les tables de destination.

  12. #12
    Modérateur
    Avatar de Waldar
    Homme Profil pro
    Customer Success Manager @Vertica
    Inscrit en
    September 2008
    Messages
    8 435
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Customer Success Manager @Vertica
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : September 2008
    Messages : 8 435
    Points : 17 721
    Points
    17 721
    Par défaut
    Toujours sans un exemple un peu plus détaillé c'est difficile de vous aider, mais je vois trois étapes dans votre traitement :
    1. Conserver le lien entête détail dans une table de travail.
    2. Modifier vos entêtes.
    3. Modifier votre détail.

  13. #13
    Modérateur
    Avatar de escartefigue
    Homme Profil pro
    bourreau
    Inscrit en
    March 2010
    Messages
    9 823
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loir et Cher (Centre)

    Informations professionnelles :
    Activité : bourreau
    Secteur : Finance

    Informations forums :
    Inscription : March 2010
    Messages : 9 823
    Points : 37 433
    Points
    37 433
    Billets dans le blog
    7
    Par défaut
    Voici un exemple possible, qui s'appuie sur une table de renumérotation :

    Code SQL : 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
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    -- création du jeu d'essai source
       create table T1ENT 
             (  T1ID  integer primary key
              , T1CH  char(4) not null
             )
       ; 
       create table T2DTL
             (  T1ID  integer 
              , T2DT  date    not null
              , PRIMARY KEY (T1ID, T2DT)
              , FOREIGN KEY (T1ID) REFERENCES T1ENT(T1ID)
             )
       ; 
       insert into T1ENT (T1ID, T1CH)
       values (1, 'truc')
            , (2, 'azer')
       ;
       insert into T2DTL (T1ID, T2DT)
       values (1, '2023-02-14')
            , (1, '2023-05-20')
            , (2, '2022-11-03')
            , (2, '2022-12-15')
            , (2, '2022-12-28')
       ;
    -- création de la table de renumérotation ID source <-> ID cible
    -- la contrainte PK garantit l'unicité de l'ID source
    -- la contrainte UNIQUE garantit l'unicité de l'ID cible
       create table T3RENUM 
             (  T3IDOLD  integer primary key
              , T3IDNEW  integer not null
              , UNIQUE (T3IDNEW)
             )
       ;  
       insert into T3RENUM (T3IDOLD, T3IDNEW)
       values (1, 11)
            , (2, 03)
       ;
    -- creation des tables cibles
       create table T1BENT 
             (  T1ID  integer primary key
              , T1CH  char(4) not null
             )
       ; 
       create table T2BDTL
             (  T1ID  integer 
              , T2DT  date    not null
              , PRIMARY KEY (T1ID, T2DT)
              , FOREIGN KEY (T1ID) REFERENCES T1BENT(T1ID)
             )
       ; 
    -- alimentation des tables cibles avec ID renumérotés
       insert into T1BENT (T1ID, T1CH)
       select T3IDNEW, T1CH
       from T1ENT as T1
       inner join T3RENUM as T3
          on T3.T3IDOLD=T1.T1ID
       ;
       insert into T2BDTL (T1ID, T2DT)
       select T3IDNEW, T2DT
       from T2DTL T2
       inner join T3RENUM T3
          on T3.T3IDOLD=T2.T1ID
       ;
     
    -- contrôle des résultats
       select * from T1BENT
       ;
       select * from T2BDTL
       ;

    Résultat (on a bien conservé les liens entre entête et détail, mais avec les nouveaux identifiants) :

    T1ID T1CH
    3 azer
    11 truc

    T1ID T2DT
    3 2022-11-03
    3 2022-12-15
    3 2022-12-28
    11 2023-02-14
    11 2023-05-20


    Si la renumérotation est un simple calcul (par exemple ajout de 1000 à chaque identifiant), alors c'est encore plus facile, la table de renumérotation est inutile, le nouvel ID peut être calculé dans la requête.
    Mais attention, cette deuxième méthode n'est pas applicable avec Access ou MYSQL qui ne sont pas ensemblistes...

  14. #14
    Expert éminent
    Avatar de Séb.
    Profil pro
    Inscrit en
    March 2005
    Messages
    4 937
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : March 2005
    Messages : 4 937
    Points : 7 834
    Points
    7 834
    Billets dans le blog
    17
    Par défaut
    Si la renumérotation est un simple calcul (par exemple ajout de 1000 à chaque identifiant), alors c'est encore plus facile, la table de renumérotation est inutile, le nouvel ID peut être calculé dans la requête.
    Mais attention, cette deuxième méthode n'est pas applicable avec Access ou MYSQL qui ne sont pas ensemblistes...
    C'est faisable avec MySQL à condition de spécifier un ordre d'opération :

    UPDATE ma_table
    SET id = id + 1000
    ORDER BY id DESC;
    Les ID les plus grands seront traités en premier => Pas de collision
    Un problème exposé clairement est déjà à moitié résolu
    Keep It Smart and Simple

Discussions similaires

  1. Gestion du cache en Load Balancing
    Par loic_86 dans le forum ASP.NET
    Réponses: 1
    Dernier message: 10/09/2007, 11h00
  2. [cache] Gestion du cache en général
    Par Maxoo dans le forum Balisage (X)HTML et validation W3C
    Réponses: 7
    Dernier message: 15/12/2006, 11h21
  3. Gestion du cache d'un ResulSet
    Par skunkies dans le forum JDBC
    Réponses: 1
    Dernier message: 30/10/2006, 19h12
  4. [Sécurité] Gestion du cache / cookies
    Par dug dans le forum Langage
    Réponses: 4
    Dernier message: 25/01/2006, 22h17
  5. [Xml][Memoire] gestion du cache
    Par tatou42 dans le forum Format d'échange (XML, JSON...)
    Réponses: 11
    Dernier message: 21/09/2005, 18h48

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