SQL 데이터베이스 테이블에서 n 번째 행을 선택하는 방법은 무엇입니까?


399

데이터베이스 테이블에서 n 번째 행 을 선택하는 (이상적으로) 데이터베이스 불가지론 적 방법을 배우고 싶습니다 . 다음 데이터베이스의 고유 기능을 사용하여 이것이 어떻게 달성 될 수 있는지 보는 것도 흥미로울 것입니다.

  • SQL 서버
  • MySQL
  • PostgreSQL
  • SQLite
  • 신탁

현재 SQL Server 2005에서 다음과 같은 작업을 수행하고 있지만 다른 더 독립적 인 접근 방식을 보는 데 관심이 있습니다.

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

위의 SQL에 대한 크레딧 : Firoz Ansari의 웹 로그

업데이트 : SQL 표준에 관한 Troels Arvin의 답변을 참조하십시오 . 트롤, 우리가 인용 할 수있는 링크가 있습니까?


3
예. 다음은 ISO SQL 표준에 대한 정보에 대한 링크입니다. troels.arvin.dk/db/rdbms/links/#standards
Troels Arvin

13
관계의 정의에 의해 테이블의 행에는 순서가 없으므로 테이블의 N 번째 행을 선택할 수 없음을 나타냅니다. 선택할 수있는 것은 (나머지) 쿼리에 의해 반환 된 행 집합의 N 번째 행입니다. 이는 예제와 다른 모든 대답이 수행하는 것입니다. 대부분의 경우 이것은 의미론 일 수 있지만 질문의 근본적인 문제를 가리 킵니다. 을 반환해야하는 경우 테이블에 OrderSequenceNoOrderNo N을 도입 하고 새 주문을 만들 때 독립 시퀀스 생성기 에서 생성하십시오 .
Damir Sudarevic

2
SQL 표준은 옵션을 정의합니다 offset x fetch first y rows only. 현재 Postgres, Oracle12, DB2에서 지원됩니다.
a_horse_with_no_name

답변:


349

표준의 선택적 부분 에서이 작업을 수행하는 방법이 있지만 많은 데이터베이스가 자체 작업 방식을 지원합니다.

이것과 다른 것들에 대해 이야기하는 정말 좋은 사이트는 http://troels.arvin.dk/db/rdbms/#select-limit 입니다.

기본적으로 PostgreSQL 및 MySQL은 비표준을 지원합니다.

SELECT...
LIMIT y OFFSET x 

Oracle, DB2 및 MSSQL은 표준 윈도우 기능을 지원합니다.

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

(위의 DB를 사용하지 않기 때문에 위의 사이트에서 방금 복사했습니다)

업데이트 : PostgreSQL 8.4부터 표준 윈도우 기능이 지원되므로 PostgreSQL에서도 두 번째 예제가 작동 할 것으로 예상됩니다.

업데이트 : SQLite는 2018-09-15의 버전 3.25.0에서 창 기능 지원을 추가 했으므로 두 형식 모두 SQLite에서 작동합니다.


3
MySQL은 OFFSET 및 LIMIT 구문도 사용합니다. Firebird는 FIRST 및 SKIP 키워드를 사용하지만 SELECT 바로 뒤에 배치됩니다.
Doug

7
되어서는 안된다는 WHERE rownumber = n에만 n 번째 행을 얻을?
Steve Bennett

MySQL은 버전 8부터 창 기능을 지원합니다. 1DB 이후 MariaDB
Paul Spiegel

101

PostgreSQL은 SQL 표준에 의해 정의 된 윈도우 기능 을 지원 하지만 어색하므로 대부분의 사람들은 (비표준) LIMIT/OFFSET :

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

이 예는 21 번째 행을 선택합니다. OFFSET 20Postgres에 처음 20 개의 레코드를 건너 뛰라고 지시합니다. ORDER BY조항을 지정하지 않으면 어떤 레코드를 되 찾을 것이라는 보장이 없으며, 거의 유용하지 않습니다.


31

나머지는 확실하지 않지만 SQLite와 MySQL에는 "기본"행 순서가 없습니다. 이 두 방언에서 최소한 다음 스 니펫은 추가 된 날짜 / 시간을 기준으로 정렬하여 the_table에서 15 번째 항목을 가져옵니다.

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

(물론 DATETIME 필드를 추가하고 항목을 추가 한 날짜 / 시간으로 설정해야합니다.)


