TOP Clause in SQL in Where Caluse - sql-server

I want to use top clause in where Condition.
I want to do like this:-
SELECT column_name_1,column_name_2,column_name_n
FROM table_name
Where condition and top n.

You could use a ranking function (ROW_NUMBER()/RANK()/DENSE_RANK() depending on your requirements) and a subquery. But I have no idea why you would want to do this. It is more verbose and has no benefits that I can see - consider the following queries, which are equivalent (in terms of results, not necessarily execution plans):
DECLARE #N INT = 5;
SELECT TOP (#N) name
FROM sys.all_objects
ORDER BY object_id;
SELECT name
FROM ( SELECT object_id, name, RowNum = ROW_NUMBER() OVER(ORDER BY object_id)
FROM sys.all_objects
) AS o
WHERE RowNum <= #N;
I know which one I find easier to read

Related

Microsoft SQL, ROW_NUMBER & OUTER APPLY

I am having some problems with an outer apply, specifically that I cant reference ROW from within the OUTER APPLY.
Note if i place the where ROW criteria outside of the outer apply people without a 3 ,4 or 5th row for example aren't returned.
OUTER APPLY
(
SELECT ROW_NUMBER()
OVER (ORDER BY L.DATECREATED) AS ROW,
L.PERCENTAGE
LABOURALLOCATION L
***WHERE ROW = 1***
) RECORDS
You can't reference an aliased column in your where clause. You also can't put the row_number() function in your where or in a having. If you need to limit to row = 1 within the cross apply, the only way to do it is to put the row_number() in a subquery or perhaps as a CTE. Note that ROW is a reserved word - I usually use ROW_NUM.
OUTER APPLY
(
SELECT ROW_NUM, PERCENTAGE
FROM (
SELECT ROW_NUMBER()
OVER (ORDER BY L.DATECREATED) AS ROW_NUM,
L.PERCENTAGE
FROM LABOURALLOCATION L
)
WHERE ROW_NUM = 1
) RECORDS

T- SQL Duplicate Records

I am trying to delete every other record which are duplicate my select query returns every other record duplicate (tblPoints.ptUser_ID) is the unique id
SELECT *, u.usMembershipID
FROM [ABCRewards].[dbo].[tblPoints]
inner join tblUsers u on u.User_ID = tblPoints.ptUser_ID
where ptUser_ID in (select user_id from tblusers where Client_ID = 8)
and ptCreateDate >= '3/9/2016'
and ptDesc = 'December Anniversary'
Usually duplicates getting returned by an INNER JOIN suggests an issue with the query but if you are certain that your join is correct then this would do it:
;WITH CTE
AS (SELECT *
, ROW_NUMBER() OVER(PARTITION BY t.ptUser_ID ORDER BY t.ptUser_ID) AS rn
FROM [ABCRewards].[dbo].[tblPoints] AS t)
/*Uncomment below to Review duplicates*/
--SELECT *
--FROM CTE
--WHERE rn > 1;
/*Uncomment below to Delete duplicates*/
--DELETE
--FROM CTE
--WHERE rn > 1;
When cleaning up data duplication, I have always used the same query pattern to delete all the duplicate and keep the wanted one(original, most recent, whatever). The below query pattern delete all duplicates and keep the one you wish to keep.
Just replace all [] with your table and fields.
[Field(s)ToDetectDuplications] : Put here the field(s) that allow you to say that they are dupplicate when they have the same values.
[Field(s)ToChooseWhichDupplicationIsKept ] : Put here a fields to choose which dupplicate will be kept. For exemple, the one with the
biggest value or the less old one.
.
DELETE [YourTableName]
FROM [YourTableName]
INNER JOIN (SELECT [YourTablePrimaryKey],
I = ROW_NUMBER() OVER(PARTITION BY [Field(s)ToDetectDuplications] ORDER BY [Field(s)ToChooseWhichDupplicationIsKept ] DESC)
FROM [dbo].[YourTableName]) AS T ON [YourTableName].[YourTablePrimaryKey] = T.[YourTablePrimaryKey]
AND T.I > 1
I recommend to have a look to what will be deleted before. To do so, just replace the "delete" statement with a "select" instead just like below.
SELECT T.I,
[YourTableName].*
FROM [YourTableName]
INNER JOIN (SELECT [YourTablePrimaryKey],
I = ROW_NUMBER() OVER(PARTITION BY [Field(s)ToDetectDuplications] ORDER BY [Field(s)ToChooseWhichDupplicationIsKept ] DESC)
FROM [dbo].[YourTableName]) AS T ON [YourTableName].[YourTablePrimaryKey] = T.[YourTablePrimaryKey]
AND T.I > 1
Explanation :
Here we use "row_number()", "Partition by" and "Order by" to detect duplicates. "Partition" group together all rows. Set your partitions fields in order to have one row per partition when the data is right. That way bad data come out with partition that have more than one row. Row_number assign them a number. When a number is greater then 1, then this mean there is a duplicate with this partition. The "order by" is use to tell "row_number" in what order to assign them a number. Number 1 is kept, all others are deleted.
Exemple with OP's schema and specification
Here I attempted to fill the patern with guess I have made on your database schema.
DECLARE #userID INT
SELECT #userID = 8
SELECT T.I,
[ABCRewards].[dbo].[tblPoints].*
FROM [ABCRewards].[dbo].[tblPoints]
INNER JOIN (SELECT [YourTablePrimaryKey],
I = ROW_NUMBER() OVER(PARTITION BY T.ptDesc, T.ptUser_ID ORDER BY ptCreateDate DESC)
FROM [ABCRewards].[dbo].[tblPoints]
WHERE T.ptCreateDate >= '3/9/2016'
AND T.ptDesc = 'December Anniversary'
AND T.ptUser_ID = #userID
) AS T ON [ABCRewards].[dbo].[tblPoints].[YourTablePrimaryKey] = T.[YourTablePrimaryKey]
AND T.I > 1

SQL Server: How to select fist 10 records from table without using TOP keyword

There is a table which contains 50 records. I want to select first 10 records without using TOP keyword.
In SQL Server 2012+ you can use OFFSET ... FETCH
SELECT *
FROM YourTable
ORDER BY YourColumn ASC
OFFSET 0 ROWS
FETCH FIRST 10 ROWS ONLY
You can use ROW_NUMBER and Common Table Expression to query any range of data.
USE AdventureWorks2012;
GO
WITH OrderedOrders AS
(
SELECT SalesOrderID, OrderDate,
ROW_NUMBER() OVER (ORDER BY OrderDate) AS RowNumber
FROM Sales.SalesOrderHeader
)
SELECT SalesOrderID, OrderDate, RowNumber
FROM OrderedOrders
WHERE RowNumber <= 10 -- other conditions: RowNumber between 50 and 60
Refere ROW NUMBER Here
Although it's probably the same thing internally, you can use
set rowcount 10
and then run the query.
I guess you can try something like this:
SELECT t.Id, t.Name FROM Table t
WHERE 10 > (SELECT count(*) FROM Table t2 WHERE t.id > t2.id)
You can use ROW_NUMBER. Let's say your table contains columns ID and Name. In that case you can use such query:
SELECT t.Id, t.Name
FROM (
SELECT ID, Name,
ROW_NUMBER() OVER (ORDER BY Id) AS RowNumber
FROM TableName
) t
WHERE RowNumber <= 10

Taking the second last row with only one select in SQL Server?

I was trying to select the second last row with SQL Server. So I wrote a query like this:
SELECT TOP 1 * From Cinema
WHERE CinemaID!=(SELECT TOP 1 CinemaID
FROM Cinema
ORDER BY CinemaID DESC)
ORDER BY CinemaID DESC
and it did what I need. But I want to do the same thing with only one select.
I read that the LIMIT clause in MySql does that. But I couldn't find any equivalent
of that. So I appreciate any help about finding something useful.
To get the 2nd last row in one select:
SELECT TOP 1 * From
(select Top 2 * from Cinema ORDER BY CinemaID DESC) x
ORDER BY CinemaID
It's really only "one" select because the outer select is over only 2 rows.
The best way to do this (and compatible with the ANSI SQL standard), is to use a CTE (Common Table Expression) with the ROW_NUMBER function:
;WITH OrderedCinemas AS
(
SELECT
CinemaID, CinemaName,
ROW_NUMBER() OVER(ORDER BY CinemaID DESC) AS 'RowNum'
FROM dbo.Cinema
)
SELECT
CinemaID, CinemaName
FROM OrderedCinemas
WHERE RowNum = 2
By using this construction, you can get the second highest value very easily - or the fifth hightest (WHERE RowNum = 5) or the top 3 rows (WHERE RowNum <= 3) or whatever you need - the CinemaID values are just ordered and sequentially numbered for your use.
The following doesn't work, explaination of why:
Using ranking-function derived column in where clause (SQL Server 2008)
I'll keep it here for completeness:
SELECT row_number() OVER (ORDER BY col) r, *
FROM tbl
WHERE r = 2
More info:
http://www.bidn.com/blogs/marcoadf/bidn-blog/379/ranking-functions-row_number-vs-rank-vs-dense_rank-vs-ntile
So I think the most readable way of doing it is:
SELECT * FROM (SELECT row_number() OVER (ORDER BY col) r, * FROM tbl) q
WHERE r = 2
Since this (old) question has not been tagged with a specific SQL-Server version and none of (the very good) answers uses only one SELECT clause - for the good reason that it was not possible in old verions - here is one that works only in recent, 2012+ versions:
SELECT c.*
FROM dbo.Cinema AS c
ORDER BY CinemaID DESC
OFFSET 1 ROW
FETCH FIRST 1 ROW ONLY ;
Tested at SQLFiddle
SELECT TOP 1 * FROM tbl_CompanyMaster
where Companyid >= (SELECT MAX(Companyid) - 1 FROM tbl_CompanyMaster)
select * from TABLE_NAME order by COLUMN_NAME desc limit 1,1 ;
Where COLUMN_NAME should be "primary key" or "Unique"
Two selects but a bit quicker
select top 1 * from(
SELECT TOP 2 * From Cinema
WHERE CinemaID
ORDER BY CinemaID DESC) top2
Order by CinemaID
So, in the spirit of only using one SELECT clause as stated in the OP and thoroughly abusing T-SQL in general, I proffer something I would never, ever recommend using in production that nevertheless satisfies the stated criteria:
update Cinema
set Cinema.SomeField = Cinema.SomeField
output inserted.*
from Cinema
inner join
(
select top 2 CinemaID, ROW_NUMBER() over (order by CinemaID desc) as RowNum
from Cinema
) rsRowNum on rsRowNum.CinemaID = Cinema.CinemaID
where RowNum = 2
This query will also work for SQLITE
SELECT * From
(select * from Cinema ORDER BY CinemaID DESC LIMIT 2) AS name
ORDER BY CinemaID LIMIT 1
You're only using one SELECT statement. A SELECT statement can include an arbitrary (more or less) number of subqueries--correlated subqueries, scalar subqueries, and so on, each with their own SELECT clause. But it's still just one SELECT statement.
If you want to avoid a subquery, you could select the top 2, and skip the one you don't want. That kind of programming is pretty brittle, though. You have to remember what to skip every time; sooner or later, you'll forget.
SELECT field_name FROM (SELECT TOP 2 field_name FROM table_name
ORDER BY field_name DESC)
WHERE rownum = 2;
select top 1* from(SELECT TOP 2 * From Cinema
WHERE CinemaID
ORDER BY CinemaID DESC) XYZ
ORDER BY CinemaID
where XYZ is not a keyword. It is just a word. And word can be anything.
If you need to do that, but:
the column is different than id
you need to order by some specific column
can't use SELECT on FROM clause (if you are using old versions of Hibernate, per example).
You can do:
select top 1 * from Cinema
where date < (select MAX(date) from Cinema)
order by date desc
The easiest way to get second last row from sql table is user ORDER BY CinemaID DESC and set LIMIT 1,1
TRY THIS
SELECT * from `Cinema` ORDER BY `CinemaID` DESC LIMIT 1,1
select * from
(select ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) as R, * from Cinema ) T1
where (select count(*) from Cinema ) - T1.R = 1
SELECT * FROM record
WHERE record_id=(SELECT max(record_id)-1 FROM record)
Here is my code:
SELECT * From
(select * from table name ORDER BY column name DESC LIMIT 2) AS xyz
ORDER BY column name LIMIT 1;

