Bagaimana cara memilih baris ke-n dalam tabel database SQL?

399

Saya tertarik mempelajari beberapa cara agnostik database untuk memilih baris ke- n dari tabel database. Ini juga akan menarik untuk melihat bagaimana ini dapat dicapai dengan menggunakan fungsionalitas asli dari database berikut:

  • SQL Server
  • MySQL
  • PostgreSQL
  • SQLite
  • Peramal

Saat ini saya sedang melakukan sesuatu seperti berikut ini di SQL Server 2005, tetapi saya akan tertarik melihat pendekatan yang lebih agnostik:

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

Penghargaan untuk SQL di atas: Weblog Firoz Ansari

Pembaruan: Lihat Troels jawaban Arvin mengenai standar SQL. Troel, apakah Anda punya tautan yang bisa kami kutip?

Charles Roper
sumber
3
Iya. Berikut ini tautan ke informasi tentang standar ISO SQL: troels.arvin.dk/db/rdbms/links/#standards
Troels Arvin
13
Hanya untuk menunjukkan bahwa dengan definisi suatu relasi, baris dalam tabel tidak memiliki urutan, sehingga baris ke-N dalam tabel tidak dapat dipilih. Apa yang dapat dipilih adalah baris ke-N di baris-set yang dikembalikan oleh (sisa) kueri, yang merupakan apa yang dilakukan oleh contoh Anda dan semua jawaban lainnya. Bagi sebagian besar ini mungkin hanya semantik, tetapi itu menunjuk ke masalah mendasar dari pertanyaan itu. Jika Anda perlu kembali OrderNo N, maka perkenalkan kolom OrderSequenceNo di tabel dan hasilkan dari generator urutan independen saat membuat pesanan baru.
Damir Sudarevic
2
Standar SQL mendefinisikan opsi offset x fetch first y rows only. Saat ini didukung oleh (setidaknya) Postgres, Oracle12, DB2.
a_horse_with_no_name

Jawaban:

349

Ada beberapa cara untuk melakukan ini di bagian opsional standar, tetapi banyak database mendukung cara mereka sendiri untuk melakukannya.

Situs yang sangat bagus yang membicarakan hal ini dan yang lainnya adalah http://troels.arvin.dk/db/rdbms/#select-limit .

Pada dasarnya, PostgreSQL dan MySQL mendukung non-standar:

SELECT...
LIMIT y OFFSET x 

Oracle, DB2 dan MSSQL mendukung fungsi windowing standar:

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

(yang saya salin dari situs yang ditautkan di atas karena saya tidak pernah menggunakan DB tersebut)

Pembaruan: Pada PostgreSQL 8.4 fungsi windowing standar didukung, jadi harap contoh kedua juga berfungsi untuk PostgreSQL.

Pembaruan: SQLite menambahkan dukungan fungsi jendela di versi 3.25.0 pada 2018-09-15 sehingga kedua bentuk juga berfungsi di SQLite.

Henrik Gustafsson
sumber
3
MySQL juga menggunakan sintaks OFFSET dan LIMIT. Firebird menggunakan kata kunci FIRST dan SKIP, tetapi mereka ditempatkan tepat setelah SELECT.
Doug
7
Bukankah seharusnya hanya WHERE rownumber = nmendapatkan baris ke-n saja?
Steve Bennett
MySQL mendukung fungsi jendela sejak versi 8. MariaDB sejak versi 10.2
Paul Spiegel
102

PostgreSQL mendukung fungsi windowing seperti yang didefinisikan oleh standar SQL, tetapi mereka canggung, jadi kebanyakan orang menggunakan (non-standar) LIMIT/OFFSET :

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

Contoh ini memilih baris ke-21. OFFSET 20memberitahu Postgres untuk melewati 20 catatan pertama. Jika Anda tidak menentukan ORDER BYklausa, tidak ada jaminan catatan mana yang akan Anda dapatkan, yang jarang berguna.

Neall
sumber
31

Saya tidak yakin tentang yang lain, tapi saya tahu SQLite dan MySQL tidak memiliki urutan baris "default". Dalam dua dialek itu, paling tidak, cuplikan berikut ini mengambil entri ke-15 dari the_table, mengurutkan berdasarkan tanggal / waktu penambahannya:

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

(tentu saja, Anda harus memiliki bidang DATETIME yang ditambahkan, dan mengaturnya ke tanggal / waktu ketika entri ditambahkan ...)