이것은 인라인 오프셋 값으로 쿼리를 제한하는 가장 좋은 방법입니다. 그러나 여기서 우리는 0,14를 사용해서는 안됩니까? 1,15는 첫 번째 행을 떠납니다.
검투사

15는 무엇을 의미합니까? 나는 1이 하나의 기록을 얻는다는 것을 알고 있습니다. 쉼표는 내가 체크 아웃 예에서 사용되지 1keydata.com/sql/sql-limit.html
committedandroider을

1
실제로 여기에서 php.about.com/od/mysqlcommands/g/Limit_sql.htm 에서 15 번째 항목을 가져 오려면 LIMIT 14, 1 (0은 첫 번째 요소, 1은 길이 1
commitandroider

추가 * DESC LIMIT 15,1
JerryGoyal 님이

25

SQL 2005 이상에는이 기능이 내장되어 있습니다. ROW_NUMBER () 함수를 사용하십시오. << 이전 및 다음 >> 스타일 브라우징 기능이있는 웹 페이지에 적합합니다.

통사론:

SELECT
    *
FROM
    (
        SELECT
            ROW_NUMBER () OVER (ORDER BY MyColumnToOrderBy) AS RowNum,
            *
        FROM
            Table_1
    ) sub
WHERE
    RowNum = 23

나는이 솔루션이 더 똑바로 느껴지기 때문에 선호합니다.
FoxArc

18

나는 이것이 비효율적이라고 생각하지만 아주 간단한 접근법이며, 시도한 작은 데이터 세트에서 작동했습니다.

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

이것은 다섯 번째 항목을 얻고 두 번째 최상위 숫자를 변경하여 다른 n 번째 항목을 얻습니다

SQL 서버에서만 (제 생각에는) ROW_NUMBER ()를 지원하지 않는 이전 버전에서 작동해야합니다.


ROW_NUMBER ()가 SQL 2000에서 작동하지 않기 때문에 이것을 사용할 것입니다 (예, 우리는 여전히 SQL 2000에 클라이언트가 있습니다) 구체적으로, '5'를 루프의 반복자 변수로 바꾸고 사용할 것입니다 테이블의 각 행을 차례로 복사하고 수정합니다. 어쩌면 누군가가이 코멘트를보고 유용 할 것입니다
Inversus


14

SQL Server에서 확인하십시오.

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

이것은 당신에게 emp 테이블의 10 번째 줄을 줄 것입니다!


이 질문에 대한 답변을 이미 입력 하셨습니다. 생각이 맞지 않다고 생각되는 답변을 삭제 하십시오. 두 답변이 모두 옳다고 생각되면 두 답변을 한 곳에 게시하십시오
SpringLearner

11

일부 답변이 주장하는 것과는 달리 SQL 표준은이 주제와 관련하여 침묵하지 않습니다.

SQL : 2003부터 "창 함수"를 사용하여 행을 건너 뛰고 결과 세트를 제한 할 수있었습니다.

그리고 SQL : 2008에서는 약간 더 간단한 접근법이 추가되었습니다.
OFFSET skip ROWS FETCH FIRST n ROWS ONLY

개인적으로, 나는 SQL : 2008의 추가가 실제로 필요하다고 생각하지 않으므로 ISO라면 이미 큰 표준에서 제외했을 것입니다.


표준하지만이 있다는 것을 그것의 좋은 자신의 삶처럼 사람을 만드는 쉽게, 그리고 좋은 너무 : 표준적인 방법으로 물건을 할 마이크로 소프트
user230910

7

예전에는 MSSQL 2000에서 일할 때 "트리플 플립"이라고하는 작업을 수행했습니다.

편집

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

우아하지 않았고 빠르지는 않았지만 효과가있었습니다.


25 개의 행이 있고 10 행 페이지 크기의 세 번째 페이지 (예 : 21-25 행)를 원한다고 가정하십시오. 가장 안쪽 쿼리는 상위 30 개 행 (1 ~ 25 행)을 가져옵니다. 중간 쿼리는 마지막 10 개의 행 (25-16 행)을 가져옵니다. 외부 쿼리는 그것들을 재정렬하고 16-25 행을 반환합니다. 21-25 행을 원한다면 이것은 분명히 잘못입니다.
Bill Karwin

중간 페이지를 원하면 작동하지 않습니다. 25 개의 행이 있고 두 번째 페이지 (예 : 11-20 행)를 원한다고 가정합니다. 내부 쿼리는 상위 2 * 10 = 20 행 또는 1-20 행을 가져옵니다. 중간 쿼리는 마지막 15 개 행 (25-((2-1) * 10) = 15)을 가져 와서 20-6 행을 생성합니다. 마지막 쿼리는 순서를 바꾸고 6-20 행을 반환합니다. 총 행 수가 원하는 페이지 크기의 배수가 아니면이 기술이 작동하지 않습니다.
Bill Karwin

아마도 가장 좋은 결론은 나머지 MS SQL Server 2000 인스턴스를 업그레이드해야한다는 것입니다. :-) 거의 2012 년이며이 문제는 몇 년 동안 더 나은 방식으로 해결되었습니다!
Bill Karwin

