2017-08-31 1 views
0

Je souhaite sélectionner une adresse e-mail par utilisateur en fonction de la règle suivante. Si preferred_email est Y, sélectionnez cette adresse e-mail ([email protected]). Si preferred_email n'est pas Y, utilisez cette adresse e-mail. Certains utilisateurs ne peuvent avoir qu'une valeur favorite_email de N. J'ai essayé d'utiliser une instruction case, mais elle n'a pas renvoyé le résultat correct.Sélectionnez une adresse e-mail par utilisateur

Ceci est un exemple de la table.

user_id email_address  preferred_email 
25  [email protected] N 
25  [email protected] Y 
26  [email protected] N 
27  [email protected] N 
+0

Alors, quelle est la sortie désirée? [email protected] pour les 3 user_ids? Ou [email protected] juste pour user_id # 25 et puis [email protected] et [email protected] pour # 26 et # 27 respectivement? –

+0

Quelle version d'Oracle utilisez-vous? Différentes versions viennent avec des outils différents qui peuvent être utilisés pour les questions top-n (le plus grand-n-par-groupe). – mathguy

Répondre

2
SELECT user_id, 
     MAX(email_address) KEEP (DENSE_RANK FIRST ORDER BY preferred_email DESC, ROWNUM) 
     AS email_address 
FROM your_table 
GROUP BY user_id 

ou

SELECT user_id, 
     email_address 
FROM (
    SELECT t.*, 
     ROW_NUMBER() OVER (PARTITION BY user_id 
          ORDER BY preferred_email DESC, ROWNUM) 
      AS rn 
    FROM your_table t 
) 
WHERE rn = 1; 
+0

Vous n'avez pas besoin de 'ROWNUM' dans la clause' ORDER BY' de chaque solution; s'il y a au moins une adresse e-mail préférée, une adresse essentiellement aléatoire parmi celles-ci sera sélectionnée (avec ou sans 'ROWNUM' dans la clause' ORDER BY'), et si ce n'est pas le cas, une adresse essentiellement aléatoire sera sélectionnée . 'max (email_address)' est quasiment identique au hasard dans ce contexte. – mathguy

0

Dans Oracle 12.1 et plus, cela peut se faire facilement avec la clause match_recognize, comme ceci:

select user_id, email_address 
from inputs 
match_recognize (
    partition by user_id 
    order by preferred_email desc nulls last 
    all rows per match 
    pattern (^x) 
    define x as 0 = 0 
) 
; 

Cependant, cette solution (comme ainsi que certains des autres proposés ici) présente une faiblesse potentielle: il repose sur l'ordre explicite de 'Y' vs 'N', et il suppose que ce sont les seules valeurs possibles dans la colonne preferred_email (et aussi que la colonne n'est pas nullable).

Il serait préférable, si la colonne preferred_email n'est pas contraint de non annulable et seules valeurs possibles 'Y' et 'N', d'avoir une clause d'ordre comme

order by case preferred_email when 'Y' then 0 end [...] 

Malheureusement, la clause match_recognize pouvez commander seulement par colonnes , pas par des expressions. (Dans l'espoir que cela sera traité dans le futur!) Dans ce cas, une solution globale utilisant la fonction d'agrégat FIRST/LAST, comme dans la réponse de MT0, est le meilleur choix - mais avec la clause ORDER BY modifiée en conséquence.

select user_id, 
     max(email_address) keep (dense_rank first 
       order by case preferred_email when 'Y' then 0 end) as email_address 
from  inputs 
group by user_id 
;