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

SQL Oracle Discussion :

Requête avec intervalle et comptage


Sujet :

SQL Oracle

  1. #1
    Membre confirmé

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    507
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 507
    Points : 503
    Points
    503
    Par défaut Requête avec intervalle et comptage
    Bonjour.

    Je souhaite optimiser une requête qui me pose des problèmes de performances.

    Le principe: je souhaite sélectionner les lignes d'une table t1, dont une colonne est présente dans un intervalle (entre 2 colonnes) de t2, mais les données ramenées ne sont pas les mêmes selon le nombre d'occurrences trouvées dans t2.
    J'ai décomposé cela en 2 requêtes.

    Quand il y a 0 ou plusieurs intervalles dans t2:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    SELECT t1.col1, t1.col2, t1.col3
      FROM table1 t1
     WHERE NOT EXISTS
         (SELECT COUNT(*), t2.borneinf, t2.bornesup
            FROM table2 t2
          WHERE t1.col4 >= t2.borneinf
              AND t1.col4 <= t2.bornesup
           GROUP BY t2.borneinf, t2.bornesup
          HAVING COUNT(*) = 1)
    Quand il y a effectivement un seul intervalle, ça se complique car j'ai besoin de récupérer certaines données de t2:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    SELECT t1.col1, t1.col2, t2.col1
      FROM table1 t1,
              table2 t2
     WHERE t1.col4 >= t2.borneinf
         AND t1.col4 <= t2.bornesup
         AND EXISTS
         (SELECT COUNT(*), t2.borneinf, t2.bornesup
            FROM table2 t2
          WHERE t1.col4 >= t2.borneinf
              AND t1.col4 <= t2.bornesup
           GROUP BY t2.borneinf, t2.bornesup
          HAVING COUNT(*) = 1)
    La table t1 fait 900 000 lignes, la table t2 en fait 130 000.
    Il y a des index sur t1.col4, t2.borneinf et t2.bornesup (qui sont des number), les stats sont calculées. Oracle 10g.
    Quand je lance la procédure, je fais un "INSERT ...SELECT..." avec les SELECT ci-dessus, en /*+ APPEND */.

    Je suis persuadé qu'il y a un moyen d'écrire ça autrement mais je sèche (et je suis très à la bourre dans les dev...).

    Si vous avez une idée, merci d'avance, vous allez me sauver!!!

  2. #2
    Modérateur
    Avatar de Waldar
    Homme Profil pro
    Customer Success Manager @Vertica
    Inscrit en
    Septembre 2008
    Messages
    8 452
    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 : Septembre 2008
    Messages : 8 452
    Points : 17 820
    Points
    17 820
    Par défaut
    Un petit jeu de test et ce sera parfait !

  3. #3
    Membre confirmé

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    507
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 507
    Points : 503
    Points
    503
    Par défaut
    Bonjour Waldar.

    Comme je ne peux pas faire de copier coller (car je bosse sous une VM ), je t'en fais un à la main:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    with table1 as (
    select 1 as col1, 1 as col2, 1 as col3, 150 as col4 from dual union all
    select 1 as col1, 2 as col2, 1 as col3, 250 as col4 from dual union all
    select 1 as col1, 3 as col2, 1 as col3, 350 as col4 from dual union all
    select 1 as col1, 4 as col2, 1 as col3, 450 as col4 from dual union all
    select 1 as col1, 5 as col2, 1 as col3, 550 as col4 from dual union all
    select 1 as col1, 6 as col2, 1 as col3, 650 as col4 from dual),
    table2 as (
    select 1 as col1, 100 as borneinf, 199 as bornesup from dual union all
    select 2 as col1, 100 as borneinf, 199 as bornesup from dual union all
    select 3 as col1, 200 as borneinf, 299 as bornesup from dual union all
    select 4 as col1, 300 as borneinf, 499 as bornesup from dual union all
    select 5 as col1, 300 as borneinf, 499 as bornesup from dual union all
    select 6 as col1, 500 as borneinf, 599 as bornesup from dual)
    Je suis en train de regarder une autre solution fonctionnelle, éventuellement à faire 2 tables selon le nombre d'intervalles. Je suis en train de me faire confirmer (ou non) si les intervalles peuvent être à cheval ou s'il y a une continuité stricte (ce qui ne devrait pas changer grand chose dans la requête en fin de compte).

    Edit: les col1, col2 et col3 de table1 n'ont pas d'importance en soi, col2 suffira pour identifier les lignes.

    Edit2: la table table2 est déjà une extraction d'une autre table. L'idée que j'ai - dans le cas où les requêtes seraient trop lourdes - est de séparer cette table en 2 tables, avec d'un côté les lignes avec un intervalle en un seul exemplaire, de l'autre les lignes avec intervalles en doublons. Ca éviterait de faire le count(*) group by, il ne resterait que la comparaison.

  4. #4
    Membre confirmé

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    507
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 507
    Points : 503
    Points
    503
    Par défaut
    MA solution fonctionnelle a l'air pas mal et rapide. Dans ma table t2, je ne garde que les lignes avec les intervalles en un seul exemplaire car je n'ai pas besoin des données que dans le cas ou il y a un seul intervalle.

    Ca se résumera donc à cela:

    Quand il y a 0 ou plusieurs intervalles dans t2:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    SELECT t1.col1, t1.col2, t1.col3
      FROM table1 t1
     WHERE NOT EXISTS
         (SELECT 1
            FROM table2 t2
          WHERE t1.col4 >= t2.borneinf
              AND t1.col4 <= t2.bornesup)
    Quand il y a un seul intervalle:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    SELECT t1.col1, t1.col2, t2.col1
      FROM table1 t1,
              table2 t2
     WHERE t1.col4 >= t2.borneinf
         AND t1.col4 <= t2.bornesup
    MAIS (il y a toujours un mais) j'ai un autre problème, à mon avis plus simple: la construction de la table t2.

    Au départ, ma table2 est définie ainsi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    CREATE TABLE table2 AS
    SELECT col1 as borneinf, col2, col3... col12 as bornesup
      FROM table3
     WHERE  (conditions compliquées avec table3 appelée plusieurs fois)......
    Il faut que je retire de cette table les lignes avec des intervalles en double:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    DELETE FROM table2
     WHERE (borneinf, bornesup) IN
         (SELECT borneinf, bornesup FROM
               (SELECT COUNT(*), borneinf, bornesup
                   FROM table2
                GROUP BY borneinf, bornesup
                HAVING COUNT(*) <>1)
         )
    Mon problème: je veux créer la table et "faire le DELETE" en un seul coup, donc créer la table en filtrant sur les intervalles uniques.

    Une idée?

  5. #5
    Modérateur
    Avatar de Waldar
    Homme Profil pro
    Customer Success Manager @Vertica
    Inscrit en
    Septembre 2008
    Messages
    8 452
    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 : Septembre 2008
    Messages : 8 452
    Points : 17 820
    Points
    17 820
    Par défaut
    Beaucoup d'informations, au final ce n'est plus très clair de ce que vous voulez faire !

    Entre la requête multi intervalle, les create tables vous m'avez perdu !

    Une petite synthèse ?

  6. #6
    Membre confirmé

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    507
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 507
    Points : 503
    Points
    503
    Par défaut
    Effectivement, trop d'infos tue l'info, d'autant que ma vision a évolué depuis.

    Au final, mon problème se résume à cela:
    je fais deux actions (un CREATE et un DELETE) pour avoir les données souhaitées dans une table, j'aimerais directement créer la table en une seule fois sans avoir à faire de DELETE.

    Action 1:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    CREATE TABLE table2 AS
    SELECT col1 AS borneinf, col2, col3... col12 AS bornesup
      FROM table3
    WHERE condition
    Action 2:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    DELETE FROM table2
     WHERE (borneinf, bornesup) IN
         (SELECT borneinf, bornesup FROM
               (SELECT COUNT(*), borneinf, bornesup
                   FROM table2
                GROUP BY borneinf, bornesup
                HAVING COUNT(*) <>1)
         )
    Je souhaite intégrer l'action 2 dans le CREATE, donc filtrer directement les lignes sur l'unicité du couple (borneinf, bornesup) à la création.
    Ca m'échappe...

    Merci d'avance.

  7. #7
    Modérateur
    Avatar de Waldar
    Homme Profil pro
    Customer Success Manager @Vertica
    Inscrit en
    Septembre 2008
    Messages
    8 452
    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 : Septembre 2008
    Messages : 8 452
    Points : 17 820
    Points
    17 820
    Par défaut
    Donc tous les intervalles présents plusieurs fois ne sont pas à insérer (même pas en un exemplaire) ?

    Quelque chose dans ce goût-là je dirai :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    CREATE TABLE table2 AS
    WITH SR as
    (
    SELECT col1, col2, col3... col12,
           count(*) over(partition by col1, col12) as nb_borne
      FROM table3
     WHERE condition
    )
    SELECT col1 AS borneinf, col2, col3... col12 AS bornesup
      FROM SR
     WHERE nb_borne = 1;

  8. #8
    Membre confirmé

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    507
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 507
    Points : 503
    Points
    503
    Par défaut
    Ah la la, je ne suis pas encore à l'aise avec les PARTITION BY...

    Et je ne pensais pas qu'on pouvait utiliser un WITH dans un CREATE.

    Effectivement, on ne prend pas les intervalles multiples car on n'a pas besoin de leurs données.
    Je teste ça et je te dis quoi.

  9. #9
    Membre confirmé

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    507
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 507
    Points : 503
    Points
    503
    Par défaut
    Ca fonctionne.
    C'est déjà ça de réglé. J'ai un autre souci mais c'est un autre sujet.

    Merci Waldar.

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

Discussions similaires

  1. [MySQL] Problème requête avec intervalle de date
    Par AurelienNF dans le forum PHP & Base de données
    Réponses: 9
    Dernier message: 25/05/2011, 14h04
  2. Requête avec intervalle de dates
    Par fastjim dans le forum Langage SQL
    Réponses: 2
    Dernier message: 21/06/2010, 17h05
  3. écriture d'une requête avec intervalle de date
    Par urbanspike dans le forum VB 6 et antérieur
    Réponses: 3
    Dernier message: 08/10/2008, 15h49
  4. Requête avec un intervalle de date
    Par heruwenli dans le forum SQL
    Réponses: 4
    Dernier message: 26/06/2008, 12h46
  5. Requête avec comptage
    Par Arsene12 dans le forum WinDev
    Réponses: 4
    Dernier message: 16/04/2008, 14h26

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