Jak wybrać n-ty wiersz w tabeli bazy danych SQL?

399

Chciałbym poznać niektóre (idealnie) agnostyczne sposoby wybierania n- tego wiersza z tabeli bazy danych. Interesujące byłoby również zobaczyć, jak można to osiągnąć za pomocą natywnej funkcjonalności następujących baz danych:

  • SQL Server
  • MySQL
  • PostgreSQL
  • SQLite
  • Wyrocznia

Obecnie robię coś podobnego do SQL Server 2005, ale chciałbym zobaczyć bardziej agnostyczne podejście innych:

WITH Ordered AS (
SELECT ROW_NUMBER() OVER (ORDER BY OrderID) AS RowNumber, OrderID, OrderDate
FROM Orders)
SELECT *
FROM Ordered
WHERE RowNumber = 1000000

Podziękowania za powyższy SQL: blog internetowy Firoz Ansari

Aktualizacja: Zobacz odpowiedź Troelsa Arvina dotycząca standardu SQL. Troels, czy masz jakieś linki, które możemy zacytować?

Charles Roper
źródło
3
Tak. Oto link do informacji o standardzie ISO SQL: troels.arvin.dk/db/rdbms/links/#standards
Troels Arvin
13
Wystarczy zaznaczyć, że zgodnie z definicją relacji wiersze w tabeli nie mają kolejności, więc nie można wybrać N-tego wiersza w tabeli. To, co można wybrać, to N-ty wiersz w zestawie wierszy zwrócony przez (resztę) zapytania, co jest wynikiem twojego przykładu i wszystkich innych odpowiedzi. Dla większości może to być tylko semantyka, ale wskazuje na podstawowy problem pytania. Jeśli musisz zwrócić OrderNo N, wprowadź kolumnę OrderSequenceNo w tabeli i wygeneruj ją z niezależnego generatora sekwencji po utworzeniu nowego zamówienia.
Damir Sudarevic
2
Standard SQL określa tę opcję offset x fetch first y rows only. Obecnie obsługiwany przez (przynajmniej) Postgres, Oracle12, DB2.
a_horse_w_na_name

Odpowiedzi:

349

Można to zrobić w opcjonalnych częściach standardu, ale wiele baz danych obsługuje ich własny sposób.

Naprawdę dobra strona, która mówi o tym i innych rzeczach to http://troels.arvin.dk/db/rdbms/#select-limit .

Zasadniczo PostgreSQL i MySQL obsługują niestandardowe:

SELECT...
LIMIT y OFFSET x 

Oracle, DB2 i MSSQL obsługuje standardowe funkcje okienkowania:

SELECT * FROM (
  SELECT
    ROW_NUMBER() OVER (ORDER BY key ASC) AS rownumber,
    columns
  FROM tablename
) AS foo
WHERE rownumber <= n

(które właśnie skopiowałem ze strony, do której prowadzi link, ponieważ nigdy nie używam tych baz danych)

Aktualizacja: Począwszy od PostgreSQL 8.4, obsługiwane są standardowe funkcje okienkowania, więc spodziewaj się, że drugi przykład zadziała również dla PostgreSQL.

Aktualizacja: SQLite dodał obsługę funkcji okna w wersji 3.25.0 w dniu 15.09.2018, więc obie formy działają również w SQLite.

Henrik Gustafsson
źródło
3
MySQL również używa składni OFFSET i LIMIT. Firebird używa słów FIRST i SKIP, ale są one umieszczane zaraz po WYBORZE.
Doug
7
Czy nie powinno to dotyczyć WHERE rownumber = ntylko n-tego rzędu?
Steve Bennett,
MySQL obsługuje funkcje okien od wersji 8. MariaDB od wersji 10.2
Paul Spiegel
101

PostgreSQL obsługuje funkcje okienkowania zdefiniowane w standardzie SQL, ale są one niewygodne, więc większość ludzi używa (niestandardowych) LIMIT/OFFSET :

SELECT
    *
FROM
    mytable
ORDER BY
    somefield
LIMIT 1 OFFSET 20;

