맨 아래 행을 선택하는 방법은 무엇입니까?


100

SELECT TOP (200)을 할 수 있지만 왜 BOTTOM (200)은 안되나요?

제가 의미하는 바는 철학에 들어 가지 않기 위해 어떻게하면 TOP (200)에 해당하는 것을 할 수 있지만 반대로 (BOTTOM이 할 것이라고 기대하는 것처럼 아래쪽에서 ...)?

답변:


89
SELECT
    columns
FROM
(
     SELECT TOP 200
          columns
     FROM
          My_Table
     ORDER BY
          a_column DESC
) SQ
ORDER BY
     a_column ASC

2
파생 테이블을 사용하는 이유는 무엇입니까?
RichardOD 2009

14
A-> Z 순서로 행을 반환하고 싶지만 Z-> A 순서에서 상위 200 개를 선택하려면 이것이 한 가지 방법입니다. 다른 답변은 ORDER BY를 변경하는 것만으로 질문에 설명 된 것과 동일한 결과를 반환하지 않을 것입니다.
Tom H

3
@Tom H. 당신이 의미하는 바에 대해 몇 초 동안 생각해야했습니다 (14 시간 동안 일어났습니다). 처음에는 당신과 답변에 의한 순서의 차이를 볼 수 없었지만 이제는 할 수 있습니다. 그래서 +1.
RichardOD 2009

1
이 답변과 다른 답변은 작은 테이블에서 작업 할 때 잘 작동합니다. 아래 몇 행에만 관심이있을 때 전체 테이블을 열별로 정렬 할 가치가 없다고 생각합니다.
steadyfish 2014-10-13

1
좋은 대답, 최고의 imho ... 진짜 문제는 ASP 스크립트에서이 작업을 수행 할 수 없다는 것이므로 objRecordset을 수동으로 또는 ASP가 제공하는 기능으로 재정렬해야한다고 생각합니다 ....
Andrea_86

98

불필요합니다. 를 사용 ORDER BY하고 정렬을 DESC로 변경 하여 동일한 효과를 얻을 수 있습니다.


3
구글이 똑같은 말을했고 이제 9 명이 동의합니다. 저에게 충분합니다. 감사합니다. : D
CloudMeta 2009-12-09

9
DESC를 사용하면 마지막 N 개 행이 반환되지만 반환 된 행도 처음 N 개 행에서 역순이됩니다.
RickNZ 2009

11
테이블에 ORDER BY 할 인덱스가 없으면 어떻게됩니까?
Protector One

8
@Justin : varchar 값을 포함하는 열이 하나만 있다고 상상해보십시오. ORDER BY는 알파벳순으로 정렬 할 것입니다. 이것은 (아마도) 우리가 원하는 것이 아닙니다.
Protector one

3
Tom H.가 올바른 anwser입니다. 그렇지 않으면 행이 역순으로 표시됩니다.
Pierre-Olivier Goulet

39

죄송합니다. 제 생각에는 정답이없는 것 같습니다.

TOPX는 방송을 위해 정의되지 않은 레코드 기능한다. 그 정의에 따라 BOTTOM함수 를 정의 할 수 없습니다.

색인 또는 정렬 순서와 무관합니다. 당신이 할 때 ORDER BY y DESC가장 높은 y 값을 가진 행을 먼저 얻습니다. 이것이 자동 생성 된 ID 인 경우 다른 답변에서 제안 된대로 테이블에 마지막으로 추가 된 레코드를 표시해야합니다. 하나:

  • 자동 생성 된 ID 열이있는 경우에만 작동합니다.
  • TOP기능 과 비교하면 성능에 상당한 영향을 미칩니다.

정답은 TOP맨 아래 행을 가져 오는 것과 동등한 것이 없으며 존재할 수 없다는 것 입니다.


3
사실, 동등한 조치가없는 것 같습니다.
찌를

4
TOP은 항목이 테이블에 추가 된 순서와 관련이 없습니다. "내 쿼리와 일치하는 첫 번째 X 레코드를 제공하십시오"를 의미합니다.
Luke

4
예, 쿼리와 일치하는 테이블에 추가 된 첫 번째 레코드를 제공합니다.
Martijn Burger