@Bill Karwin : 계산 IF / ELSE IF아래 의 블록을 참고하십시오 ( OuterPageSize1 및 2 페이지에서 OuterPageSize값이 10으로 다시 떨어짐) . 3 페이지 (21-25 페이지)에서 계산은 올바르게 5를 반환하고 모든 페이지에서 4 이상을 반환합니다. 계산의 부정적인 결과는 0으로 대체됩니다 (아마도 그 시점에서 빈 데이터 행을 즉시 반환하는 것이 더 빠를 것입니다).
Adam V

아, 지금 봅니다. 글쎄, 나는 오늘 MS SQL Server 2000을 사용하는 것이 문제의 가치가 없다고 생각한다.
Bill Karwin

6

SQL 서버


위에서 n 번째 레코드를 선택하십시오

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

아래에서 n 번째 레코드를 선택하십시오

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

5

신탁:

select * from (select foo from bar order by foo) where ROWNUM = x

1
where ROWNUM = xOracle DB에서는 x = 1에서만 작동합니다. 즉, where ROWNUM = 2행을 반환하지 않습니다.
aff

5

Oracle 12c에서는 다음을 사용할 수 있습니다. OFFSET..FETCH..ROWS 옵션을ORDER BY

예를 들어, 위에서 세 번째 레코드를 얻으려면

SELECT * 
FROM   sometable
ORDER BY column_name
OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY;

4

혼동의 빠른 해결책은 다음과 같습니다.

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

여기에서 N = 0을 채우면 마지막 행, N = 1에 의해 두 번째 마지막, N = 3을 채우는 네 번째 마지막 행 등을 얻을 수 있습니다.

이것은 인터뷰에서 매우 일반적인 질문이며 매우 간단한 질문입니다.

또한 u보다 Amount, ID 또는 일부 숫자 정렬 순서를 원하면 MySQL에서 CAST 기능을 사용할 수 있습니다.

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

여기에 N = 4를 채우면 CART 테이블에서 최고 금액의 다섯 번째 마지막 레코드를 얻을 수 있습니다. 필드와 테이블 이름을 맞추고 솔루션을 제안 할 수 있습니다.



3

예를 들어, MSSQL에서 매 10 번째 행을 선택하려면 다음을 사용할 수 있습니다.

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

MOD를 선택하고 원하는 숫자를 10으로 변경하십시오.


3

SQL Server의 경우 행 번호로 이동하는 일반적인 방법은 다음과 같습니다.

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

예를 들어 :

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

이것은 20 번째 행의 정보를 반환합니다. 이후에 행 수를 0으로 설정하십시오.


2

LIMIT n, 1은 MS SQL Server에서 작동하지 않습니다. 나는 그것이 그 구문을 지원하지 않는 유일한 주요 데이터베이스에 불과하다고 생각합니다. 공평하게 말하면 SQL 표준의 일부는 아니지만 광범위하게 지원되어야합니다. SQL 서버를 제외한 모든 것에서 LIMIT가 훌륭하게 작동합니다. SQL 서버의 경우 우아한 솔루션을 찾을 수 없었습니다.


1
Oracle, DB2를 제외하고 전 세계의 거의 모든 엔터프라이즈 급 데이터베이스가 있습니다. PostgreSQL은 LIMIT 키워드를 지원하는 유일한 엔터프라이즈 지원 데이터베이스에 관한 것으로, 주로 오픈 소스이기 때문에 ACID 무시 MySQL 군중이 접근 할 수 있어야하기 때문입니다.
David