W tym przykładzie wybrano 21 wiersz. OFFSET 20nakazuje Postgresowi pominięcie pierwszych 20 rekordów. Jeśli nie określisz ORDER BYklauzuli, nie ma gwarancji, który rekord otrzymasz, co jest rzadko przydatne.

Neall
źródło
31

Nie jestem pewien co do reszty, ale wiem, że SQLite i MySQL nie mają żadnej „domyślnej” kolejności wierszy. Przynajmniej w tych dwóch dialektach następujący fragment pobiera 15. pozycję z tabeli, sortując według daty / godziny, w której został dodany:

SELECT * FROM the_table ORDER BY added DESC LIMIT 1,15

(oczywiście musisz mieć dodane pole DATETIME i ustawić je na datę / czas dodania wpisu ...)

Ellen Teapot
źródło
Wygląda to na najlepszy sposób ograniczenia zapytania za pomocą wbudowanej wartości przesunięcia. Ale czy nie powinniśmy użyć tutaj 0,14? 1,15 opuści pierwszy rząd.
Gladiator
Co jednak oznacza 15? Wiem, że 1 mówi, aby uzyskać jeden rekord. Przecinek nie jest używany w przykładzie, który sprawdziłem 1keydata.com/sql/sql-limit.html
popełniono błąd
1
Faktycznie, stąd php.about.com/od/mysqlcommands/g/Limit_sql.htm , jeśli chciał chwycić wpis 15-ty nie robisz LIMIT 14, 1 (0-te jest pierwszym elementem, o długości 1
committedandroider
należy WYBRAĆ * Z ZAMÓWIENIA tabeli dodając LIMIT DESC 15,1
JerryGoyal 27.04.16
25

SQL 2005 i nowsze wersje mają tę wbudowaną funkcję. Użyj funkcji ROW_NUMBER (). Doskonale nadaje się do stron internetowych z przeglądaniem w stylu << Poprzednie i Następne >>:

Składnia:

SELECT
    *
FROM
    (
        SELECT
            ROW_NUMBER () OVER (ORDER BY MyColumnToOrderBy) AS RowNum,
            *
        FROM
            Table_1
    ) sub
WHERE
    RowNum = 23
Ben Breen
źródło
Wolę to rozwiązanie, ponieważ wydaje się bardziej proste.
FoxArc,
18

Podejrzewam, że jest to niezwykle nieefektywne, ale jest dość prostym podejściem, które działało na małym zestawie danych, na którym wypróbowałem.

select top 1 field
from table
where field in (select top 5 field from table order by field asc)
order by field desc

To otrzyma piąty przedmiot, zmień drugi najwyższy numer, aby uzyskać inny n-ty przedmiot

Tylko serwer SQL (tak myślę), ale powinien działać na starszych wersjach, które nie obsługują ROW_NUMBER ().

Tim Saunders
źródło
Użyję tego, ponieważ ROW_NUMBER () nie działa w SQL 2000 (tak, wciąż mamy klienta w SQL 2000) W szczególności zamierzam zastąpić „5” zmienną iteracyjną pętli i użyć aby skopiować i zmodyfikować kolejno każdy wiersz tabeli. Może ktoś zobaczy ten komentarz i uzna to za przydatne
Inversus
15

1 mała zmiana: n-1 zamiast n.

select *
from thetable
limit n-1, 1
Nick Berardi
źródło
która technologia
user230910
14

Sprawdź to na serwerze SQL:

Select top 10 * From emp 
EXCEPT
Select top 9 * From emp

To da ci 10-ty RZĘD tabeli emp!

Rameshwar Pawale
źródło
Podałeś już odpowiedź na to pytanie tutaj Usuń tę odpowiedź, która Twoim zdaniem jest nieprawidłowa. Jeśli uważasz, że obie odpowiedzi są prawidłowe, opublikuj obie odpowiedzi w jednym miejscu
SpringLearner
11

W przeciwieństwie do tego, co twierdzą niektóre odpowiedzi, standard SQL nie milczy na ten temat.

Od SQL: 2003 można używać „funkcji okna” do pomijania wierszy i ograniczania zestawów wyników.

W SQL: 2008 dodano nieco prostsze podejście, używając
OFFSET skip ROWS FETCH FIRST n ROWS ONLY

Osobiście nie sądzę, aby dodatek SQL: 2008 był naprawdę potrzebny, więc gdybym był ISO, trzymałbym go poza dość dużym standardem.

Troels Arvin
źródło
Fajnie, że istnieje standard, sprawia, że ​​ludzie tacy jak ja są łatwiejsi i tak miło z Microsoftu robić rzeczy w standardowy sposób :)
user230910 15.01.2018
7