4
루크의 말에 동의합니다. 데이터베이스 테이블은 정의에 따라 순서가 없습니다. select 문에 ORDER BY 절이없는 경우 RDBMS에서 제공하는 순서에 의존해서는 안됩니다. 위키에서 읽기 그러나 데이터베이스 시스템은 테이블을 쿼리하는 SELECT 문에 ORDER BY 절이 지정되지 않는 한 행의 순서를 보장하지 않습니다.
Zohar Peled

2
나는이 대답에 동의하지만 실제로 Tom의 대답 은 모든 실제 사용에 대한 문제를 해결합니다.
안토니오

18

논리적으로

BOTTOM (x) is all the records except TOP (n - x), where n is the count; x <= n

예 : 직원에서 하위 1000 명 선택 :

T-SQL에서

DECLARE 
@bottom int,
@count int

SET @bottom = 1000 
SET @count = (select COUNT(*) from Employee)

select * from Employee emp where emp.EmployeeID not in 
(
SELECT TOP (@count-@bottom) Employee.EmployeeID FROM Employee
)

1
안녕하세요 shadi2014, "ORDER BY"를 사용하지 않으면 결과가 무작위로 나타납니다.
bummi

5
부미, 당신 말이 맞아요.하지만 그게이 대답을 옳게 만드는 것입니다. Select TOP 자체는 이론적으로 "무작위"이며 이것은 Select BOTTOM에 대한 올바른 구현입니다. 5000 개 기록의 표에서 (1000)는 최고 4000 제외한 모든 바닥입니다
tzachs

9

솔루션에서 ORDER BY 절을 구현하는 답변 중 하나라도 요점이 누락되었거나 TOP이 반환하는 내용을 실제로 이해하지 못하는 것 같습니다.

