Comment puis-je faire le top 1 dans Oracle?

251

Comment dois-je procéder comme suit?

select top 1 Fname from MyTbl

Dans Oracle 11g ?

Or
la source
3
Pouvez-vous nous dire l'ordre selon lequel vous voulez 'top 1'?
Andrew Wolfe
1
Tout d'abord, vous ne devriez jamais compter sur le moteur DB pour le faire. Si vous voulez savoir des choses comme ça, mettez un séquenceur. Lorsque vous faites cela, il est garanti qu'ils seront numérotés dans l'ordre où ils ont été insérés.
FlyingGuy
1
Matériel très utile sur ce sujet use-the-index-luke.com/sql/partial-results/top-n-queries
Ilia Maskov

Réponses:

257

Si vous souhaitez uniquement une première ligne sélectionnée, vous pouvez:

select fname from MyTbl where rownum = 1

Vous pouvez également utiliser des fonctions analytiques pour commander et prendre le top x:

select max(fname) over (rank() order by some_factor) from MyTbl
mcpeterson
la source
54
C'est bien si vous ne voulez qu'une seule ligne et que vous ne vous en souciez pas. Si vous voulez des lignes spécifiques, comme l'enregistrement le plus récent, vous devez faire le tri dans une sous-sélection, comme la réponse de Vash. Oracle attribue des rownums avant le tri.
Scott Bailey
4
@Scott yup. c'est exact. Et Patrick, bon point, je pense que la syntaxe est incorrecte à ce sujet. Cela devrait vraiment être un garder (dense_rank () dernier ...)
mcpeterson
2
La différence entre le premier et le deuxième exemple est que le premier sélectionne une ligne (n'importe quelle ligne, sans ordre). Le deuxième exemple obtient la valeur de la première ligne, sans faire de requête interne de commande (selon les exemples ci-dessous).
JulesLt
3
La syntaxe n'est pas correcte dans: sélectionnez max (fname) sur (classement () par some_factor) de MyTbl
Stéphane Gerber
1
@jclozano "pas une fonctionnalité très connue d'Oracle" - Avec respect, je vous prie de différer. Cela est important car «des fonctionnalités peu connues» impliquent généralement de l'obscurité et pourraient donc suggérer que nous devrions éviter de les utiliser. Ce n'est pas obscur et son utilisation ne doit pas être évitée.
Sepster
166
SELECT *
  FROM (SELECT * FROM MyTbl ORDER BY Fname )
 WHERE ROWNUM = 1;
Damian Leszczyński - Vash
la source
8
Cette réponse obtient correctement la ligne TOP (ordonne les résultats avant de restreindre sur ROWNUM).
JulesLt
Cette réponse n'est pas une traduction exacte - la requête d'origine n'a pas de ORDER BY et ne renvoie pas toutes les colonnes du tableau.
OMG Ponies
Je me tiens corrigé (voir ci-dessous). Permutera les votes une fois le temps écoulé.
JulesLt
7
@OMGPonies oui. mais c'est probablement ce que la plupart des gens veulent réellement venir sur cette page via googler leur problème
NimChimpsky
4
Cela doit être à coup sûr la réponse gagnante dans ce fil. Je pourrais ajouter une note que pour top Xquelqu'un peut le changer enWHERE ROWNUM <= X
SomethingSomething
31

Avec Oracle 12c (juin 2013), vous pouvez l'utiliser comme suit.

SELECT * FROM   MYTABLE
--ORDER BY COLUMNNAME -OPTIONAL          
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
MSK
la source
6
Commande intéressante, j'utilise ici 12c et ce OFFSET 0 ROWSn'est apparemment pas nécessaire, vous pouvez utiliser FETCH NEXT 1 ROWS ONLYou même FETCH FIRST ROW ONLY, l'ordre par est important ou ce sera équivalent à simplement utiliser un WHERE rownum = 1. Je l'ai même essayé dans une instruction OUTER APPLY et cela a fonctionné comme la fonction TOP de Ms-SQL.
Rafael Merlin
Vous avez raison @RafaelMerlin. Après votre message, j'ai reconnu que OFFSET 0 ROWS n'est pas nécessaire. Il serait utile lors de la récupération de données entre le X supérieur et le Y supérieur.
MSK
1
Plus d'exemples: oracle-base.com/articles/12c/…
FixFaier
Jusqu'ici tout va bien, avec un point manquant important qui est TIES. Reportez - vous ceci pour les cas où des liens se produisent pour la version 12c +et12c -
Barbaros Özhan
10

Vous pouvez utiliser ROW_NUMBER()une ORDER BYclause en sous-requête et utiliser cette colonne en remplacement de TOP N. Cela peut être expliqué étape par étape.

Voir le tableau ci-dessous qui a deux colonnes NAMEet DT_CREATED.

entrez la description de l'image ici

Si vous devez prendre uniquement les deux premières dates indépendamment de NAME, vous pouvez utiliser la requête ci-dessous. La logique a été écrite dans la requête

-- The number of records can be specified in WHERE clause
SELECT RNO,NAME,DT_CREATED
FROM
(
    -- Generates numbers in a column in sequence in the order of date
    SELECT ROW_NUMBER() OVER (ORDER BY DT_CREATED) AS RNO,
    NAME,DT_CREATED
    FROM DEMOTOP
)TAB
WHERE RNO<3;

RÉSULTAT

entrez la description de l'image ici

Dans certaines situations, nous devons sélectionner des TOP Nrésultats respectifs à chacun NAME. Dans ce cas, nous pouvons utiliser PARTITION BYune ORDER BYclause en sous-requête. Reportez-vous à la requête ci-dessous.

-- The number of records can be specified in WHERE clause
SELECT RNO,NAME,DT_CREATED
FROM
(
  --Generates numbers in a column in sequence in the order of date for each NAME
    SELECT ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY DT_CREATED) AS RNO,
    NAME,DT_CREATED
    FROM DEMOTOP
)TAB
WHERE RNO<3;

