How to select bottom most rows?
SELECT
columns
FROM
(
SELECT TOP 200
columns
FROM
My_Table
ORDER BY
a_column DESC
) SQ
ORDER BY
a_column ASC
Select top and bottom rows
Using a union is the only thing I can think of to accomplish this
select * from (select top(5) * from logins order by USERNAME ASC) a
union
select * from (select top(5) * from logins order by USERNAME DESC) b
Why is there no `select last` or `select bottom` in SQL Server like there is `select top`?
You can think of it like this.
SELECT TOP N
without ORDER BY
returns some N
rows, neither first, nor last, just some. Which rows it returns is not defined. You can run the same statement 10 times and get 10 different sets of rows each time.
So, if the server had a syntax SELECT LAST N
, then result of this statement without ORDER BY
would again be undefined, which is exactly what you get with existing SELECT TOP N
without ORDER BY
.
You have stressed in your question that you know and understand what I've written below, but I'll still keep it to make it clear for everyone reading this later.
Your first phrase in the question
In SQL-server we have
SELECT TOP N ...
now in that we can get the
first n rows in ascending order (by default), cool.
is not correct. With SELECT TOP N
without ORDER BY
you get N "random" rows. Well, not really random, the server doesn't jump randomly from row to row on purpose. It chooses some deterministic way to scan through the table, but there could be many different ways to scan the table and server is free to change the chosen path when it wants. This is what is meant by "undefined".
The server doesn't track the order in which rows were inserted into the table, so again your assumption that results of SELECT TOP N
without ORDER BY
are determined by the order in which rows were inserted in the table is not correct.
So, the answer to your final question
why no
select last/bottom
like it's counterpart.
is:
- without
ORDER BY
results ofSELECT LAST N
would be exactly the same as results ofSELECT TOP N
- undefined. - with
ORDER BY
result ofSELECT LAST N ... ORDER BY X ASC
is exactly the same as result ofSELECT TOP N ... ORDER BY X DESC
.
So, there is no point to have two key words that do the same thing.
There is a good point in the Pieter's answer: the word TOP
is somewhat misleading. It really means LIMIT
result set to some number of rows.
By the way, since SQL Server 2012 they added support for ANSI standard OFFSET
:
OFFSET { integer_constant | offset_row_count_expression } { ROW | ROWS }
[
FETCH { FIRST | NEXT } {integer_constant | fetch_row_count_expression } { ROW | ROWS } ONLY
]
Here adding another key word was justified that it is ANSI standard AND it adds important functionality - pagination, which didn't exist before.
I would like to thank @Razort4x here for providing a very good link to MSDN in his question. The "Advanced Scanning" section there has an excellent example of mechanism called "merry-go-round scanning", which demonstrates why the order of the results returned from a SELECT
statement cannot be guaranteed without an ORDER BY
clause.
This concept is often misunderstood and I've seen many question here on SO that would greatly benefit if they had a quote from that link.
The answer to your question
Why doesn't SQL Server have a
SELECT LAST
or saySELECT BOTTOM
or
something like that, where we don't have to specify theORDER BY
and
then it would give the last record inserted in the table at the time
of executing the query (again I am not going into details about how
would this result in case of uncommitted reads or phantom reads).
is:
The devil is in the details that you want to omit. To know which record was the "last inserted in the table at the time of executing the query" (and to know this in a somewhat consistent/non-random manner) the server would need to keep track of this information somehow. Even if it is possible in all scenarios of multiple simultaneously running transactions, it is most likely costly from the performance point of view. Not every SELECT
would request this information (in fact very few or none at all), but the overhead of tracking this information would always be there.
So, you can think of it like this: by default the server doesn't do anything specific to know/keep track of the order in which the rows were inserted, because it affects performance, but if you need to know that you can use, for example, IDENTITY
column. Microsoft could have designed the server engine in such a way that it required an IDENTITY
column in every table, but they made it optional, which is good in my opinion. I know better than the server which of my tables need IDENTITY
column and which do not.
Summary
I'd like to summarise that you can look at SELECT LAST
without ORDER BY
in two different ways.
1) When you expect SELECT LAST
to behave in line with existing SELECT TOP
. In this case result is undefined for both LAST
and TOP
, i.e. result is effectively the same. In this case it boils down to (not) having another keyword. Language developers (T-SQL language in this case) are always reluctant to add keywords, unless there are good reasons for it. In this case it is clearly avoidable.
2) When you expect SELECT LAST
to behave as SELECT LAST INSERTED ROW
. Which should, by the way, extend the same expectations to SELECT TOP
to behave as SELECT FIRST INSERTED ROW
or add new keywords LAST_INSERTED
, FIRST_INSERTED
to keep existing keyword TOP
intact. In this case it boils down to the performance and added overhead of such behaviour. At the moment the server allows you to avoid this performance penalty if you don't need this information. If you do need it IDENTITY
is a pretty good solution if you use it carefully.
SELECT BOTTOM without changing ORDER BY
You could just wrap it in another query and put in the order you require...
SELECT x.*
FROM (
SELECT TOP 504
"date",
price
FROM [dbo].[AssetRet]
WHERE asset = 'SP500'
ORDER BY "date" DESC
) x
ORDER BY x."date"
Does this alternative work?...you will need a later version of sql-server for the partition function RANK
...
SELECT x."date",
x.price
FROM (
SELECT "date",
price,
Rnk = RANK() OVER (ORDER BY "date" DESC)
FROM [dbo].[AssetRet]
WHERE asset = 'SP500'
) x
WHERE x.Rnk <= 504
ORDER BY x."date"
EDIT
Looks like this other answer pretty much covered your question
Select Bottom 30% of Rows by Group in SQL Server 2017
This would do I think:-
WITH RecordCount AS (
SELECT ParentID, ROUND(COUNT(1)*.3, 0) RecordCount -- To be selected
FROM MyData
GROUP BY ParentID
), SortedRecords AS (
SELECT ID, ROW_NUMBER() OVER (PARTITION BY ParentID ORDER BY ID DESC) RowIndex
FROM MyData
)
SELECT d.*
FROM MyData d
JOIN SortedRecords sr ON d.ID = sr.ID
JOIN RecordCount rc ON d.ParentID = rc.ParentID AND sr.RowIndex <= rc.RecordCount;
Select Top and Last rows in a table (SQL server)
To get the bottom 1000 you will want to order it by a column in descending order, and still take the top 1000.
SELECT TOP 1000 *
FROM [SomeTable]
ORDER BY MySortColumn DESC
If you care for it to be in the same order as before you can use a common table expression for that:
;WITH CTE AS (
SELECT TOP 1000 *
FROM [SomeTable]
ORDER BY MySortColumn DESC
)
SELECT *
FROM CTE
ORDER BY MySortColumn
SQL Server SELECT LAST N Rows
You can do it by using the ROW NUMBER BY PARTITION Feature also. A great example can be found here:
I am using the Orders table of the Northwind database... Now let us retrieve the Last 5 orders placed by Employee 5:
SELECT ORDERID, CUSTOMERID, OrderDate
FROM
(
SELECT ROW_NUMBER() OVER (PARTITION BY EmployeeID ORDER BY OrderDate DESC) AS OrderedDate,*
FROM Orders
) as ordlist
WHERE ordlist.EmployeeID = 5
AND ordlist.OrderedDate <= 5
Select bottom n records and concatenate in same row
Select last 20 rows when ordered by id. Return in ASC order, with a single query
SELECT
STUFF((
SELECT '; ' +
ISNULL(val1, '') + '; ' +
ISNULL(val2, '') + '; ' +
ISNULL(val3, '') + '; ' +
ISNULL(val4, 14), '')
FROM
(
SELECT top(20) *
FROM table_x
ORDER BY id DESC
) AS Latest_rec
ORDER BY id ASC
FOR XML PATH ('')), 1, 2, '') AS val;
How to select bottom N rows from each group - Oracle 11g
Try this:
WITH ordered
AS (SELECT qcd_outlet_code,
qcd_year,
qcd_quarter,
qcd_credit,
Row_number()
over (
PARTITION BY qcd_outlet_code
ORDER BY qcd_outlet_code, qcd_year DESC, qcd_quarter DESC)
AS rn
FROM QTR_CREDIT_DATA)
SELECT d.qcd_outlet_code AS "Outlet_Code:string",
d.qcd_quarter
||' '
||d.qcd_year AS "MCT_quarter:string",
Nvl(d.qcd_credit, 0) AS "MCT_Total_Credits_Earned",
Row_number()
over (
PARTITION BY qcd_outlet_code
ORDER BY qcd_outlet_code, qcd_year, qcd_quarter)
AS "Display_Order:string"
FROM ordered d
WHERE rn <= 2;
SQL Query select top and bottom rows from every batch in the table
Use row_number()
:
select *
from (
select t.*,
row_number() over(partition by batchkey order by executiontime) rn_asc,
row_number() over(partition by batchkey order by executiontime desc) rn_desc
from mytable t
) t
where 1 in (rn_asc, rn_desc)
The logic is to rank records having the same batchkey
by ascending and descending executiontime
, then to filter on the the top and bottom row numbers. If there is just one record for a given batchkey
, it gets ranked 1
in both directions.
Related Topics
Executing a Stored Procedure Inside Begin/End Transaction
SQL Server Stored Procedure Parameters
SQL Statement Help - Select Latest Order for Each Customer
Find Which Rows Have Different Values for a Given Column in Teradata SQL
Using Hibernate's Criteria and Projections to Select Multiple Distinct Columns
How to Treat Max() of an Empty Table as 0 Instead of Null
SQL Update Query Syntax with Inner Join
Exception Ora-08103: Object No Longer Exists on Using Setfetchsize of Hibernate
Db2- How to Check If Varchar Field Value Has Integers
SQL Query for Finding Rows with Special Characters Only
Sqlite Format Number with 2 Decimal Places Always
Select from One Table, Insert into Another Table Oracle SQL Query
MySQL Question - How to Handle Multiple Types of Users - One Table or Multiple
How to See Progress of Running SQL Stored Procedures
Removing Leading Zeros from Varchar SQL Developer
How to Identify Invalid (Corrupted) Values Stored in Oracle Date Columns