Ellen Teapot
sumber
Ini terlihat seperti cara terbaik untuk membatasi kueri dengan nilai offset inline. Tapi bukankah kita harus menggunakan 0,14 di sini? 1,15 akan meninggalkan baris pertama.
Gladiator
Apa artinya ke 15? Saya tahu kata 1 untuk mendapatkan satu catatan. Koma tidak digunakan dalam contoh aku memeriksa 1keydata.com/sql/sql-limit.html
committedandroider
1
Sebenarnya, dari sini php.about.com/od/mysqlcommands/g/Limit_sql.htm , jika Anda ingin mengambil entri ke-15 tidak akan Anda lakukan LIMIT 14, 1 (0 adalah elemen pertama, 1 panjang
berkomitmenandroid
sholud be SELECT * FROM the_table ORDER BY tambah DESC LIMIT 15,1
JerryGoyal
25

SQL 2005 dan di atasnya memiliki fitur ini bawaan. Gunakan fungsi ROW_NUMBER (). Sangat bagus untuk halaman web dengan penjelajahan gaya << Sebelumnya dan Selanjutnya >>:

Sintaksis:

SELECT
    *
FROM
    (
        SELECT
            ROW_NUMBER () OVER (ORDER BY MyColumnToOrderBy) AS RowNum,
            *
        FROM
            Table_1
    ) sub
WHERE
    RowNum = 23
Ben Breen
sumber
Saya lebih suka solusi ini, karena rasanya lebih lurus ke depan.
FoxArc
18

Saya menduga ini sangat tidak efisien tetapi pendekatan yang cukup sederhana, yang bekerja pada dataset kecil yang saya coba.

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

Ini akan mendapatkan item ke-5, mengubah nomor top kedua untuk mendapatkan item ke-n yang berbeda

Hanya SQL server (saya pikir) tetapi harus berfungsi pada versi yang lebih lama yang tidak mendukung ROW_NUMBER ().

Tim Saunders
sumber
Saya akan menggunakan ini karena ROW_NUMBER () tidak berfungsi di SQL 2000 (ya, kami masih memiliki klien di SQL 2000) Secara khusus, saya akan mengganti '5' dengan variabel iterator dari loop dan gunakan itu untuk menyalin dan memodifikasi setiap baris tabel secara bergantian. Mungkin seseorang akan melihat komentar ini dan menemukan ini berguna
Inversus
15

1 perubahan kecil: n-1 bukannya n.

select *
from thetable
limit n-1, 1
Nick Berardi
sumber
teknologi yang mana?
user230910
14

Verifikasi di SQL Server:

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

Ini akan memberi Anda 10 baris tabel emp!

Rameshwar Pawale
sumber
Anda telah memberikan jawaban untuk pertanyaan ini di sini. Hapus jawaban yang menurut Anda tidak benar. Jika Anda berpikir kedua jawaban itu benar, maka poskan kedua jawaban itu di satu tempat
SpringLearner
11

Bertentangan dengan apa yang diklaim oleh beberapa jawaban, standar SQL tidak membisu mengenai subjek ini.

Sejak SQL: 2003, Anda telah dapat menggunakan "fungsi jendela" untuk melewati baris dan membatasi set hasil.

Dan dalam SQL: 2008, pendekatan yang sedikit lebih sederhana telah ditambahkan, menggunakan
OFFSET skip ROWS FETCH FIRST n ROWS ONLY

Secara pribadi, saya tidak berpikir bahwa penambahan SQL: 2008 benar-benar diperlukan, jadi jika saya ISO, saya akan membuatnya keluar dari standar yang sudah agak besar.

Troels Arvin
sumber
Sangat menyenangkan bahwa ada standar, membuat orang-orang seperti hidup saya lebih mudah, dan begitu baik dari microsoft untuk melakukan hal-hal dengan cara standar :)
user230910
7

Ketika kami dulu bekerja di MSSQL 2000, kami melakukan apa yang kami sebut "triple-flip":

Diedit

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

Itu tidak elegan, dan tidak cepat, tetapi berhasil.