Kiedy pracowaliśmy w MSSQL 2000, robiliśmy to, co nazywaliśmy „triple-flip”:

EDYTOWANE

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

Nie był elegancki i nie był szybki, ale działał.

Adam V.
źródło
Załóżmy, że masz 25 wierszy i chcesz trzecią stronę o rozmiarze 10 wierszy, tj. Rzędy 21–25. Najbardziej wewnętrzne zapytanie pobiera 30 górnych wierszy (wiersze 1-25). Środkowe zapytanie pobiera ostatnie 10 wierszy (wiersze 25-16). Zewnętrzne zapytanie zmienia ich kolejność i zwraca wiersze 16–25. Jest to wyraźnie błędne, jeśli chcesz wiersze 21–25.
Bill Karwin
Teraz to nie działa, jeśli chcemy środkowej strony. Powiedzmy, że mamy 25 wierszy i chcemy drugiej strony, tj. Wierszy 11-20. Wewnętrzne zapytanie uzyskuje górne 2 * 10 = 20 wierszy lub wiersze 1-20. Środkowe zapytanie otrzymuje ostatnie 15 wierszy: 25 - ((2-1) * 10) = 15, co daje wiersze 20-6. Ostatnie zapytanie odwraca kolejność i zwraca wiersze 6-20. Ta technika nie działa, chyba że całkowita liczba wierszy jest wielokrotnością żądanego rozmiaru strony.
Bill Karwin
Być może najlepszym wnioskiem jest to, że powinniśmy zaktualizować pozostałe instancje MS SQL Server 2000. :-) To prawie 2012 rok, a ten problem został rozwiązany na wiele sposobów od wielu lat!
Bill Karwin
@Bill Karwin: Zwróć uwagę na IF / ELSE IFbloki poniżej OuterPageSizeobliczeń - na stronach 1 i 2 OuterPageSizewartość spadnie z powrotem do 10. Na stronie 3 (wiersze 21–25) obliczenia zwrócą poprawnie 5, a na wszystkich stronach 4 i wyższych, wynik ujemny z obliczeń zostanie zastąpiony przez 0 (choć prawdopodobnie natychmiast byłoby natychmiast zwrócić pusty wiersz danych w tym momencie).
Adam V
Och, teraz rozumiem. Cóż, podtrzymuję moją opinię, że używanie MS SQL Server 2000 dzisiaj nie jest warte kłopotów.
Bill Karwin
6

SERWER SQL


Wybierz n-ty rekord od góry

SELECT * FROM (
SELECT 
ID, NAME, ROW_NUMBER() OVER(ORDER BY ID) AS ROW
FROM TABLE 
) AS TMP 
WHERE ROW = n

wybierz n-ty rekord od dołu

SELECT * FROM (
SELECT 
ID, NAME, ROW_NUMBER() OVER(ORDER BY ID DESC) AS ROW
FROM TABLE 
) AS TMP 
WHERE ROW = n
Aditya
źródło
5

Wyrocznia:

select * from (select foo from bar order by foo) where ROWNUM = x
Mark Harrison
źródło
1
where ROWNUM = xbędzie działać tylko dla x = 1 w Oracle DB. tzn. where ROWNUM = 2nie zwróci żadnych wierszy.
aff
5

W Oracle 12c możesz użyć OFFSET..FETCH..ROWS opcji zORDER BY

Na przykład, aby uzyskać trzeci rekord od góry:

SELECT * 
FROM   sometable
ORDER BY column_name
OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY;
Kaushik Nayak
źródło
4

Oto szybkie rozwiązanie twojego zamieszania.

