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

SAS Base Discussion :

Jointure SQL avec conditions


Sujet :

SAS Base

  1. #1
    Membre confirmé
    Femme Profil pro
    Responsable marketing opérationnel
    Inscrit en
    Avril 2011
    Messages
    58
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France

    Informations professionnelles :
    Activité : Responsable marketing opérationnel
    Secteur : Biens de consommation

    Informations forums :
    Inscription : Avril 2011
    Messages : 58
    Par défaut Jointure SQL avec conditions
    Bonjour,

    j'ai une table qui contient le détail des commandes de clients par semaine et par pages du catalogue. J'ai besoin de savoir quels sont les clients qui ont achetés sur les pages 1,564,9876 et sur 2 périodes de temps distinctes. Pour le moment, j'identifie les clients commandant sur la période1 puis ceux commandant sur la période2; puis je fusionne ces deux tables comme suit:
    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
    * Clients période1;
    PROC SQL;
    CREATE TABLE actif_t1 (keep=id_foy rename=(id_foy=foy_new)) AS
    SELECT DISTINCT id_foy
    FROM lib.tous_clients(where=(1<=sem<=25 and pages in (1,564,9876)));
    QUIT;
     
    * Clients période2;
    PROC SQL;
    CREATE TABLE actif_t2 (keep=id_foy rename=(id_foy=foy_new)) AS
    SELECT DISTINCT id_foy
    FROM lib.tous_clients (where=(45<=sem<=52 and pages in (1,564,9876)));
    QUIT;
     
    * clients OK;
    PROC SQL ;
     CREATE TABLE rep.actifs AS 
     SELECT t1.foy_new , t2.foy_new
     FROM actif_t1 t1 INNER JOIN actif_t2 t2
     ON t1.foy_new=t2.foy_new;
    QUIT;
    La table est bien entendu ENORME et le temps de taitement ENORME aussi!!!!! Je suis sûre et certaine qu'il y a moyen d'écrire cela plus efficacement.

    Merci de votre aide.

    Olive.

  2. #2
    Modérateur

    Homme Profil pro
    Consultant en Business Intelligence
    Inscrit en
    Février 2011
    Messages
    1 625
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Consultant en Business Intelligence
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2011
    Messages : 1 625
    Par défaut
    Hello,
    quelques chose comme :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    PROC SQL;
    CREATE TABLE actif_t1 (keep=id_foy RENAME=(id_foy=foy_new)) AS
    SELECT DISTINCT id_foy
    FROM lib.tous_clients WHERE
    (1<=sem<=25 or 45<=sem<=52)AND pages IN (1,564,9876);
    QUIT;
    N'oubliez pas de consulter les FAQ SAS et les cours et tutoriels SAS
    N'oubliez pas de mettre votre message à si la solution donnée résout votre problème

  3. #3
    Membre confirmé
    Femme Profil pro
    Responsable marketing opérationnel
    Inscrit en
    Avril 2011
    Messages
    58
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France

    Informations professionnelles :
    Activité : Responsable marketing opérationnel
    Secteur : Biens de consommation

    Informations forums :
    Inscription : Avril 2011
    Messages : 58
    Par défaut
    oh! des fois, je me fais honte! c'est tellement bête... merci SAM!

  4. #4
    Membre confirmé
    Femme Profil pro
    Responsable marketing opérationnel
    Inscrit en
    Avril 2011
    Messages
    58
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France

    Informations professionnelles :
    Activité : Responsable marketing opérationnel
    Secteur : Biens de consommation

    Informations forums :
    Inscription : Avril 2011
    Messages : 58
    Par défaut
    Sam,

    je suis désolée mais je n'ai pas les idées claires aujourd'hui: c'est sûrement le contre-coup de ma grippe de ce début de semaine!
    Ta solution ne fonctionne pas: ce dont j'ai besoin, c'est d'identifier les clients acheteurs sur les 3 pages sur période1 ET période2. Or ta proposition correspond à des acheteurs période1 OU période2...

    Quelqu'un a une solution pour ce pb?

    Merci!

    Olive

  5. #5
    Modérateur

    Homme Profil pro
    Consultant en Business Intelligence
    Inscrit en
    Février 2011
    Messages
    1 625
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Consultant en Business Intelligence
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2011
    Messages : 1 625
    Par défaut
    Hello,
    C'est moi qui déconnais. Malheureusement je crains que t'es obligée lire la table deux fois.
    tu peux tous faire en une seule proc sql.
    J'ai légérement modifié ton code pour adapter à mon exemple:

    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
    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
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
     
    data 	tous_clients;
     
    input sem  pages id_foy $;
    cards;
    1	1	a45
    2	564	a46
    3	9876	a45
    4	1	a4
    5	564	a5
    6	9876	a6
    7	1	a7
    8	564	a8
    9	9876	a9
    10	1	a10
    11	564	a11
    12	9876	a12
    13	1	a13
    14	564	a14
    15	9876	a15
    16	1	a16
    17	564	a17
    18	9876	a18
    19	1	a19
    20	564	a20
    21	9876	a21
    22	1	a22
    23	564	a23
    24	9876	a24
    25	1	a25
    26	564	a26
    27	9876	a27
    28	1	a28
    29	564	a29
    30	9876	a30
    31	1	a31
    32	564	a32
    33	9876	a33
    34	1	a34
    35	564	a35
    36	9876	a36
    37	1	a37
    38	564	a38
    39	9876	a39
    40	1	a40
    41	564	a41
    42	9876	a42
    43	1	a43
    44	564	a44
    45	9876	a45
    46	1	a46
    47	564	a47
    48	9876	a48
    49	1	a49
    50	564	a50
    51	9876	a51
    52	1	a52
    53	564	a53
    54	9876	a54
    55	1	a55
    56	564	a56
    57	9876	a1
    58	1	a1
    59	564	a54
    60	9876	a60
    61	1	a61
    62	564	a62
    63	9876	a63
    64	1	a64
    65	564	a65
    66	9876	a66
    ;
    run;
     
    * Clients période1;
    PROC SQL;
    CREATE TABLE actif_t1 (keep=id_foy RENAME=(id_foy=foy_new)) AS
    SELECT DISTINCT id_foy
    FROM tous_clients(WHERE=(1<=sem<=25 AND pages IN (1,564,9876)));
    QUIT;
     
    * Clients période2;
    PROC SQL;
    CREATE TABLE actif_t2 (keep=id_foy RENAME=(id_foy=foy_new)) AS
    SELECT DISTINCT id_foy
    FROM tous_clients (WHERE=(45<=sem<=52 AND pages IN (1,564,9876)));
    QUIT;
     
    * clients OK;
    PROC SQL ;
     CREATE TABLE actifs AS 
     SELECT t1.foy_new , t2.foy_new
     FROM actif_t1 t1 INNER JOIN actif_t2 t2
     ON t1.foy_new=t2.foy_new;
    QUIT;
    et voici ma proposition, Mais je ne te garantie pas un gain en performance.A tester :

    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
    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
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
     
     
    data 	tous_clients;
     
    input sem  pages id_foy $;
    cards;
    1	1	a45
    2	564	a46
    3	9876	a45
    4	1	a4
    5	564	a5
    6	9876	a6
    7	1	a7
    8	564	a8
    9	9876	a9
    10	1	a10
    11	564	a11
    12	9876	a12
    13	1	a13
    14	564	a14
    15	9876	a15
    16	1	a16
    17	564	a17
    18	9876	a18
    19	1	a19
    20	564	a20
    21	9876	a21
    22	1	a22
    23	564	a23
    24	9876	a24
    25	1	a25
    26	564	a26
    27	9876	a27
    28	1	a28
    29	564	a29
    30	9876	a30
    31	1	a31
    32	564	a32
    33	9876	a33
    34	1	a34
    35	564	a35
    36	9876	a36
    37	1	a37
    38	564	a38
    39	9876	a39
    40	1	a40
    41	564	a41
    42	9876	a42
    43	1	a43
    44	564	a44
    45	9876	a45
    46	1	a46
    47	564	a47
    48	9876	a48
    49	1	a49
    50	564	a50
    51	9876	a51
    52	1	a52
    53	564	a53
    54	9876	a54
    55	1	a55
    56	564	a56
    57	9876	a1
    58	1	a1
    59	564	a54
    60	9876	a60
    61	1	a61
    62	564	a62
    63	9876	a63
    64	1	a64
    65	564	a65
    66	9876	a66
    ;
    run;
     
     
    proc sql;
    create table actifs1 as select 	 id_foy 
    from tous_clients 
    where 45<=sem<=52 AND pages IN (1,564,9876)
     and id_foy in (select id_foy from tous_clients where  1<=sem<=25 AND pages IN (1,564,9876));
    quit;
    Bon rétablissement.
    N'oubliez pas de consulter les FAQ SAS et les cours et tutoriels SAS
    N'oubliez pas de mettre votre message à si la solution donnée résout votre problème

  6. #6
    Rédacteur

    Homme Profil pro
    SAS ALLIANCE SILVER. Consultant et formateur SAS et Cognos.
    Inscrit en
    Avril 2009
    Messages
    2 497
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : SAS ALLIANCE SILVER. Consultant et formateur SAS et Cognos.
    Secteur : Conseil

    Informations forums :
    Inscription : Avril 2009
    Messages : 2 497
    Par défaut
    A mon tour mais Samir à raison de noter la double lecture nécessaire


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    PROC SQL ;
     CREATE TABLE rep.actifs AS 
     SELECT t1.foy_new , t2.foy_new
     FROM 
    (SELECT DISTINCT id_foy
    FROM lib.tous_clients(WHERE=(1<=sem<=25 AND pages IN (1,564,9876)))) t1
    INNER JOIN
    (SELECT DISTINCT id_foy
    FROM lib.tous_clients (WHERE=(45<=sem<=52 AND pages IN (1,564,9876)))) t2
    ON t1.foy_new=t2.foy_new;
    QUIT;

  7. #7
    Membre actif
    Homme Profil pro
    Ingénieur Statisticien
    Inscrit en
    Janvier 2013
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur Statisticien
    Secteur : Service public

    Informations forums :
    Inscription : Janvier 2013
    Messages : 28
    Par défaut
    Bonjour Olive2011,

    Ci-dessous une seconde option, avec une seule lecture de table :

    (reprenant la table de test de s_a_m)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    proc sql;
    create table actifs2 as
    select id_foy, max(top1) as mtop1, max(top2) as mtop2
    from
    (select id_foy, 
    CASE WHEN 1<=sem<=25 THEN 1 ELSE 0 END as top1, 
    CASE WHEN 45<=sem<=52 THEN 1 ELSE 0 END as top2
    from tous_clients
    where (1<=sem<=25 OR 45<=sem<=52)AND pages IN (1,564,9876) 
    ) 
    group by id_foy
    having max(top1)=1 and max(top2)=1;
    quit;
    Dans la mesure où la table n'est ouverte qu'une seule fois et le filtre WHERE est appliqué le plus tôt possible, des gains de performance peuvent être espérés.
    Par contre, le fait d'utiliser des fonctions d'agrégation peut, à l'inverse, constituer un coût de performances...

  8. #8
    Modérateur

    Homme Profil pro
    Consultant en Business Intelligence
    Inscrit en
    Février 2011
    Messages
    1 625
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Consultant en Business Intelligence
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2011
    Messages : 1 625
    Par défaut
    salut jmarandet ,


    Dans ce cas, on ne gagne pas de performance, au contraire on en perd. Tu peux toi même faire le test avec les deux propositions de stéphane et moi et ta proposition pour constater que ta solution, d'une mets plus de temps à tourner et de deux, elle consomme plus de resources, et là je parle de seulement d'une table de 66 lignes. ajoutes ceci à ta proc sql, pour mieux comprendre les requêtes imbriquées.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    proc sql _method _tree;
    N'oubliez pas de consulter les FAQ SAS et les cours et tutoriels SAS
    N'oubliez pas de mettre votre message à si la solution donnée résout votre problème

  9. #9
    Membre actif
    Homme Profil pro
    Ingénieur Statisticien
    Inscrit en
    Janvier 2013
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur Statisticien
    Secteur : Service public

    Informations forums :
    Inscription : Janvier 2013
    Messages : 28
    Par défaut
    Salut s_a_m,

    C'est là justement que les choses sont plus difficiles à déterminer...

    La performance de la requête dépend de nombreux facteurs (stockage local ou distant, table au format SAS natif ou externe, accès à une base de donnée en PASS-THROUGH, présence d'index, volumétrie de la table, etc...) que nous ne pouvons apprécier à distance à la place d'Olive2011.

    Pour le reste, je m'inscrit totalement en faux concernant ta remarque, au contraire.
    Les requêtes en double-lecture comme celles que vous proposez sont plus performantes sur de petites table, mais elles s'avèreront finalement très coûteuses si elles sont appliquées à d'importantes volumétries de données.

    Ci-dessous mes mesures comparatives pour une table de 14 256 000 lignes (simple répétition de la table telle que définie par s_a_m) :

    1. Méthode de s_a_m :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    proc sql _method _tree ;
    CREATE TABLE actifs1 AS SELECT 	 id_foy 
    FROM tous_clients 
    WHERE 45<=sem<=52 AND pages IN (1,564,9876)
     AND id_foy IN (SELECT id_foy FROM tous_clients WHERE  1<=sem<=25 AND pages IN (1,564,9876));
    quit;
     
     
    NOTE: La table WORK.ACTIFS1 a été créée, avec 432000 lignes et 1 col.
    NOTE: Procédure SQL a utilisé (Durée totale du traitement) :
          temps réel          43.68 secondes
          temps UC            4.74 secondes
    (un petit problème d'unicité des identifiants récoltés se pose)

    2. Méthode de datametric (adaptée pour retirer des mentions de libnames et de 'foy_new' étranges) :
    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
     
    proc sql _method _tree ;
    CREATE TABLE actifs1 AS SELECT DISTINCT t1.id_foy 
    FROM 
    (SELECT DISTINCT id_foy
    FROM tous_clients(WHERE=(1<=sem<=25 AND pages IN (1,564,9876)))) t1
    INNER JOIN
    (SELECT DISTINCT id_foy
    FROM tous_clients (WHERE=(45<=sem<=52 AND pages IN (1,564,9876)))) t2
    ON (t1.id_foy = t2.id_foy);
    quit;
     
    NOTE: La table WORK.ACTIFS1 a été créée, avec 2 lignes et 1 col.
    NOTE: Procédure SQL a utilisé (Durée totale du traitement) :
          temps réel          16.23 secondes
          temps UC            6.25 secondes
    On note un gain de performance appréciable, à mon avis dû aux 'SELECT DISTINCT' des requêtes intermédiaires.

    3. Ma proposition :
    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
    proc sql _method _tree ;
    	create table actifs2 as
    		select id_foy, max(top1) as mtop1, max(top2) as mtop2
    		from
    			(select id_foy, 
    				CASE WHEN 1<=sem<=25 THEN 1 ELSE 0 END as top1, 
    				CASE WHEN 45<=sem<=52 THEN 1 ELSE 0 END as top2
    			from tous_clients
    			where (1<=sem<=25 OR 45<=sem<=52)AND pages IN (1,564,9876) 
    			) 
    		group by id_foy
    		having max(top1)=1 and max(top2)=1;
    quit;
     
    NOTE: La table WORK.ACTIFS2 a été créée, avec 2 lignes et 3 col.
    NOTE: Procédure SQL a utilisé (Durée totale du traitement) :
          temps réel          9.10 secondes
          temps UC            8.71 secondes
    CQFD.

  10. #10
    Membre actif
    Homme Profil pro
    Ingénieur Statisticien
    Inscrit en
    Janvier 2013
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur Statisticien
    Secteur : Service public

    Informations forums :
    Inscription : Janvier 2013
    Messages : 28
    Par défaut
    Nouvelles mesures avec l'option
    1. Méthode de s_a_m
    NOTE: Procédure SQL a utilisé (Durée totale du traitement) :
    temps réel 55.01 secondes
    temps UC utilisateur 4.06 secondes
    temps UC système 1.15 secondes
    Mémoire 191k
    Mémoire SE 10608k
    Horodatage 20/02/2013 14:06:24
    2. Méthode datametric
    NOTE: Procédure SQL a utilisé (Durée totale du traitement) :
    temps réel 35.03 secondes
    temps UC utilisateur 5.93 secondes
    temps UC système 1.50 secondes
    Mémoire 68488k
    Mémoire SE 77688k
    Horodatage 20/02/2013 14:07:17
    3. Méthode jmarandet
    NOTE: Procédure SQL a utilisé (Durée totale du traitement) :
    temps réel 24.96 secondes
    temps UC utilisateur 7.71 secondes
    temps UC système 1.01 secondes
    Mémoire 67524k
    Mémoire SE 76664k
    Horodatage 20/02/2013 14:05:10
    Sur ma machine, les mesures que j'observe en termes de temps, d'UC système et Mémoire sont donc moins en faveur des solutions à double-lecture.
    Là encore d'autres facteurs matériels peuvent influencer le choix d'une méthode SQL, comme la configuration matérielle (espace SWAP, RAM, type de coeur, etc...).

    @Olive2011, je peux te conseiller d'utiliser l'option de mesure fullstimer afin d'évaluer par toi-même la meilleur stratégie applicable à ta situation réelle.

  11. #11
    Modérateur

    Homme Profil pro
    Consultant en Business Intelligence
    Inscrit en
    Février 2011
    Messages
    1 625
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Consultant en Business Intelligence
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2011
    Messages : 1 625
    Par défaut
    Hello jmarandet

    Merci pour cette brillante analyse "médico légal" . C'est à mon tour maintenant.
    Alors:

    - En ce qui concerne les index, pass trough, on en a déjà discuter dans pas mal de post.

    - Type de stockage, réseau,UC,RAM, Nombre d'appli tournant sur le même serveur,... : on en a déjà discuter , récement ici

    - Je vais te donner des stats que j'ai obtenu avec une table 660000 lignes ( tu peux bien évidement tester sur une table plus grosse).
    CODE:

    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
    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
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
     
    options nomprint nomlogic nosymbolgen nonotes;
    DATA  tous_clients;
     
    input sem :2. pages :4. id_foy $;
    cards;
    1 1 a45
    2 564 a46
    3 9876 a45
    4 1 a4
    5 564 a5
    6 9876 a6
    7 1 a7
    8 564 a8
    9 9876 a9
    10 1 a10
    11 564 a11
    12 9876 a12
    13 1 a13
    14 564 a14
    15 9876 a15
    16 1 a16
    17 564 a17
    18 9876 a18
    19 1 a19
    20 564 a20
    21 9876 a21
    22 1 a22
    23 564 a23
    24 9876 a24
    25 1 a25
    26 564 a26
    27 9876 a27
    28 1 a28
    29 564 a29
    30 9876 a30
    31 1 a31
    32 564 a32
    33 9876 a33
    34 1 a34
    35 564 a35
    36 9876 a36
    37 1 a37
    38 564 a38
    39 9876 a39
    40 1 a40
    41 564 a41
    42 9876 a42
    43 1 a43
    44 564 a44
    45 9876 a45
    46 1 a46
    47 564 a47
    48 9876 a48
    49 1 a49
    50 564 a50
    51 9876 a51
    52 1 a52
    53 564 a53
    54 9876 a54
    55 1 a55
    56 564 a56
    57 9876 a1
    58 1 a1
    59 564 a54
    60 9876 a60
    61 1 a61
    62 564 a62
    63 9876 a63
    64 1 a64
    65 564 a65
    66 9876 a66
    ;
    run;
     
    %macro repet;
     
    data tous_clients;
    set %do i=1 %to 10000;
     tous_clients
    %end;;
    run;
     
    %mend;
     
    %repet;
    options notes fullstimer;
    proc sql ;*_method _tree ;
     CREATE TABLE actifs2 AS
      SELECT id_foy, max(top1) AS mtop1, max(top2) AS mtop2
      FROM
       (SELECT id_foy, 
        CASE WHEN 1<=sem<=25 THEN 1 ELSE 0 END AS top1, 
        CASE WHEN 45<=sem<=52 THEN 1 ELSE 0 END AS top2
       FROM tous_clients
       WHERE (1<=sem<=25 OR 45<=sem<=52)AND pages IN (1,564,9876) 
       ) 
      GROUP BY id_foy
      HAVING max(top1)=1 AND max(top2)=1;
    quit;
     
     
    proc sql ;*_method _tree ;
    CREATE TABLE actifs1 AS SELECT DISTINCT t1.id_foy 
    FROM 
    (SELECT DISTINCT id_foy
    FROM tous_clients(WHERE=(1<=sem<=25 AND pages IN (1,564,9876)))) t1
    INNER JOIN
    (SELECT DISTINCT id_foy
    FROM tous_clients (WHERE=(45<=sem<=52 AND pages IN (1,564,9876)))) t2
    ON (t1.id_foy = t2.id_foy);
    quit;
     
    proc sql;
    CREATE TABLE actifs1 AS SELECT  distinct   id_foy 
    FROM tous_clients 
    WHERE 45<=sem<=52 AND pages IN (1,564,9876)
    AND id_foy IN (SELECT distinct  id_foy FROM tous_clients WHERE  1<=sem<=25 AND pages IN (1,564,9876));
    quit;
    Résultat :
    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
    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
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
     
    15         options nomprint nomlogic nosymbolgen nonotes;
    16         DATA  tous_clients;
    17         
    18         input sem :2. pages :4. id_foy $;
    19         cards;
    86         ;
    87         run;
    88         
    89         %macro repet;
    90         
    91         data tous_clients;
    92         set %do i=1 %to 10000;
    93          tous_clients
    94         %end;;
    95         run;
    96         
    97         %mend;
    98         
    99         %repet;
    100        options notes fullstimer;
    101        proc sql ;
    101      !           *_method _tree ;
    102         CREATE TABLE actifs2 AS
    103          SELECT id_foy, max(top1) AS mtop1, max(top2) AS mtop2
    104          FROM
    105           (SELECT id_foy,
    106            CASE WHEN 1<=sem<=25 THEN 1 ELSE 0 END AS top1,
    107            CASE WHEN 45<=sem<=52 THEN 1 ELSE 0 END AS top2
    108           FROM tous_clients
    109           WHERE (1<=sem<=25 OR 45<=sem<=52)AND pages IN (1,564,9876)
    110           )
    111          GROUP BY id_foy
    112          HAVING max(top1)=1 AND max(top2)=1;
    NOTE: La table WORK.ACTIFS2 a été créée, avec 2 lignes et 3 col.
     
    2                                           Le Système SAS          15:00 Wednesday, February 20, 2013
     
    113        quit;
    NOTE: Procédure SQL a utilisé (Durée totale du traitement) :
          temps réel          0.85 secondes
          temps UC utilisateur 0.37 secondes
          temps UC système    0.04 secondes
          Mémoire                            18651k
          Mémoire SE                         143376k
          Horodatage            20/02/2013  16:19:52
     
     
    114        
    115        
    116        proc sql ;
    116      !           *_method _tree ;
    117        CREATE TABLE actifs1 AS SELECT DISTINCT t1.id_foy
    118        FROM
    119        (SELECT DISTINCT id_foy
    120        FROM tous_clients(WHERE=(1<=sem<=25 AND pages IN (1,564,9876)))) t1
    121        INNER JOIN
    122        (SELECT DISTINCT id_foy
    123        FROM tous_clients (WHERE=(45<=sem<=52 AND pages IN (1,564,9876)))) t2
    124        ON (t1.id_foy = t2.id_foy);
    NOTE: La table WORK.ACTIFS1 a été créée, avec 2 lignes et 1 col.
     
    125        quit;
    NOTE: Procédure SQL a utilisé (Durée totale du traitement) :
          temps réel          0.59 secondes
          temps UC utilisateur 0.36 secondes
          temps UC système    0.01 secondes
          Mémoire                            11843k
          Mémoire SE                         137244k
          Horodatage            20/02/2013  16:19:53
     
     
    126        
    127        proc sql;
    128        CREATE TABLE actifs1 AS SELECT  distinct   id_foy
    129        FROM tous_clients
    130        WHERE 45<=sem<=52 AND pages IN (1,564,9876)
    131        AND id_foy IN (SELECT distinct  id_foy FROM tous_clients WHERE  1<=sem<=25 AND pages IN
    131      ! (1,564,9876));
    NOTE: La table WORK.ACTIFS1 a été créée, avec 2 lignes et 1 col.
     
    132        quit;
    NOTE: Procédure SQL a utilisé (Durée totale du traitement) :
          temps réel          0.57 secondes
          temps UC utilisateur 0.23 secondes
          temps UC système    0.09 secondes
          Mémoire                            11481k
          Mémoire SE                         136988k
          Horodatage            20/02/2013  16:19:54
     
     
    133
    à condition d'exécuter le programme de cet ordre :
    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
     
    proc sql ;*_method _tree ;
     CREATE TABLE actifs2 AS
      SELECT id_foy, max(top1) AS mtop1, max(top2) AS mtop2
      FROM
       (SELECT id_foy, 
        CASE WHEN 1<=sem<=25 THEN 1 ELSE 0 END AS top1, 
        CASE WHEN 45<=sem<=52 THEN 1 ELSE 0 END AS top2
       FROM tous_clients
       WHERE (1<=sem<=25 OR 45<=sem<=52)AND pages IN (1,564,9876) 
       ) 
      GROUP BY id_foy
      HAVING max(top1)=1 AND max(top2)=1;
    quit;
     
     
    proc sql ;*_method _tree ;
    CREATE TABLE actifs1 AS SELECT DISTINCT t1.id_foy 
    FROM 
    (SELECT DISTINCT id_foy
    FROM tous_clients(WHERE=(1<=sem<=25 AND pages IN (1,564,9876)))) t1
    INNER JOIN
    (SELECT DISTINCT id_foy
    FROM tous_clients (WHERE=(45<=sem<=52 AND pages IN (1,564,9876)))) t2
    ON (t1.id_foy = t2.id_foy);
    quit;
     
    proc sql;
    CREATE TABLE actifs1 AS SELECT  distinct   id_foy 
    FROM tous_clients 
    WHERE 45<=sem<=52 AND pages IN (1,564,9876)
    AND id_foy IN (SELECT distinct  id_foy FROM tous_clients WHERE  1<=sem<=25 AND pages IN (1,564,9876));
    quit;
    En revanche t'avais raison, il y avait une coquille dans mon premier PGM, j'avais effectivement oublié de mettre un distinct
    N'oubliez pas de consulter les FAQ SAS et les cours et tutoriels SAS
    N'oubliez pas de mettre votre message à si la solution donnée résout votre problème

  12. #12
    Membre actif
    Homme Profil pro
    Ingénieur Statisticien
    Inscrit en
    Janvier 2013
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur Statisticien
    Secteur : Service public

    Informations forums :
    Inscription : Janvier 2013
    Messages : 28
    Par défaut
    @s_a_m,

    oui oui, certes... Un autre système, une autre dimension de table, une autre mesure.
    What else ?

    J'ai reconnu que ma méthode avait un coût, en particulier pour les tables de petite taille (moins d'1 million de lignes ce n'est pas tant que ça, surtout si votre machine est dans la catégorie des bêtes de course).
    Si vous avez le temps vous pouvez aussi vous amuser à construire un macro-programme qui génère plusieurs tables de taille différente, de structure différente aussi (index, variables numériques, caractères, etc...) et rechercher avec la macro %LOGPARSE à illustrer par de beaux graphiques les différentes balances coût/bénéfice de chaque stratégie.

  13. #13
    Membre chevronné
    Inscrit en
    Janvier 2010
    Messages
    235
    Détails du profil
    Informations forums :
    Inscription : Janvier 2010
    Messages : 235
    Par défaut
    La solution la plus élégante et sans doute la plus performante.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    PROC SQL;
    CREATE TABLE actif   AS
    SELECT id_foy 
    	, SUM(1<=sem<=25 AND pages IN (1,564,9876)) as nb_com1
    	, SUM(45<=sem<=52 AND pages IN (1,564,9876)) as  nb_com2
    from tous_clients
    group by id_foy
    ;QUIT;

  14. #14
    Modérateur

    Homme Profil pro
    Consultant en Business Intelligence
    Inscrit en
    Février 2011
    Messages
    1 625
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Consultant en Business Intelligence
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2011
    Messages : 1 625
    Par défaut
    je vais déterrer ce sujet.

    @sasadm "La solution la plus élégante et sans doute la plus performante" sauf que en l'état , ta proposition ne donne pas le résultat souhaité.
    N'oubliez pas de consulter les FAQ SAS et les cours et tutoriels SAS
    N'oubliez pas de mettre votre message à si la solution donnée résout votre problème

  15. #15
    Membre chevronné
    Inscrit en
    Janvier 2010
    Messages
    235
    Détails du profil
    Informations forums :
    Inscription : Janvier 2010
    Messages : 235
    Par défaut
    Citation Envoyé par s_a_m Voir le message
    je vais déterrer ce sujet.

    @sasadm "La solution la plus élégante et sans doute la plus performante" sauf que en l'état, ta proposition ne donne pas le résultat souhaité.
    Même si tu as raison (à une ligne de code près la requête ne donne pas le résultat exact) ton déterrage est un brin mesquin.

    Plutôt que de me coller un -1 tu aurais pu tout bonnement compléter par toi même. A moins que tu n'aies rien compris à la proposition "en l'état".

  16. #16
    Modérateur

    Homme Profil pro
    Consultant en Business Intelligence
    Inscrit en
    Février 2011
    Messages
    1 625
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Consultant en Business Intelligence
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2011
    Messages : 1 625
    Par défaut
    Ce que je voulais dire par "en l'état", c'est que si tu n'as pas de modif à apporter à ton code, la réponse que t'as donné est fausse et ne répond pas au besoin. Je n'ai pas à compléter ta proposition, c'est plutôt à toi de le faire puisque c'est ta propre proposition.

    Ensuite, je t'ai collé un -1 parce que je ne suis pas d'accord avec ta proposition après l’avoir testé bien sûre.

    Pour finir, je ne comprends pas trop pour quoi cette phrase :
    "ton déterrage est un brin mesquin." Mais je mettrai ça sur le compte de la colère.

    N'oubliez pas de consulter les FAQ SAS et les cours et tutoriels SAS
    N'oubliez pas de mettre votre message à si la solution donnée résout votre problème

  17. #17
    Membre chevronné
    Inscrit en
    Janvier 2010
    Messages
    235
    Détails du profil
    Informations forums :
    Inscription : Janvier 2010
    Messages : 235
    Par défaut
    Je crois que tu t'enfonces...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    PROC SQL;
    CREATE TABLE actif   AS
    SELECT id_foy 
    	, SUM(1<=sem<=25 AND pages IN (1,564,9876)) AS nb_com1
    	, SUM(45<=sem<=52 AND pages IN (1,564,9876)) AS  nb_com2
    FROM tous_clients
    GROUP BY id_foy
    having nb_com1 and nb_com2 /* coucou s_a_m */
    ;QUIT;

  18. #18
    Modérateur

    Homme Profil pro
    Consultant en Business Intelligence
    Inscrit en
    Février 2011
    Messages
    1 625
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Consultant en Business Intelligence
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2011
    Messages : 1 625
    Par défaut
    Voilà une requête qui fonctionne,
    La je te colle +1.

    Sans rancune...
    N'oubliez pas de consulter les FAQ SAS et les cours et tutoriels SAS
    N'oubliez pas de mettre votre message à si la solution donnée résout votre problème

  19. #19
    Membre actif
    Homme Profil pro
    Ingénieur Statisticien
    Inscrit en
    Janvier 2013
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur Statisticien
    Secteur : Service public

    Informations forums :
    Inscription : Janvier 2013
    Messages : 28
    Par défaut
    Bonjour sasadm,

    Effectivement, en condensé cette solution en simple lecture est sympa aussi, si les perfs sont vraiment augmentées (j'aimerais bien comprendre pourquoi) alors je dirais banco.

    Après, l'élégance est une notion subjective...
    Si vous appréciez un code assez compact et performant, alors je rejoint entièrement votre sentiment : compact et efficace, bref : impeccable.



    Si au contraire, par élégance on considère un code explicite, facile à lire et inter-opérable, alors je continue à soutenir ma proposition.

    En effet, dans votre proposition, sasadm, vous vous reposez sur le comportement implicite de certaines instructions SQL qui sont propres à l'implémentation de SAS.
    En particulier :
    1. la possibilité de réaliser à la volée des agrégations de tests logiques, sans transtypage
    2. la possibilité d'indiquer dans une clause HAVING un alias de variable en lieu et place de la fonction d'agrégation elle-même.
    3. la possibilité de faire un fenêtrage HAVING sur des variables numériques (nb_com1 AND nb_com2) au lieu d'utiliser des tests logiques.

    Bref, cette requête n'a que très peut de chance d'être portée telle-qu'elle dans un autre SGBD.


    Mais tout est affaire de goût, et de choix

  20. #20
    Membre chevronné
    Inscrit en
    Janvier 2010
    Messages
    235
    Détails du profil
    Informations forums :
    Inscription : Janvier 2010
    Messages : 235
    Par défaut
    @jmarandet

    Petite provocation de ma part: quand vous écrivez une étape data, vous arrangez-vous pour qu'elle soit "inter-opérable" ?

    Pour le reste mon code est bien plus facile à lire/écrire/maintenir que les empilements de requêtes SQL.

    Mais comme vous dites, les goûts et les couleurs...

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Requêtes SQL avec conditions
    Par leukes dans le forum Langage SQL
    Réponses: 4
    Dernier message: 15/07/2008, 09h30
  2. Requête SQL avec condition
    Par charlix dans le forum Langage SQL
    Réponses: 2
    Dernier message: 20/03/2008, 11h38
  3. [Access] requete sql avec condition sur date
    Par qeja dans le forum Langage SQL
    Réponses: 4
    Dernier message: 25/03/2006, 23h54
  4. [MySQL] concaténer une requête sql avec condition
    Par maliak dans le forum PHP & Base de données
    Réponses: 6
    Dernier message: 10/02/2006, 12h01

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