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 ?

Était-ce utile?

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;
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top