Comment sélectionner la nième ligne d’une table de base de données SQL ?
-
08-06-2019 - |
Question
Je souhaite apprendre des méthodes (idéalement) indépendantes de la base de données pour sélectionner le nème ligne d’une table de base de données.Il serait également intéressant de voir comment cela peut être réalisé en utilisant les fonctionnalités natives des bases de données suivantes :
- serveur SQL
- MySQL
- PostgreSQL
- SQLite
- Oracle
Je fais actuellement quelque chose comme ce qui suit dans SQL Server 2005, mais je serais intéressé de voir d'autres approches plus agnostiques :
WITH Ordered AS (
SELECT ROW_NUMBER() OVER (ORDER BY OrderID) AS RowNumber, OrderID, OrderDate
FROM Orders)
SELECT *
FROM Ordered
WHERE RowNumber = 1000000
Crédit pour le SQL ci-dessus : Blog de Firoz Ansari
Mise à jour: Voir Réponse de Troels Arvin concernant la norme SQL. Troels, avez-vous des liens que nous pouvons citer ?
La solution
Il existe des moyens de le faire dans les parties facultatives de la norme, mais de nombreuses bases de données prennent en charge leur propre manière de le faire.
Un très bon site qui parle de cela et d'autres choses est http://troels.arvin.dk/db/rdbms/#select-limit.
Fondamentalement, PostgreSQL et MySQL prennent en charge les éléments non standard :
SELECT...
LIMIT y OFFSET x
Oracle, DB2 et MSSQL prennent en charge les fonctions de fenêtrage standard :
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY key ASC) AS rownumber,
columns
FROM tablename
) AS foo
WHERE rownumber <= n
(que je viens de copier du site lié ci-dessus puisque je n'utilise jamais ces bases de données)
Mise à jour: Depuis PostgreSQL 8.4, les fonctions de fenêtrage standard sont prises en charge, attendez-vous donc à ce que le deuxième exemple fonctionne également pour PostgreSQL.
Mise à jour: SQLite a ajouté la prise en charge des fonctions de fenêtre dans la version 3.25.0 le 15/09/2018 afin que les deux formulaires fonctionnent également dans SQLite.
Autres conseils
Le LIMIT
/ OFFSET
syntaxe dans PostgreSQL est:
SELECT
*
FROM
mytable
ORDER BY
somefield
LIMIT 1 OFFSET 20;
Cet exemple sélectionne la 21ème ligne. OFFSET 20
dit à Postgres d'ignorer les 20 premiers enregistrements.Si vous ne précisez pas de ORDER BY
clause, il n'y a aucune garantie quel enregistrement vous récupérerez, ce qui est rarement utile.
Apparemment, le standard SQL reste silencieux sur le problème des limites en dehors des fonctions de fenêtrage folles, c'est pourquoi tout le monde l'implémente différemment.
Je ne suis pas sûr du reste, mais je sais que SQLite et MySQL n'ont pas d'ordre de lignes "par défaut".Dans ces deux dialectes, au moins, l'extrait suivant récupère la 15ème entrée de the_table, en la triant par date/heure d'ajout :
SELECT * FROM the_table ORDER BY added DESC LIMIT 1,15
(bien sûr, vous auriez besoin d'ajouter un champ DATETIME et de le définir sur la date/heure à laquelle l'entrée a été ajoutée...)
SQL 2005 et versions ultérieures intègrent cette fonctionnalité.Utilisez la fonction ROW_NUMBER().Il est excellent pour les pages Web avec une navigation de style << Précédent et Suivant >> :
Syntaxe:
SELECT
*
FROM
(
SELECT
ROW_NUMBER () OVER (ORDER BY MyColumnToOrderBy) AS RowNum,
*
FROM
Table_1
) sub
WHERE
RowNum = 23
Je soupçonne que c'est extrêmement inefficace, mais c'est une approche assez simple, qui a fonctionné sur un petit ensemble de données sur lequel je l'ai essayé.
select top 1 field
from table
where field in (select top 5 field from table order by field asc)
order by field desc
Cela obtiendrait le 5ème élément, changerait le deuxième numéro du haut pour obtenir un nième élément différent
Serveur SQL uniquement (je pense) mais devrait fonctionner sur les anciennes versions qui ne prennent pas en charge ROW_NUMBER().
Vérifiez-le sur SQL Server :
Select top 10 * From emp
EXCEPT
Select top 9 * From emp
Cela vous donnera la 10ème LIGNE de table emp !
1 petit changement :n-1 au lieu de n.
select *
from thetable
limit n-1, 1
Contrairement à ce que prétendent certaines réponses, le standard SQL n’est pas silencieux sur ce sujet.
Depuis SQL : 2003, vous pouvez utiliser des « fonctions de fenêtre » pour ignorer des lignes et limiter les jeux de résultats.
Et dans SQL :2008, une approche légèrement plus simple a été ajoutée, en utilisant
OFFSET sauter ROWS
FETCH FIRST n ROWS ONLY
Personnellement, je ne pense pas que l'ajout de SQL:2008 était vraiment nécessaire, donc si j'étais ISO, je l'aurais gardé en dehors d'un standard déjà assez large.
Oracle:
select * from (select foo from bar order by foo) where ROWNUM = x
Lorsque nous travaillions avec MSSQL 2000, nous faisions ce que nous appelions le « triple-flip » :
ÉDITÉ
DECLARE @InnerPageSize int
DECLARE @OuterPageSize int
DECLARE @Count int
SELECT @Count = COUNT(<column>) FROM <TABLE>
SET @InnerPageSize = @PageNum * @PageSize
SET @OuterPageSize = @Count - ((@PageNum - 1) * @PageSize)
IF (@OuterPageSize < 0)
SET @OuterPageSize = 0
ELSE IF (@OuterPageSize > @PageSize)
SET @OuterPageSize = @PageSize
DECLARE @sql NVARCHAR(8000)
SET @sql = 'SELECT * FROM
(
SELECT TOP ' + CAST(@OuterPageSize AS nvarchar(5)) + ' * FROM
(
SELECT TOP ' + CAST(@InnerPageSize AS nvarchar(5)) + ' * FROM <TABLE> ORDER BY <column> ASC
) AS t1 ORDER BY <column> DESC
) AS t2 ORDER BY <column> ASC'
PRINT @sql
EXECUTE sp_executesql @sql
Ce n’était ni élégant ni rapide, mais cela fonctionnait.
SERVEUR SQL
Sélectionnez le nième enregistrement à partir du haut
SELECT * FROM (
SELECT
ID, NAME, ROW_NUMBER() OVER(ORDER BY ID) AS ROW
FROM TABLE
) AS TMP
WHERE ROW = n
sélectionner le nième enregistrement à partir du bas
SELECT * FROM (
SELECT
ID, NAME, ROW_NUMBER() OVER(ORDER BY ID DESC) AS ROW
FROM TABLE
) AS TMP
WHERE ROW = n
Voici une solution rapide à votre confusion.
SELECT * FROM table ORDER BY `id` DESC LIMIT N, 1
Ici, vous pouvez obtenir la dernière ligne en remplissant N = 0, l'avant-dernière en N = 1, la quatrième dernière en remplissant N = 3 et ainsi de suite.
C'est une question très courante lors de l'entretien et c'est très simple et très simple.
De plus, si vous souhaitez un montant, un identifiant ou un ordre de tri numérique, vous pouvez opter pour la fonction CAST dans MySQL.
SELECT DISTINCT (`amount`) FROM cart ORDER BY CAST( `amount` AS SIGNED ) DESC LIMIT 4 , 1
Ici, en remplissant N = 4, vous pourrez obtenir le cinquième dernier enregistrement du montant le plus élevé de la table CART.Vous pouvez adapter le nom de votre champ et de votre table et trouver une solution.
AJOUTER:
LIMIT n,1
Cela limitera les résultats à un résultat commençant au résultat n.
Par exemple, si vous souhaitez sélectionner toutes les 10 lignes dans MSSQL, vous pouvez utiliser ;
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY ColumnName1 ASC) AS rownumber, ColumnName1, ColumnName2
FROM TableName
) AS foo
WHERE rownumber % 10 = 0
Prenez simplement le MOD et changez le numéro 10 ici comme vous le souhaitez.
LIMIT n,1 ne fonctionne pas dans MS SQL Server.Je pense que c'est à peu près la seule base de données majeure qui ne prend pas en charge cette syntaxe.Pour être honnête, cela ne fait pas partie du standard SQL, même s'il est si largement pris en charge qu'il devrait l'être.Dans tout sauf SQL Server LIMIT fonctionne très bien.Pour le serveur SQL, je n'ai pas réussi à trouver une solution élégante.
Voici une version générique d'un sproc que j'ai récemment écrit pour Oracle et qui permet une pagination/tri dynamique - HTH
-- p_LowerBound = first row # in the returned set; if second page of 10 rows,
-- this would be 11 (-1 for unbounded/not set)
-- p_UpperBound = last row # in the returned set; if second page of 10 rows,
-- this would be 20 (-1 for unbounded/not set)
OPEN o_Cursor FOR
SELECT * FROM (
SELECT
Column1,
Column2
rownum AS rn
FROM
(
SELECT
tbl.Column1,
tbl.column2
FROM MyTable tbl
WHERE
tbl.Column1 = p_PKParam OR
tbl.Column1 = -1
ORDER BY
DECODE(p_sortOrder, 'A', DECODE(p_sortColumn, 1, Column1, 'X'),'X'),
DECODE(p_sortOrder, 'D', DECODE(p_sortColumn, 1, Column1, 'X'),'X') DESC,
DECODE(p_sortOrder, 'A', DECODE(p_sortColumn, 2, Column2, sysdate),sysdate),
DECODE(p_sortOrder, 'D', DECODE(p_sortColumn, 2, Column2, sysdate),sysdate) DESC
))
WHERE
(rn >= p_lowerBound OR p_lowerBound = -1) AND
(rn <= p_upperBound OR p_upperBound = -1);
Mais en réalité, tout cela n'est-il pas simplement des astuces de salon pour une bonne conception de base de données en premier lieu ?Les rares fois où j'ai eu besoin d'une fonctionnalité comme celle-ci, c'était pour une simple requête ponctuelle permettant de créer un rapport rapide.Pour tout travail réel, utiliser des astuces comme celles-ci suscite des ennuis.Si la sélection d'une ligne particulière est nécessaire, ayez simplement une colonne avec une valeur séquentielle et terminez-la.
Dans Oracle 12c, vous pouvez utiliser OFFSET..FETCH..ROWS
option avec ORDER BY
Par exemple, pour obtenir le 3ème enregistrement en partant du haut :
SELECT *
FROM sometable
ORDER BY column_name
OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY;
Dans Sybase SQL Anywhere :
SELECT TOP 1 START AT n * from table ORDER BY whatever
N'oubliez pas le ORDER BY ou cela n'a aucun sens.
T-SQL - Sélection du Nième numéro d'enregistrement dans une table
select * from
(select row_number() over (order by Rand() desc) as Rno,* from TableName) T where T.Rno = RecordNumber
Where RecordNumber --> Record Number to Select
TableName --> To be Replaced with your Table Name
Par ex.pour sélectionner le 5 ème enregistrement d'une table Employee, votre requête doit être
select * from
(select row_number() over (order by Rand() desc) as Rno,* from Employee) T where T.Rno = 5
SELECT * FROM emp a
WHERE n = (SELECT COUNT( _rowid)
FROM emp b
WHERE a. _rowid >= b. _rowid);
SELECT
top 1 *
FROM
table_name
WHERE
column_name IN (
SELECT
top N column_name
FROM
TABLE
ORDER BY
column_name
)
ORDER BY
column_name DESC
J'ai écrit cette requête pour trouver la Nième ligne.Un exemple avec cette requête serait
SELECT
top 1 *
FROM
Employee
WHERE
emp_id IN (
SELECT
top 7 emp_id
FROM
Employee
ORDER BY
emp_id
)
ORDER BY
emp_id DESC
Pour SQL Server, une méthode générique pour utiliser le numéro de ligne est la suivante :
SET ROWCOUNT @row --@row = the row number you wish to work on.
Par exemple:
set rowcount 20 --sets row to 20th row
select meat, cheese from dbo.sandwich --select columns from table at 20th row
set rowcount 0 --sets rowcount back to all rows
Cela renverra les informations de la 20e ligne.Assurez-vous de mettre le nombre de lignes 0 par la suite.
incroyable que vous puissiez trouver un moteur SQL exécutant celui-ci...
WITH sentence AS
(SELECT
stuff,
row = ROW_NUMBER() OVER (ORDER BY Id)
FROM
SentenceType
)
SELECT
sen.stuff
FROM sentence sen
WHERE sen.row = (ABS(CHECKSUM(NEWID())) % 100) + 1
Rien d'extraordinaire, pas de fonctions spéciales, au cas où vous utiliseriez Caché comme moi...
SELECT TOP 1 * FROM (
SELECT TOP n * FROM <table>
ORDER BY ID Desc
)
ORDER BY ID ASC
Étant donné que vous disposez d’une colonne ID ou d’une colonne d’horodatage à laquelle vous pouvez faire confiance.
C'est ainsi que je procéderais dans DB2 SQL, je crois que le RRN (numéro d'enregistrement relatif) est stocké dans la table par le système d'exploitation ;
SELECT * FROM (
SELECT RRN(FOO) AS RRN, FOO.*
FROM FOO
ORDER BY RRN(FOO)) BAR
WHERE BAR.RRN = recordnumber
select * from
(select * from ordered order by order_id limit 100) x order by
x.order_id desc limit 1;
Sélectionnez d’abord les 100 premières lignes en les classant par ordre croissant, puis sélectionnez la dernière ligne en les classant par ordre décroissant et en limitant à 1.Cependant, il s'agit d'une déclaration très coûteuse car elle accède aux données deux fois.
Il me semble que, pour être efficace, vous devez 1) générer un nombre aléatoire compris entre 0 et un de moins que le nombre d'enregistrements de la base de données, et 2) pouvoir sélectionner la ligne à cette position.Malheureusement, différentes bases de données ont différents générateurs de nombres aléatoires et différentes manières de sélectionner une ligne à une position dans un ensemble de résultats - vous spécifiez généralement le nombre de lignes à ignorer et le nombre de lignes souhaitées, mais cela se fait différemment pour différentes bases de données.Voici quelque chose qui fonctionne pour moi dans SQLite :
select *
from Table
limit abs(random()) % (select count(*) from Words), 1;
Cela dépend de la capacité à utiliser une sous-requête dans la clause limit (qui dans SQLite est LIMIT <recs to skip>,<recs to take>). La sélection du nombre d'enregistrements dans une table devrait être particulièrement efficace, car elle fait partie de la base de données. métadonnées, mais cela dépend de la mise en œuvre de la base de données.De plus, je ne sais pas si la requête construira réellement le jeu de résultats avant de récupérer le Nième enregistrement, mais j'espère que ce n'est pas nécessaire.Notez que je ne spécifie pas de clause « order by ».Il serait peut-être préférable de "classer par" quelque chose comme la clé primaire, qui aura un index - obtenir le Nième enregistrement à partir d'un index pourrait être plus rapide si la base de données ne peut pas obtenir le Nième enregistrement de la base de données elle-même sans créer le jeu de résultats. .
Pour le serveur SQL, ce qui suit renverra la première ligne de la table donnée.
declare @rowNumber int = 1;
select TOP(@rowNumber) * from [dbo].[someTable];
EXCEPT
select TOP(@rowNumber - 1) * from [dbo].[someTable];
Vous pouvez parcourir les valeurs avec quelque chose comme ceci :
WHILE @constVar > 0
BEGIN
declare @rowNumber int = @consVar;
select TOP(@rowNumber) * from [dbo].[someTable];
EXCEPT
select TOP(@rowNumber - 1) * from [dbo].[someTable];
SET @constVar = @constVar - 1;
END;