Filter first then select page

How to first filter the result based on params then to apply where-between?
Some thing like
With Results as
(
Select colName,Title, Row_Number(Over...) as row from a table where colName=5
)
Select * from Results
where
row between #first and #last
But it does not works. I need to move my where colName=5 from with clause to outside then I got wrong data as It first get rows between #first n #last then search for colName=5.
Also I want count of Results.
Any idea?
You can use COUNT(*) OVER() to get the count of the unfiltered results
WITH cte as
(
select *,
ROW_NUMBER() over (order by name desc) AS RN,
count(*) over() AS [Count]
from master..spt_values
)
SELECT name, number,[Count]
FROM cte
WHERE RN BETWEEN 20 AND 24
Returns
name number Count
----------------------------------- ----------- -----------
VIEW 8278 2506
VIEW 8278 2506
view 2 2506
varchar 3 2506
varbinary 1 2506
This has performance implications though. You might want to just calculate the COUNT up front and cache it somewhere rather than recalculating it for every page request.
Your ROW_NUMBER syntax is incorrect. It should be this:
With Results as
(
SELECT colName, Title, ROW_NUMBER() OVER (ORDER BY ...) AS RN
FROM your_table
WHERE colName = 5
)
SELECT * FROM Results
WHERE rn BETWEEN #first AND #last
ORDER BY rn
See the documentation for more information.
I use approach very similar to Martin Smiths (currently selected answer) and at least in the tests I've made it gives better performance results.
; WITH cte as
(
select *,
ROW_NUMBER() over (order by name desc) AS RN
from master..spt_values
)
SELECT name, number, (SELECT COUNT(*) FROM cte) AS [Count]
FROM cte
WHERE RN BETWEEN 20 AND 24
Run this and his queries side by side and compare execution plans.

Resources