SELECT * FROM table ORDER BY `id` DESC LIMIT N, 1

Tutaj możesz uzyskać ostatni wiersz, wypełniając N = 0, drugi ostatni przez N = 1, czwarty ostatni, wypełniając N = 3 i tak dalej.

To bardzo częste pytanie w trakcie wywiadu i jest to bardzo prosta odpowiedź.

Ponadto, jeśli chcesz Kwota, identyfikator lub jakieś porządek sortowania numerycznego, możesz przejść do funkcji CAST w MySQL.

SELECT DISTINCT (`amount`) FROM cart ORDER BY CAST( `amount` AS SIGNED ) DESC LIMIT 4 , 1

Tutaj, wypełniając N = 4, będziesz mógł zdobyć Piąty ostatni rekord najwyższej kwoty z tabeli KOSZYK. Możesz dopasować nazwę pola i tabeli i znaleźć rozwiązanie.

Amit Shah
źródło
3

DODAJ:

LIMIT n,1

To ograniczy wyniki do jednego wyniku, zaczynając od wyniku n.

Andrew G. Johnson
źródło
3

Na przykład, jeśli chcesz wybierać co 10 wiersz w MSSQL, możesz użyć;

SELECT * FROM (
  SELECT
    ROW_NUMBER() OVER (ORDER BY ColumnName1 ASC) AS rownumber, ColumnName1, ColumnName2
  FROM TableName
) AS foo
WHERE rownumber % 10 = 0

Po prostu weź MOD i zmień tutaj liczbę 10 na dowolną.

EA
źródło
3

W przypadku SQL Server ogólny sposób na przejście według numeru wiersza jest taki:

SET ROWCOUNT @row --@row = the row number you wish to work on.

Na przykład:

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

Zwróci to informację o 20 rzędzie. Pamiętaj, aby później wpisać 0

Zoe
źródło
2

LIMIT n, 1 nie działa w MS SQL Server. Myślę, że chodzi tylko o jedyną dużą bazę danych, która nie obsługuje tej składni. Szczerze mówiąc, nie jest to część standardu SQL, chociaż jest tak szeroko wspierana, że ​​powinna. We wszystkim oprócz SQL Server LIMIT działa świetnie. W przypadku serwera SQL nie udało mi się znaleźć eleganckiego rozwiązania.

Kibee
źródło
1
Z wyjątkiem Oracle, DB2, a także niemal każdej bazy danych na poziomie przedsiębiorstwa na całym świecie. PostgreSQL dotyczy jedynej bazy danych zdolnej do obsługi przedsiębiorstw, która obsługuje słowo kluczowe LIMIT, a to głównie dlatego, że będąc open source, musi być dostępny dla tłumu MySQL ignorowanego przez ACID.
David
3
@AlexD Ta „odpowiedź” została opublikowana w dawnych czasach Stackoverflow, zanim komentarze zostały zaimplementowane. Umieściłbym to jako komentarz do innej odpowiedzi, ale w tym czasie komentarze nie istniały.
Kibbee
2

Oto ogólna wersja sproca, który niedawno napisałem dla Oracle, która umożliwia dynamiczne stronicowanie / sortowanie - 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);
Greg Hurlman
źródło
2

Ale tak naprawdę, czy to nie są naprawdę tylko sztuczki dla dobrego projektu bazy danych? Kilka razy potrzebowałem takiej funkcjonalności, żeby zrobić proste, jednorazowe zapytanie, aby zrobić szybki raport. Przy każdej prawdziwej pracy używanie takich sztuczek jest zapraszaniem do kłopotów. Jeśli konieczne jest wybranie określonego wiersza, wystarczy mieć kolumnę o wartości sekwencyjnej i wykonać to.


źródło
2

W przypadku serwera SQL poniższe polecenie zwróci pierwszy wiersz z podanej tabeli.

declare @rowNumber int = 1;
    select TOP(@rowNumber) * from [dbo].[someTable];
EXCEPT
    select TOP(@rowNumber - 1) * from [dbo].[someTable];

Możesz przeglądać wartości za pomocą czegoś takiego:

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;
nPcomp
źródło
1