3
@AlexD이 "답변"은 주석이 구현되기 전에 Stackoverflow 이전에 게시되었습니다. 나는 이것을 다른 답변에 대한 의견으로 게시했을 것이지만 시간에 의견이 존재하지 않았습니다.
Kibbee

2

동적 페이징 / 정렬을 허용하는 Oracle 용으로 최근에 작성한 sproc의 일반 버전은 다음과 같습니다.

-- 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);

2

그러나 실제로이 모든 것이 실제로 좋은 데이터베이스 디자인을위한 팔러 트릭이 아닙니까? 몇 번이나 이와 같은 기능이 필요했습니다. 간단한 쿼리를 통해 빠른 보고서를 작성하는 것이 었습니다. 실제 작업의 경우 이와 같은 트릭을 사용하면 문제가 발생합니다. 특정 행을 선택해야하는 경우 순차적 값이있는 열만 있으면됩니다.


2

SQL Server의 경우 다음은 테이블 제공에서 첫 번째 행을 리턴합니다.

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

다음과 같이 값을 반복 할 수 있습니다.

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;

1

Sybase SQL Anywhere에서 :

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

ORDER BY를 잊어 버리거나 의미가 없습니다.


1

T-SQL-테이블에서 N 번째 레코드 번호 선택

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

예를 들어 직원 테이블에서 5 번째 레코드를 선택하려면 쿼리는

select * from
 (select row_number() over (order by Rand() desc) as Rno,* from Employee) T where T.Rno = 5

1
SELECT * FROM emp a
WHERE  n = (SELECT COUNT( _rowid)
              FROM emp b
             WHERE a. _rowid >= b. _rowid);

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

N 번째 행을 찾기 위해이 쿼리를 작성했습니다. 이 쿼리의 예는 다음과 같습니다.

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

0

이 엔진을 실행하는 SQL 엔진을 찾을 수 있다는 것을 믿을 수 없습니다 ...

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

0

내가하는 것처럼 Caché를 사용하는 경우를 제외하고는 공상이없고 특별한 기능이 없습니다 ...

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

ID 열 또는 날짜 스탬프 열이 있다고 가정하면 신뢰할 수 있습니다.


0

이것이 DB2 SQL 내에서하는 방법입니다. RRN (상대 레코드 번호)이 O / S에 의해 테이블 ​​내에 저장되어 있다고 생각합니다.

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

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

먼저 오름차순으로 정렬하여 상위 100 개의 행을 선택한 다음 내림차순으로 정렬하여 마지막 행을 선택하고 1로 제한하십시오. 그러나 이것은 데이터에 두 번 액세스하므로 매우 비싼 설명입니다.


0

효율적으로하려면 1) 데이터베이스 레코드 수보다 0에서 1 사이의 난수를 생성하고 2) 해당 위치에서 행을 선택할 수 있어야합니다. 불행히도 데이터베이스마다 다른 난수 생성기와 결과 집합의 위치에서 행을 선택하는 다른 방법이 있습니다. 일반적으로 건너 뛸 행 수와 원하는 행 수를 지정하지만 데이터베이스마다 다르게 수행됩니다. 다음은 SQLite에서 나를 위해 작동하는 것입니다.

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

limit 절에서 하위 쿼리를 사용할 수 있는지에 달려 있습니다 (SQLite에서 LIMIT <recs to skip>, <recs to take>) 테이블에서 레코드 수를 선택하는 것은 데이터베이스의 일부이므로 특히 효율적이어야합니다. 메타 데이터이지만 데이터베이스 구현에 따라 다릅니다. 또한 N 번째 레코드를 검색하기 전에 쿼리가 실제로 결과 집합을 작성할지 여부는 알 수 없지만 필요하지 않기를 바랍니다. "order by"절을 지정하지 않았습니다. 기본 키와 같이 "순서대로"정렬하는 것이 좋습니다. 인덱스가있을 것입니다. 데이터베이스가 결과 세트를 빌드하지 않고 데이터베이스 자체에서 N 번째 레코드를 가져올 수없는 경우 인덱스에서 N 번째 레코드를 얻는 것이 더 빠를 수 있습니다. .


0

기사에서 SQL Server에 대해 가장 적합한 답변

WITH myTableWithRows AS (
    SELECT (ROW_NUMBER() OVER (ORDER BY myTable.SomeField)) as row,*
    FROM myTable)
SELECT * FROM myTableWithRows WHERE row = 3
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.