一番下の行を選択する方法は?


100

SELECT TOP(200)...を実行できますが、BOTTOM(200)を実行できないのはなぜですか?

さて、私が何を意味するのかという哲学に入るのではなく、TOP(200)と同等のことをどのようにして行うことができますか?

回答:


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

2
なぜ派生テーブルを使用しているのですか?
RichardOD

14
A-> Z順で行を返したいが、Z-> A順で上位200を選択する場合、これはその1つの方法です。他の答えは、ORDER BYを変更するだけであることを示唆しているため、質問で説明されているのと同じ結果が返されません。順序が問題でない限り、OPは言っていません。
トムH

3
@トムH.あなたが何を意味しているかについて数秒間考えなければなりませんでした(私は14時間アップしています)。最初は、あなたとあなたの答えの順序の違いがわかりませんでしたが、今はわかります。だから+1。
RichardOD

1
小さなテーブルで作業しているとき、これと他の答えはうまくいきます。一番下の数行だけに関心がある場合は、テーブル全体を列で並べる価値はないと思います。
ステディフィッシュ2014年

1
良い答え、最高の私見...本当の問題は、私は手動またはASPが提供する機能でobjRecordsetの順序を変更する必要があると思うので、私は、ASPスクリプトからこれを行うことができないということです....
Andrea_86

99

不要です。を使用しORDER BYて並べ替えを変更するだけでDESC、同じ効果を得ることができます。


3
グーグルは同じことを言った、そして今あなたの9人は同意する、私にとって十分に良い、ありがとう:D
CloudMeta

8
DESCを使用すると、最後のN行が返されますが、返される行も最初のN行と逆の順序になります。
RickNZ、2009

11
テーブルにORDER BYのインデックスがない場合はどうなりますか?
プロテクター1

8
@Justin:varchar値を含む列が1つだけあると想像してください。ORDER BYはアルファベット順にソートされますが、これは(おそらく)希望どおりではありません。
プロテクター1

3
トムH.は正しいアンサーです。それ以外の場合、行は逆の順序になります。
Pierre-Olivier Goulet 2014年

39

申し訳ありませんが、私の意見では正しい答えは見当たらないと思います。

TOPX機能は、未定義の順序でレコードを示しています。その定義から、BOTTOM関数を定義することはできません。

インデックスや並べ替え順序に依存しません。実行するORDER BY y DESCと、最も高いy値を持つ行が最初に取得されます。これが自動生成されたIDの場合、他の回答で提案されているように、テーブルに最後に追加されたレコードが表示されます。しかしながら:

  • これは、自動生成されたID列がある場合にのみ機能します
  • TOP関数と比較すると、パフォーマンスに大きな影響があります。

正解はTOP、一番下の行を取得するのと同じことはあり得ません。


3
確かに、同等のものはなく、回避策があるようです。
2014年

4
TOPは、アイテムがテーブルに追加された順序とは関係ありません。つまり、「クエリに一致する最初のXレコードを与える」
Luke

4
はい、そうです。クエリに一致する、テーブルに追加された最初のレコードをレコードに提供します。
Martijn Burger、

4
ルークに同意します。定義上、データベーステーブルには順序がありません。selectステートメントにORDER BY句がない場合、RDBMSが提供する順序に依存することはできません。ウィキにここで読む ORDER BY句は、テーブルをクエリSELECT文で指定されていない限りしかし、データベースシステムは、行の任意の順序を保証するものではありません。
Zohar Peled

2
私はこの答えに同意しますが、実際にはトムの答えはすべての実用的な用途の問題を解決します。
アントニオ

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個のレコードに制限する、順序付けされていないクエリ結果セットを返します。(Oracleの観点からは、WHERE NUM <(N + 1)を追加するのと同じです。

順序を使用するソリューションは、TOP句によって返される行を返す場合があります(データセットが最初に順序付けされていないため)。

TOPの有用性は、データセットが特定のサイズNに達すると、行のフェッチを停止することです。データをすべてフェッチしなくても、データがどのように見えるかを感じることができます。

BOTTOMを正確に実装するには、データセット全体を順序付けずにフェッチしてから、データセットを最後のNレコードに制限する必要があります。巨大なテーブルを扱っている場合、これは特に効果的ではありません。それは必ずしもあなたがあなたが求めていると思うことをあなたに与えるわけでもありません。データセットの最後が必ずしも「最後に挿入された行」であるとは限りません(おそらく、ほとんどのDML集中型アプリケーションではそうではありません)。

同様に、ORDER BYを実装するソリューションは、残念ながら、大規模なデータセットを処理する場合に悲惨な結果をもたらす可能性があります。たとえば、100億件のレコードがあり、最後の10件が必要な場合、100億件のレコードを注文して、最後の10件を選択するのは非常に愚かです。

ここでの問題は、BOTTOMがTOPと比較するときに私たちが考える意味を持たないことです。

レコードが何度も挿入、削除、挿入、削除されると、一部のギャップがストレージに表示され、その後、可能であれば行が挿入されます。しかし、TOPを選択すると、テーブルの存在の早い段階で挿入された可能性があるため、よく見られるのはソートされたデータのようです。テーブルで多くの削除が行われない場合、順序付けされているように見えることがあります。(たとえば、作成日はテーブル作成自体と同じくらい前の日付になる場合があります)。しかし、実際には、これが削除の多いテーブルである場合、TOP N行はまったくそのように見えない可能性があります。

つまり、ここでの一番下の行(しゃれた意図)は、BOTTOM Nレコードを要求している誰かが実際に何を要求しているのかを知らないということです。または、少なくとも、彼らが求めていることと実際にBOTTOMが意味していることは同じではありません。

したがって、ソリューションはリクエスタの実際のビジネスニーズを満たす可能性がありますが、最下位であるための基準を満たしていません。


1
素晴らしい説明。トピックにもっと光を当てるために賛成票を投じます。
rohrl77

私のユースケースはこれです。大きなinsertステートメントを実行して、行をインデックスのない大きなテーブルに入れました。(インデックスを作成する前に、最初にテーブルにデータを入力しています。)再起動などでクライアントセッションが失われました。次に、新しく追加した行がそこにあるかどうかを確認します。テーブルの「一番下」の行が最近の行の1つである場合、操作が完了したことがわかります。「下」行が何か別の場合、保証はありません。テーブル全体をスキャンして確認する必要があります...しかし、「下」をできるだけ早くチェックすることで、時間を節約できます。上'。
Ed Avis

良い説明ですが、それでもボトムが存在することを意味し、データを逆に読み取る/取得する必要があるだけです。新しいテーブルでの挿入が中止された(確かにエッジの)ケースでは、すべてを取得せずに最後に挿入されたレコード(下部)を確認すると便利です。テーブルデータを逆の順序で取得できない技術的な理由はありますか?
James

3

「ジャスティンエティエ」が現在認めている回答は、「プロテクター1」が指摘しているように正解ではありません。

私の知る限り、現時点では、他の回答やコメントは、作者が要求した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つの方法です。
幻滅

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 2016年

編集しました。コメントを追加して、説明が少し良くなることを願っています。主なアイデアは、テーブルからトップxを選択し、次にトップx-必要な数を選択し、exceptステートメントを使用して不要な結果を除外することです。
HumbleWebDev 2016年

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

まず、次を使用して、テーブルの元の順序に従ってサブクエリにインデックスを作成します。

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.