TOP은 레코드 집합을 반환 된 처음 N 개 레코드로 제한하는 정렬되지 않은 쿼리 결과 집합을 반환합니다. (오라클 관점에서는 where ROWNUM <(N + 1)을 추가하는 것과 유사합니다.

순서를 사용하는 모든 솔루션 은 순서에 사용 된 기준에 따라 TOP 절에 의해 반환되는 행을 반환 할 수 있습니다 (데이터 집합이 처음에 순서가 지정되지 않았기 때문에).

TOP의 유용성은 데이터 세트가 특정 크기 N에 도달하면 행 가져 오기를 중지한다는 것입니다. 데이터를 모두 가져 오지 않고도 데이터가 어떻게 보이는지 느낄 수 있습니다.

BOTTOM을 정확하게 구현하려면 순서가 지정되지 않은 전체 데이터 세트를 가져온 다음 데이터 세트를 최종 N 개 레코드로 제한해야합니다. 거대한 테이블을 다루는 경우에는 특히 효과적이지 않습니다. 그것은 당신이 생각 하는 것을 반드시 당신에게주지 않을 것입니다 당신이 요구하고 입니다. 데이터 세트의 끝이 반드시 "마지막으로 삽입 된 행"일 필요는 없습니다 (그리고 대부분의 DML 집약적 애플리케이션에는 해당되지 않을 것입니다).

마찬가지로, ORDER BY를 구현하는 솔루션은 안타깝게도 대규모 데이터 세트를 처리 할 때 잠재적으로 재앙이 될 수 있습니다. 예를 들어 100 억 개의 레코드가 있고 마지막 10 개를 원한다면 100 억 개의 레코드를 주문하고 마지막 10 개를 선택하는 것은 어리석은 일입니다.

여기서 문제는 BOTTOM이 TOP과 비교할 때 생각 하는 의미가 없다는 것입니다 .

레코드가 삽입, 삭제, 삽입, 삭제 될 때 저장소에 약간의 간격이 나타나고 나중에 가능하면 행이 슬롯에 들어갑니다. 그러나 TOP을 선택 하면 테이블의 존재 초기에 삽입되었을 수 있으므로 정렬 된 데이터 인 것처럼 보입니다 . 테이블이 많이 삭제되지 않으면 나타날 수 있습니다. 정렬 된 수 있습니다. (예를 들어, 생성 날짜는 테이블 생성 자체만큼 과거의 시간 일 수 있습니다). 그러나 현실은 이것이 삭제가 많은 테이블 인 경우 TOP N 행이 전혀 그렇게 보이지 않을 수 있다는 것입니다.

그래서-여기서 결론은 (말장난 의도) BOTTOM N 레코드를 요청하는 사람이 실제로 요청하는 내용을 알지 못한다는 것입니다. 또는 적어도 그들이 요구하는 것과 BOTTOM이 실제로 의미하는 바는 같은 것이 아닙니다.

따라서 솔루션은 요청자의 실제 비즈니스 요구를 충족시킬 수 있지만 BOTTOM이되는 기준을 충족하지 않습니다.


1
훌륭한 설명. 주제에 대해 더 많은 빛을 비추도록 찬성합니다.
rohrl77

내 사용 사례는 이것입니다. 큰 insert문을 실행하여 색인화되지 않은 큰 테이블에 행을 넣었습니다. (인덱싱을 시작하기 전에 먼저 테이블을 채우고 있습니다.) 재부팅 등으로 인해 클라이언트 세션이 손실되었습니다. 이제 새로 추가 된 행이 거기에 있는지 확인하고 싶습니다. 테이블의 '맨 아래'행이 최근 행 중 하나라면 작업이 완료된 것입니다. '하단'행이 다른 경우에는 보장 할 수 없으며 확인하기 위해 전체 테이블을 스캔해야합니다 ...하지만 '하단'을 빠르게 확인하여 시간을 절약 할 수 있습니다. 상단'.
Ed Avis

좋은 설명이지만 여전히 데이터를 반대로 읽거나 검색해야하는 바닥의 존재를 의미합니다. 새 테이블에 대한 삽입이 중단 된 경우 (분명히 가장자리) 모든 항목을 검색하지 않고 마지막으로 삽입 된 레코드 (하단)를 확인하는 것이 유용합니다. 테이블 데이터를 역순으로 검색 할 수없는 기술적 이유가 있습니까?
James

3

현재 "Justin Ethier"가 받아 들인 답변은 "Protector One"이 지적한 정답이 아닙니다.

내가 볼 수있는 한, 현재로서는 작성자가 요청한 BOTTOM (x)에 해당하는 다른 답변이나 의견이 없습니다.

먼저이 기능이 필요한 시나리오를 고려해 보겠습니다.

SELECT * FROM Split('apple,orange,banana,apple,lime',',')

그러면 열 1 개와 레코드 5 개로 구성된 테이블이 반환됩니다.

  • 사과
  • 주황색
  • 바나나
  • 사과
  • 라임

보시다시피 : ID 열이 없습니다. 반환 된 열로 주문할 수 없습니다. 상위 2 개 레코드에 대해 할 수있는 것처럼 표준 SQL을 사용하여 하위 2 개 레코드를 선택할 수 없습니다.

해결책을 제공하려는 시도는 다음과 같습니다.

SELECT * INTO #mytemptable FROM Split('apple,orange,banana,apple,lime',',')
ALTER TABLE #mytemptable ADD tempID INT IDENTITY
SELECT TOP 2 * FROM #mytemptable ORDER BY tempID DESC
DROP TABLE #mytemptable

그리고 여기에 더 완벽한 솔루션이 있습니다.

SELECT * INTO #mytemptable FROM Split('apple,orange,banana,apple,lime',',')
ALTER TABLE #mytemptable ADD tempID INT IDENTITY
DELETE FROM #mytemptable WHERE tempID <= ((SELECT COUNT(*) FROM #mytemptable) - 2)
ALTER TABLE #mytemptable DROP COLUMN tempID
SELECT * FROM #mytemptable
DROP TABLE #mytemptable

나는 이것이 모든 상황에서 사용하는 것이 좋은 생각이라고 결코 주장하지 않지만 원하는 결과를 제공합니다.



1

다른 방식으로 주문할 때의 문제는 종종 인덱스를 잘 사용하지 않는다는 것입니다. 시작이나 끝이 아닌 행을 여러 개 선택해야하는 경우에도 확장 할 수 없습니다. 다른 방법은 다음과 같습니다.

DECLARE @NumberOfRows int;
SET @NumberOfRows = (SELECT COUNT(*) FROM TheTable);

SELECT col1, col2,...
FROM (
    SELECT col1, col2,..., ROW_NUMBER() OVER (ORDER BY col1) AS intRow
    FROM TheTable
) AS T
WHERE intRow > @NumberOfRows - 20;

2
1) ORDER BY 절의 방향을 "인덱스를 잘 사용하지 않는다"면 적절한 RDBMS를 얻으십시오! RDBMS는 색인을 앞뒤로 탐색하는지 여부를 신경 쓰지 않아야합니다. 2) 인덱스 사용에 대해 염려하지만 솔루션은 테이블의 모든 행에 시퀀스를 첨부합니다. 이것이 적절한 인덱스가 사용되지 않도록 보장하는 한 가지 방법입니다.
환멸

