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 :

Problème avec une requête récalcitrante


Sujet :

Développement SQL Server

  1. #1
    Membre expérimenté
    Profil pro
    Inscrit en
    Février 2004
    Messages
    1 824
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2004
    Messages : 1 824
    Points : 1 544
    Points
    1 544
    Par défaut Problème avec une requête récalcitrante
    Bonjour à tous,

    J'ai un souci avec une requête qui demande beaucoup de ressources à s'exécuter.

    Voici un exemple de ce que j'essaye de faire.

    Admettons on a une table
    - t_clients (id)
    - t_commandes (id, client_id, date_validation)

    Et on veut supprimer les commandes dont la date de validation est avant le 1er janvier 2015, et supprimer les clients qui n'ont pas de commande après.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    DELETE FROM t_commandes WHERE t_commandes.date_validation < '2015-01-01';
    DELETE FROM t_clients WHERE NOT EXISTS (SELECT * FROM t_commandes WHERE t_commandes .client_id = t_clients.id);
    La seconde requête exploite à fond le fichier de log, jusqu'à ce que l'OS manifeste qu'il n'y a bientôt plus d'espace disque et ensuite c'est SQL Serveur qui lance une erreur "impossible d'agrandir le fichier de log".

    Est-ce qu'il n'y a pas moyen de contourner ce "WHERE NOT EXISTS" ? On peut utiliser des procédures, des tables temporaires etc.

    Merci beaucoup par avance,

    A bientôt
    "Heureusement qu'il y avait mon nez, sinon je l'aurais pris en pleine gueule" Walter Spanghero

  2. #2
    Modérateur

    Profil pro
    dba
    Inscrit en
    Janvier 2010
    Messages
    5 643
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : dba

    Informations forums :
    Inscription : Janvier 2010
    Messages : 5 643
    Points : 13 092
    Points
    13 092
    Par défaut
    Bonjour,

    Si c'est le fichier de log qui sature, alors remplacer le NOT EXISTS ne changera pas grand chose.

    Quelle est la taille du fichier de log ? la taille disponible sur le disque ? quel est le mode de récupération de votre base ?
    Quel est le nombre de clients qui devraient être supprimés par votre requete ?

  3. #3
    Membre expérimenté
    Profil pro
    Inscrit en
    Février 2004
    Messages
    1 824
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2004
    Messages : 1 824
    Points : 1 544
    Points
    1 544
    Par défaut
    Citation Envoyé par aieeeuuuuu Voir le message
    Bonjour,

    Si c'est le fichier de log qui sature, alors remplacer le NOT EXISTS ne changera pas grand chose.

    Quelle est la taille du fichier de log ? la taille disponible sur le disque ? quel est le mode de récupération de votre base ?
    Quel est le nombre de clients qui devraient être supprimés par votre requete ?
    J'ai fait un "shrink" du fichier de log pour le ramener à 1Mo, et la taille disponible sur le disque est de 11Go (c'est une machine virtuelle).

    Il y a à peu d'un milliard de commandes, dont 90% à supprimer, et un peu moins en client mais avec le même rapport, 90%.

    (Les clients et les commandes étaient un exemple pour poser deux tables qui se joignent)
    "Heureusement qu'il y avait mon nez, sinon je l'aurais pris en pleine gueule" Walter Spanghero

  4. #4
    Invité
    Invité(e)
    Par défaut
    Fais ça par petit paquet genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    select 1
    while @@rowcount > 0
            DELETE TOP(50000) FROM t_commandes WHERE t_commandes.date_validation < '2015-01-01';
     
    select 1
    while @@rowcount > 0
            DELETE TOP(50000) FROM t_clients WHERE NOT EXISTS (SELECT * FROM t_commandes WHERE t_commandes .client_id = t_clients.id);

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

    Informations professionnelles :
    Activité : bourreau
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2010
    Messages : 10 134
    Points : 38 557
    Points
    38 557
    Billets dans le blog
    9
    Par défaut
    Il n'est pas raisonnable de supprimer 90% des enregistrements d'une table d'un milliard de lignes par opération delete :
    - delete charge inutilement la log ce qui ralentit l'opération et pénalise les autres transactions
    - delete est lent puisqu'il affecte data et index
    - delete desorganise le tablespace

    Pour cette volumétrie, je décharge la table, traite le fichier unload pour filtrer ce qui est à conserver, trie le fichier résultant sur l'index cluster, et recharge la table en mode replace en activant les statistiques
    Ainsi, les données sont purgées, la table est réorganisée, les stats sont à jour et la log n'a pas bougé

    Traitement à lancer hors autres activités batch et TP sur la table bien sur

  6. #6
    Membre expérimenté
    Profil pro
    Inscrit en
    Février 2004
    Messages
    1 824
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2004
    Messages : 1 824
    Points : 1 544
    Points
    1 544
    Par défaut
    Citation Envoyé par 7gyY9w1ZY6ySRgPeaefZ Voir le message
    Fais ça par petit paquet genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    select 1
    while @@rowcount > 0
            DELETE TOP(50000) FROM t_commandes WHERE t_commandes.date_validation < '2015-01-01';
     
    select 1
    while @@rowcount > 0
            DELETE TOP(50000) FROM t_clients WHERE NOT EXISTS (SELECT * FROM t_commandes WHERE t_commandes .client_id = t_clients.id);
    Ça ça a l'air pas mal, je connaissais pas DELETE TOP(X).

    En revanche, ne faut-il pas rajouter des COMMIT dans les boucles ?

    Sinon ça risque de revenir au même, dans qu'une transaction n'a pas été validée, ça conserve "quelque part" les impacts des opérations pour faire un rollback si un problème survient. Enfin je pense..
    "Heureusement qu'il y avait mon nez, sinon je l'aurais pris en pleine gueule" Walter Spanghero

  7. #7
    Membre expérimenté
    Profil pro
    Inscrit en
    Février 2004
    Messages
    1 824
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2004
    Messages : 1 824
    Points : 1 544
    Points
    1 544
    Par défaut
    Citation Envoyé par escartefigue Voir le message
    Il n'est pas raisonnable de supprimer 90% des enregistrements d'une table d'un milliard de lignes par opération delete :
    - delete charge inutilement la log ce qui ralentit l'opération et pénalise les autres transactions
    - delete est lent puisqu'il affecte data et index
    - delete desorganise le tablespace

    Pour cette volumétrie, je décharge la table, traite le fichier unload pour filtrer ce qui est à conserver, trie le fichier résultant sur l'index cluster, et recharge la table en mode replace en activant les statistiques
    Ainsi, les données sont purgées, la table est réorganisée, les stats sont à jour et la log n'a pas bougé

    Traitement à lancer hors autres activités batch et TP sur la table bien sur
    C'est scriptable ça ?
    "Heureusement qu'il y avait mon nez, sinon je l'aurais pris en pleine gueule" Walter Spanghero

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

    Informations professionnelles :
    Activité : bourreau
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2010
    Messages : 10 134
    Points : 38 557
    Points
    38 557
    Billets dans le blog
    9
    Par défaut
    Le mieux est de lancer ce type de traitement la nuit par scheduler (automate d'exploitation) avec blocage des ressources pour éviter le lancement de travaux concurrents qui partiraient en -904 ou -911 à cause des utilitaires en cours

  9. #9
    Membre expérimenté
    Profil pro
    Inscrit en
    Février 2004
    Messages
    1 824
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2004
    Messages : 1 824
    Points : 1 544
    Points
    1 544
    Par défaut
    Citation Envoyé par escartefigue Voir le message
    Le mieux est de lancer ce type de traitement la nuit par scheduler (automate d'exploitation) avec blocage des ressources pour éviter le lancement de travaux concurrents qui partiraient en -904 ou -911 à cause des utilitaires en cours
    Je m'en suis sorti à coups de modulo parce que avec un DELETE TOP(X) il aurait fallût y claquer un COMMIT derrière et appuyer sur F5 en attendant "0 lignes affectés" étant donné qu'avec un while @@ROWCOUNT ça revenait au même, l'ensemble exécuté sous une même transaction.

    La donne à changée, on a compris qu'il ne s'agissait pas d'un script de maintenance, mais d'un script pompier pour débloquer une situation causée par un défaut de maintenance, ou plutôt un défaut de penser à la maintenance..

    Sur serveur de production d'un client grand compte à plus d'1 million d'utilisateur quotidien de leur service... Mon métier m'étonnera toujours, et ça c'est super cool !
    "Heureusement qu'il y avait mon nez, sinon je l'aurais pris en pleine gueule" Walter Spanghero

  10. #10
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par mister3957 Voir le message
    Je m'en suis sorti à coups de modulo parce que avec un DELETE TOP(X) il aurait fallût y claquer un COMMIT derrière et appuyer sur F5 en attendant "0 lignes affectés" étant donné qu'avec un while @@ROWCOUNT ça revenait au même, l'ensemble exécuté sous une même transaction.
    N'importe quoi. Le commit est implicite par défaut avec MS SQL Server. Sur Oracle, non.

  11. #11
    Membre expérimenté
    Profil pro
    Inscrit en
    Février 2004
    Messages
    1 824
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2004
    Messages : 1 824
    Points : 1 544
    Points
    1 544
    Par défaut
    Citation Envoyé par 7gyY9w1ZY6ySRgPeaefZ Voir le message
    N'importe quoi. Le commit est implicite par défaut avec MS SQL Server. Sur Oracle, non.
    Manifestement nous n'étions pas dans un contexte "par défaut", ni sous Oracle.
    "Heureusement qu'il y avait mon nez, sinon je l'aurais pris en pleine gueule" Walter Spanghero

  12. #12
    Expert éminent
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 153
    Points : 7 403
    Points
    7 403
    Billets dans le blog
    1
    Par défaut
    Enlevez-moi d'un doute...

    Le fichier de log... On parle bien du LDF non ?

    Si c'est le cas, je vois pas trop quelle différence il y a entre :

    delete matable;

    delete matable where id mod 2 = 0;
    delete matable where id mod 2 = 1;

    => En effet, dans les deux cas, l'intégralité des lignes supprimées vont se retrouver dans le LDF, histoire de pouvoir être récupérées si besoin lors d'une restauration... Non ?

    C'est pas plutôt TEMPDB (MDF et LDF) qui posaient problème ? (recopie de 90% d'une table de 1G lignes, ça bouffe effectivement de la place...)

    Autant sous Oracle, avec le rollback segment, je comprends la différence entre les deux requêtes, autant avec SQL Server, j'ai un peu plus de mal...
    On ne jouit bien que de ce qu’on partage.

  13. #13
    Rédacteur

    Avatar de SQLpro
    Homme Profil pro
    Expert bases de données / SQL / MS SQL Server / Postgresql
    Inscrit en
    Mai 2002
    Messages
    21 766
    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 766
    Points : 52 563
    Points
    52 563
    Billets dans le blog
    5
    Par défaut
    Oui les lignes sont biens journalisées pour un DELETE et même pour un TRUNCATE !

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

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

    Informations professionnelles :
    Activité : bourreau
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2010
    Messages : 10 134
    Points : 38 557
    Points
    38 557
    Billets dans le blog
    9
    Par défaut
    Seul le load peut - optionnellement - ne pas journaliser

    D'où l'intérêt de la solution par unload =>tri => reload/stat pour des suppressions fortement massiques

  15. #15
    Rédacteur

    Avatar de SQLpro
    Homme Profil pro
    Expert bases de données / SQL / MS SQL Server / Postgresql
    Inscrit en
    Mai 2002
    Messages
    21 766
    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 766
    Points : 52 563
    Points
    52 563
    Billets dans le blog
    5
    Par défaut
    Citation Envoyé par escartefigue Voir le message
    Seul le load peut - optionnellement - ne pas journaliser

    D'où l'intérêt de la solution par unload =>tri => reload/stat pour des suppressions fortement massiques

    En mode de journalisation BULK ou SIMPLE, oui.

    Mais je pense que les temps de chargement et déchargement des fichiers ainsi que le tri externe sera plus long. Notamment si bon parallélisme...

    Sans les tables sous forme DDL, les contraintes et la hardware difficile de donner des voies d'optimisation !

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

  16. #16
    Modérateur

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Janvier 2005
    Messages
    5 826
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Janvier 2005
    Messages : 5 826
    Points : 12 371
    Points
    12 371
    Par défaut
    Toute transaction journalise; en revanche effectivement, en mode BULK, la journalisation est minimale (mais pas inexistante).

    Une alternative aurait été :

    1. scripter la table pour la recréer sous un autre nom
    2. réaliser l'INSERT des lignes à conserver
    3. échanger les tables par métadonnées, avec un ALTER TABLE maGrosseTable SWITCH to maNouvellePetiteTable


    Rétrécir le fichier du journal des transactions à 1Mo se fait au détriment des performances, notamment parce que ce fichier grossira de nouveau, et ensuite parce que le nombre de fichier virtuels (VLF) ce de ce fichier va exploser. Or, traverser un VLF est coûteux aussi bien en CPU qu'en IO, donc il faut bien paramétrer l'incrément de fichier, et peut-être considérer prendre des sauvegardes ce ce fichier plus fréquemment ou passer en mode de récupération BULK

    @++

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [TADOQuery] Problème avec une requête
    Par yannba dans le forum Bases de données
    Réponses: 2
    Dernier message: 20/01/2006, 13h51
  2. Problème avec une requête
    Par demonark dans le forum Langage SQL
    Réponses: 3
    Dernier message: 19/09/2005, 05h35
  3. Problème avec une requête
    Par ringostarr dans le forum Langage SQL
    Réponses: 5
    Dernier message: 19/04/2005, 20h34
  4. Problème avec une requête
    Par snoopy69 dans le forum Débuter
    Réponses: 2
    Dernier message: 20/01/2005, 12h39
  5. problème avec une requête imbriquée
    Par jaimepasteevy dans le forum Langage SQL
    Réponses: 13
    Dernier message: 05/12/2003, 10h29

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