Adam V
sumber
Katakanlah Anda memiliki 25 baris dan Anda ingin halaman ketiga dari 10 pageize baris, yaitu baris 21-25. Kueri paling dalam mendapat 30 baris teratas (baris 1-25). Kueri tengah mendapatkan 10 baris terakhir (baris 25-16). Kueri luar mengatur ulang mereka dan mengembalikan baris 16-25. Ini jelas salah jika Anda menginginkan baris 21-25.
Bill Karwin
Sekarang tidak berfungsi jika kita menginginkan halaman tengah. Katakanlah kita memiliki 25 baris dan kita ingin halaman kedua, yaitu baris 11-20. Kueri dalam mendapatkan 2 * 10 = 20 baris teratas, atau baris 1-20. Kueri tengah mendapatkan 15 baris terakhir: 25 - ((2-1) * 10) = 15, yang menghasilkan baris 20-6. Kueri terakhir membalik urutan dan mengembalikan baris 6-20. Teknik ini tidak berfungsi, kecuali jumlah total baris adalah kelipatan dari ukuran halaman yang Anda inginkan.
Bill Karwin
Mungkin kesimpulan terbaik adalah bahwa kita harus memutakhirkan instance MS SQL Server 2000 yang tersisa. :-) Sudah hampir 2012, dan masalah ini telah dipecahkan dengan cara yang lebih baik selama bertahun-tahun!
Bill Karwin
@ Bill Karwin: Perhatikan IF / ELSE IFblok di bawah OuterPageSizeperhitungan - pada halaman 1 dan 2, mereka akan menurunkan OuterPageSizenilainya kembali menjadi 10. Pada halaman 3 (baris 21-25) perhitungan akan kembali dengan benar 5, dan pada semua halaman 4 dan lebih besar, hasil negatif dari perhitungan akan diganti dengan 0 (meskipun mungkin akan lebih cepat untuk mengembalikan baris data kosong segera pada saat itu).
Adam V
Oh saya mengerti sekarang. Yah, saya berdiri pada pendapat saya bahwa menggunakan MS SQL Server 2000 hari ini tidak sepadan dengan masalahnya.
Bill Karwin
6

SERVER SQL


Pilih catatan ke-n dari atas

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

pilih catatan ke-n dari bawah

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

Peramal:

select * from (select foo from bar order by foo) where ROWNUM = x
Mark Harrison
sumber
1
where ROWNUM = xhanya akan bekerja untuk x = 1 di Oracle DB. yaitu where ROWNUM = 2tidak akan mengembalikan baris.
aff
5

Di Oracle 12c, Anda dapat menggunakan OFFSET..FETCH..ROWS opsi denganORDER BY

Misalnya, untuk mendapatkan catatan ke-3 dari atas:

SELECT * 
FROM   sometable
ORDER BY column_name
OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY;
Kaushik Nayak
sumber
4

Inilah solusi cepat dari kebingungan Anda.

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

Di sini Anda bisa mendapatkan baris Terakhir dengan Mengisi N = 0, Kedua terakhir dengan N = 1, Keempat Terakhir Dengan Mengisi N = 3 dan seterusnya.

Ini adalah pertanyaan yang sangat umum selama wawancara dan ini sangat sederhana.

Lebih lanjut Jika Anda ingin Jumlah, ID atau Urutan Sortasi Numerik daripada yang Anda gunakan untuk fungsi CAST di MySQL.

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

Di Sini Dengan mengisi N = 4 Anda akan bisa mendapatkan Catatan Terakhir Kelima dari Jumlah Tertinggi dari tabel CART. Anda dapat menyesuaikan nama bidang dan tabel Anda dan menghasilkan solusi.

Amit Shah
sumber
3

MENAMBAHKAN:

LIMIT n,1

Itu akan membatasi hasil untuk satu hasil mulai dari hasil n.

Andrew G. Johnson
sumber
3

Misalnya, jika Anda ingin memilih setiap baris ke-10 di MSSQL, Anda dapat menggunakan;

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

Ambil saja MOD dan ubah nomor 10 di sini nomor yang Anda inginkan.

EA
sumber
3

Untuk SQL Server, cara umum untuk menggunakan nomor baris adalah sebagai berikut:

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

Sebagai contoh:

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

Ini akan mengembalikan informasi baris ke-20. Pastikan untuk memasukkan rowcount 0 sesudahnya.

Zoe
sumber
2

LIMIT n, 1 tidak berfungsi di MS SQL Server. Saya pikir ini hanya tentang satu-satunya database utama yang tidak mendukung sintaks itu. Agar adil, itu bukan bagian dari standar SQL, meskipun begitu banyak didukung sehingga seharusnya. Dalam segala hal kecuali LIMIT SQL server berfungsi dengan baik. Untuk SQL server, saya belum dapat menemukan solusi yang elegan.