1

위의 "Tom H"답변은 정확하며 하단 5 개 행을 얻는 데 효과적입니다.

SELECT [KeyCol1], [KeyCol2], [Col3]
FROM
(SELECT TOP 5 [KeyCol1],
       [KeyCol2],
       [Col3]
  FROM [dbo].[table_name]
  ORDER BY [KeyCol1],[KeyCol2] DESC) SOME_ALAIS
  ORDER BY [KeyCol1],[KeyCol2] ASC

감사.


0

이 시도.

declare @floor int --this is the offset from the bottom, the number of results to exclude
declare @resultLimit int --the number of results actually retrieved for use
declare @total int --just adds them up, the total number of results fetched initially

--following is for gathering top 60 results total, then getting rid of top 50. We only keep the last 10
set @floor = 50 
set @resultLimit = 10
set @total = @floor + @resultLimit

declare @tmp0 table(
    --table body
)

declare @tmp1 table(
    --table body
)

--this line will drop the wanted results from whatever table we're selecting from
insert into @tmp0
select Top @total --what to select (the where, from, etc)

--using floor, insert the part we don't want into the second tmp table
insert into @tmp1
select top @floor * from @tmp0

--using select except, exclude top x results from the query
select * from @tmp0
except 
select * from @tmp1

OP에 대한 코드는 무엇입니까? 문제 해결 방법에 대한 설명을 조금 더 추가해주세요
techspider

편집했습니다. 좀 더 잘 설명하고 더 많은 의견을 추가하기를 바랍니다. 주요 아이디어는 테이블에서 상위 x를 선택한 다음 원하는 상위 x-num을 선택한 다음 except 문을 사용하여 불필요한 결과를 제외하는 것입니다.
HumbleWebDev

0

반환 된 행 수를 알 필요가없는 이에 대한 해결책을 찾았습니다.

예를 들어 최근 1 (또는 2, 5 또는 34)을 제외하고 테이블에 기록 된 모든 위치를 가져 오려는 경우

SELECT * 
FROM
    (SELECT ROW_NUMBER() OVER (ORDER BY CreatedDate) AS Row, * 
    FROM Locations
    WHERE UserId = 12345) AS SubQuery
WHERE Row > 1 -- or 2, or 5, or 34

0

내림차순으로 정렬 된 간단한 하위 쿼리를 쿼리 한 다음 동일한 열을 오름차순으로 정렬하는 것이 트릭입니다.

SELECT * FROM 
    (SELECT TOP 200 * FROM [table] t2 ORDER BY t2.[column] DESC) t1
    ORDER BY t1.[column]

0
SELECT TOP 10*from TABLE1 ORDER BY ID DESC

여기서 ID는 TABLE1의 기본 키입니다.


0

먼저 다음을 사용하여 테이블의 원래 순서에 따라 하위 쿼리에 인덱스를 만듭니다.

ROW_NUMBER () OVER (ORDER BY (SELECT NULL) ) AS RowIndex

그런 다음 RowIndex기본 쿼리에서 만든 열을 기준으로 테이블을 내림차순으로 정렬합니다 .

ORDER BY RowIndex DESC

마지막으로 TOP원하는 행 수를 사용 하십시오.

    SELECT TOP 1 * --(or 2, or 5, or 34)
    FROM   (SELECT ROW_NUMBER() OVER (ORDER BY  (SELECT NULL) ) AS RowIndex, * 
            FROM MyTable) AS SubQuery
    ORDER BY RowIndex DESC
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.