W Sybase SQL Anywhere:

SELECT TOP 1 START AT n * from table ORDER BY whatever

Nie zapomnij ORDER BY lub jest to bez znaczenia.

Graeme Perrow
źródło
1

T-SQL - Wybieranie N-tego rekordu z tabeli

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

Na przykład, aby wybrać 5. rekord z tabeli Pracownik, twoje zapytanie powinno być

select * from
 (select row_number() over (order by Rand() desc) as Rno,* from Employee) T where T.Rno = 5
Sangeeth Krishna
źródło
1
SELECT * FROM emp a
WHERE  n = (SELECT COUNT( _rowid)
              FROM emp b
             WHERE a. _rowid >= b. _rowid);
Rahul Sharma
źródło
1
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

Napisałem to zapytanie, aby znaleźć N-ty wiersz. Przykładem tego zapytania byłoby

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
Arjun Chiddarwar
źródło
0

niewiarygodne, że można znaleźć silnik SQL wykonujący ten ...

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
jrEving
źródło
0

Nic szczególnego, żadnych specjalnych funkcji, na wypadek, gdybyś używał Caché tak jak ja ...

SELECT TOP 1 * FROM (
  SELECT TOP n * FROM <table>
  ORDER BY ID Desc
)
ORDER BY ID ASC

Biorąc pod uwagę, że masz kolumnę identyfikatora lub kolumnę datestamp, której możesz zaufać.

Adwokat diabła
źródło
0

W ten sposób zrobiłbym to w programie DB2 SQL. Sądzę, że RRN (względny numer rekordu) jest przechowywany w tabeli przez O / S;

SELECT * FROM (                        
   SELECT RRN(FOO) AS RRN, FOO.*
   FROM FOO                         
   ORDER BY RRN(FOO)) BAR             
 WHERE BAR.RRN = recordnumber

źródło
0
select * from 
(select * from ordered order by order_id limit 100) x order by 
x.order_id desc limit 1;

Najpierw wybierz 100 najlepszych wierszy, sortując rosnąco, a następnie wybierz ostatni wiersz, sortując malejąco i ogranicz do 1. Jednak jest to bardzo kosztowne stwierdzenie, ponieważ dwukrotnie uzyskuje dostęp do danych.

Dwipam Katariya
źródło
0

Wydaje mi się, że aby być wydajnym, musisz 1) wygenerować losową liczbę od 0 do jeden mniejszą niż liczba rekordów bazy danych, i 2) być w stanie wybrać wiersz w tej pozycji. Niestety różne bazy danych mają różne generatory liczb losowych i różne sposoby wyboru wiersza w pozycji w zestawie wyników - zwykle określa się liczbę wierszy do pominięcia i liczbę wierszy, ale robi się inaczej dla różnych baz danych. Oto coś, co działa dla mnie w SQLite:

select * 
from Table 
limit abs(random()) % (select count(*) from Words), 1;

Zależy to od możliwości zastosowania podzapytania w klauzuli limitu (która w SQLite to LIMIT <recs to skip>, <recs to take>) Wybór liczby rekordów w tabeli powinien być szczególnie wydajny, ponieważ jest częścią bazy danych metadane, ale zależy to od implementacji bazy danych. Ponadto nie wiem, czy zapytanie faktycznie zbuduje zestaw wyników przed pobraniem N-tego rekordu, ale mam nadzieję, że nie będzie to konieczne. Pamiętaj, że nie określam klauzuli „sortuj według”. Może być lepiej „uporządkować według” czegoś takiego jak klucz podstawowy, który będzie miał indeks - uzyskanie N-tego rekordu z indeksu może być szybsze, jeśli baza danych nie może uzyskać N-tego rekordu z samej bazy danych bez budowania zestawu wyników .

John Deighan
źródło
0

Najbardziej odpowiednia odpowiedź, jaką widziałem w tym artykule dla serwera SQL

WITH myTableWithRows AS (
    SELECT (ROW_NUMBER() OVER (ORDER BY myTable.SomeField)) as row,*
    FROM myTable)
SELECT * FROM myTableWithRows WHERE row = 3
Knight Fighter
źródło