Kibbee
sumber
1
Kecuali untuk Oracle, DB2, hampir setiap basis data tingkat perusahaan di seluruh dunia. PostgreSQL adalah tentang satu-satunya basis data perusahaan yang mampu mendukung kata kunci LIMIT, dan itu sebagian besar karena menjadi open source perlu didekati oleh kerumunan MySQL ACID yang acuh.
David
3
@AlexD "Jawaban" ini diposting kembali di masa lalu Stackoverflow sebelum komentar diterapkan. Saya akan memposting ini sebagai komentar untuk jawaban lain, tetapi pada saat itu, komentar tidak ada.
Kibbee
2

Berikut ini adalah versi umum dari sproc yang baru-baru ini saya tulis untuk Oracle yang memungkinkan paging / sorting dinamis - 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
sumber
2

Tapi sungguh, bukankah ini hanya trik sulap untuk desain database yang bagus? Beberapa kali saya membutuhkan fungsionalitas seperti ini, permintaan sederhana untuk membuat laporan cepat. Untuk pekerjaan nyata, menggunakan trik seperti ini mengundang masalah. Jika memilih baris tertentu diperlukan maka hanya memiliki kolom dengan nilai sekuensial dan selesai dengannya.


sumber
2

Untuk SQL server, berikut ini akan mengembalikan baris pertama dari tabel pemberian.

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

Anda dapat mengulangi nilai dengan sesuatu seperti ini:

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
sumber
1

Dalam Sybase SQL Anywhere:

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

Jangan lupa ORDER OLEH atau tidak ada artinya.

Graeme Perrow
sumber
1

T-SQL - Memilih NNth RecordNumber dari Tabel

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

Untuk misalnya memilih catatan ke-5 dari tabel Karyawan, permintaan Anda seharusnya

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

Saya telah menulis kueri ini untuk menemukan baris ke-N. Contoh dengan kueri ini adalah

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
sumber
0

sulit dipercaya bahwa Anda dapat menemukan mesin SQL yang menjalankan yang ini ...

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
sumber
0

Tidak ada yang mewah, tidak ada fungsi khusus, jika Anda menggunakan Caché seperti yang saya lakukan ...

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

Mengingat Anda memiliki kolom ID atau kolom stempel data yang dapat Anda percayai.

Pengacara Setan
sumber
0

Ini adalah bagaimana saya akan melakukannya dalam DB2 SQL, saya percaya RRN (nomor catatan relatif) disimpan dalam tabel oleh O / S;

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

sumber
0
select * from 
(select * from ordered order by order_id limit 100) x order by 
x.order_id desc limit 1;

Pertama-tama pilih 100 baris teratas dengan memesan dalam naik dan kemudian pilih baris terakhir dengan memesan dalam turun dan batasi ke 1. Namun ini adalah pernyataan yang sangat mahal karena mengakses data dua kali.

Dwipam Katariya
sumber
0

Tampak bagi saya bahwa, agar efisien, Anda perlu 1) menghasilkan angka acak antara 0 dan satu kurang dari jumlah catatan basis data, dan 2) dapat memilih baris pada posisi itu. Sayangnya, database yang berbeda memiliki generator angka acak yang berbeda dan cara yang berbeda untuk memilih baris pada posisi dalam set hasil - biasanya Anda menentukan berapa banyak baris untuk dilewati dan berapa banyak baris yang Anda inginkan, tetapi itu dilakukan secara berbeda untuk database yang berbeda. Ini adalah sesuatu yang bekerja untuk saya dalam SQLite:

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

Itu tergantung pada kemampuan untuk menggunakan subquery dalam klausa batas (yang dalam SQLite adalah LIMIT <recs to skip>, <recs to take>) Memilih jumlah record dalam sebuah tabel harus sangat efisien, menjadi bagian dari database meta data, tetapi itu tergantung pada implementasi database. Juga, saya tidak tahu apakah kueri benar-benar akan membangun set hasil sebelum mengambil catatan Nth, tapi saya berharap itu tidak perlu. Perhatikan bahwa saya tidak menentukan klausa "pesanan menurut". Mungkin lebih baik untuk "memesan dengan" sesuatu seperti kunci utama, yang akan memiliki indeks - mendapatkan catatan N dari indeks mungkin lebih cepat jika database tidak bisa mendapatkan catatan N dari database itu sendiri tanpa membangun set hasil .

John Deighan
sumber
0

Jawaban yang paling cocok saya lihat di artikel ini untuk sql server

WITH myTableWithRows AS (
    SELECT (ROW_NUMBER() OVER (ORDER BY myTable.SomeField)) as row,*
    FROM myTable)
SELECT * FROM myTableWithRows WHERE row = 3
Knight Fighter
sumber