RÉSULTAT

entrez la description de l'image ici

Sarath Avanavu
la source
1
L'utilisation de ROW_NUMBER () ... est une solution plus correcte que dans la réponse au sujet. Un problème avec cette solution (et avec la variante max (champ) aussi) que vous ne pouvez pas faire des choses comme "sélectionnez ... (sélectionnez ROW_NUMBER () ...) pour la mise à jour ;"
Alexo Po.
Et c'est parfois très important en PL / SQL (désolé, impossible de modifier le commentaire précédent en 5 minutes maximum).
Alexo Po.
Dans ce cas, nous pouvons utiliser CTE comme dans la partie extérieure. Droite? @Alexo Po.
Sarath Avanavu
Je pense que je ne vous comprends pas. La clause for update peut être utilisée lorsque ROWID est "facilement" conservé par Oracle. Ainsi, le regroupement (et le regroupement en raison de l'utilisation de la clause analytique) masque le véritable ROWID et les lignes ne peuvent pas être verrouillées. Et deuxièmement, CTE ( with (select ... ) as clause) ne change rien à ce problème, CTE vise simplement à lire et à prendre en charge les requêtes. Droite? @Sarath Avanavu
Alexo Po.
Remarque sur moi. Le problème avec ROWID se produit en fait spécifiquement en raison de la condition RNO <3 , dans ce cas, la valeur de RNO n'est pas connectée à ROWID, c'est pourquoi Oracle ne peut pas verrouiller les lignes.
Alexo Po.
9

Vous pouvez faire quelque chose comme

    SELECT *
      FROM (SELECT Fname FROM MyTbl ORDER BY Fname )
 WHERE rownum = 1;

Vous pouvez également utiliser les fonctions analytiques RANK et / ou DENSE_RANK , mais ROWNUM est probablement la plus simple.

Suman
la source
1
pouvez - vous aider avec quelques exemples de rang , etc.
breakfreehg
9
select * from (
    select FName from MyTbl
)
where rownum <= 1;
a'r
la source
5

Utilisation:

SELECT x.*
  FROM (SELECT fname 
          FROM MyTbl) x
 WHERE ROWNUM = 1

Si vous utilisez Oracle9i +, vous pouvez envisager d' utiliser des fonctions analytiques comme ROW_NUMBER (), mais elles ne fonctionneront pas aussi bien que ROWNUM .

Poneys OMG
la source
1
Belle réponse mais contient une petite faute de frappe. Où vous dites qu'Oracle9i + ne devrait pas être 8i? download-west.oracle.com/docs/cd/A87860_01/doc/server.817/…
Ian Carpenter
@carpenteri: Certes, les analyses étaient disponibles dans 8i - je ne me souviens pas des détails, mais les analyses n'étaient pas vraiment accessibles au public avant 9i.
OMG Ponies
Petit commentaire - La réponse de Vash ci-dessous comprend un ORDER BY sur la requête interne qui est critique si vous voulez la valeur TOP de fname, plutôt que 'first' (qui peut être n'importe quoi, probablement la première ligne insérée). Cela pourrait-il valoir la peine d'être modifié?
JulesLt
@JulesLt: La requête fournie par l'OP n'inclut pas de ORDER BY, c'est donc la réponse qui représente et la traduction exacte vers la syntaxe Oracle.
OMG Ponies
Ma mauvaise compréhension de la syntaxe SQL SERVER TOP (présumé à tort qu'elle était similaire à FIRST dans RANK, pas à ROWNUM). A voté.
JulesLt
3

Pour sélectionner la première ligne d'un tableau et pour sélectionner une ligne dans un tableau, deux tâches différentes nécessitent une requête différente. Il existe de nombreuses façons de le faire. Quatre d'entre eux sont:

Première

select  max(Fname) from MyTbl;

Seconde

select  min(Fname) from MyTbl;

Troisième

select  Fname from MyTbl  where rownum = 1;

Quatrième

select  max(Fname) from MyTbl where rowid=(select  max(rowid) from MyTbl)
Vikas Hardia
la source
3

J'ai eu le même problème, et je peux résoudre ce problème avec cette solution:

select a.*, rownum 
from (select Fname from MyTbl order by Fname DESC) a
where
rownum = 1

Vous pouvez commander votre résultat avant d'avoir la première valeur en haut.

Bonne chance

user2607028
la source