Paging in SQL, how to use row number in where clause? - sql

I am trying to use the row numbers from MSSQL in the where clause of my SELECT query (in a stored procedure) however it won't let me reference that column in the where clause. How would you usually go about such a thing?
I'm trying the following (which doesn't work):
SELECT ROW_NUMBER() OVER(ORDER BY [dtd]) AS row_num, [data]
FROM dbo.Reports INNER JOIN dbo.Types
WITH (NOLOCK)
ON dbo.Reports.type = dbo.Types.id
WHERE [dbo].[Reports].[Id] = #Id AND (row_num < (#page - 1) * 30) AND (row_num > #page * 30)
ORDER BY [dtd] DESC

You can use a CTE:
WITH CTE AS
(
SELECT ROW_NUMBER() OVER(ORDER BY [dtd]) AS row_num, [data]
FROM dbo.Reports INNER JOIN dbo.Types
WITH (NOLOCK)
ON dbo.Reports.type = dbo.Types.id
WHERE [dbo].[Reports].[Id] = #Id
)
SELECT * FROM CTE
WHERE (row_num < (#page - 1) * 30) AND (row_num > #page * 30)
ORDER BY [dtd] DESC

Related

Merge two SELECT queries with different ORDER BY

I have a Story table, and I need the following query:
- first 6 rows sorted by distance (I calculate it)
- next rows are sorted by TIME property descending
declare #profileID int
set #profileID = 6
declare #longitude float
set #longitude = 17.6009169090776
declare #latitude float
set #latitude = 46.9548404806283
SELECT
first.*
FROM
(
SELECT top 6
[dbo].[Story].*,
SQRT( POWER(#Longitude - [dbo].[Story].[Longitude], 2) + POWER(#Latitude - [dbo].[Story].[Latitude], 2)) as [DistanceFromUser]
FROM
[dbo].[Follow]
LEFT JOIN
[dbo].[Story]
ON
[dbo].[Follow].[Followed] = [dbo].[Story].[ProfileID]
WHERE
[dbo].[Follow].[Follower] = #ProfileID and
[dbo].[Story].IsDraft = 0
ORDER BY
[DistanceFromUser] asc
) first
UNION ALL
SELECT
last.*
FROM
(
SELECT TOP 100 PERCENT
[dbo].[Story].*,
SQRT( POWER(#Longitude - [dbo].[Story].[Longitude], 2) + POWER(#Latitude - [dbo].[Story].[Latitude], 2)) as [DistanceFromUser]
FROM
[dbo].[Follow]
LEFT JOIN
[dbo].[Story]
ON
[dbo].[Follow].[Followed] = [dbo].[Story].[ProfileID]
WHERE
[dbo].[Follow].[Follower] = #ProfileID and
[dbo].[Story].IsDraft = 0
**ORDER BY
Time desc**
) last
My problem is the second query. It not sorts the records after the 6'th row descending by the TIME property, it sorts as ascending.
thnx
Try This
SELECT
first.*
FROM
(
SELECT top 6
[dbo].[Story].*,
SQRT( POWER(#Longitude - [dbo].[Story].[Longitude], 2) + POWER(#Latitude - [dbo].[Story].[Latitude], 2)) as [DistanceFromUser]
,1 as ord
FROM
[dbo].[Follow]
LEFT JOIN
[dbo].[Story]
ON
[dbo].[Follow].[Followed] = [dbo].[Story].[ProfileID]
WHERE
[dbo].[Follow].[Follower] = #ProfileID and
[dbo].[Story].IsDraft = 0
ORDER BY
[DistanceFromUser] asc
) first
UNION ALL
SELECT
last.*
FROM
(
SELECT TOP 100 PERCENT
[dbo].[Story].*,
SQRT( POWER(#Longitude - [dbo].[Story].[Longitude], 2) + POWER(#Latitude - [dbo].[Story].[Latitude], 2)) as [DistanceFromUser]
,row_number() over(order by Time desc) as ord
FROM
[dbo].[Follow]
LEFT JOIN
[dbo].[Story]
ON
[dbo].[Follow].[Followed] = [dbo].[Story].[ProfileID]
WHERE
[dbo].[Follow].[Follower] = #ProfileID and
[dbo].[Story].IsDraft = 0
**ORDER BY
Time desc**
) last
My try (example)
declare #ta as table
(
id int
,na varchar(100)
,sal numeric(18,2)
)
insert into #ta( id,na,sal) values (1,'aa',10)
insert into #ta( id,na,sal) values (3,'bb',100)
insert into #ta( id,na,sal) values (2,'c',5)
insert into #ta( id,na,sal) values (4,'dd',50)
select * from
(select top 2 * , 1 as ord from #ta order by id) as f
union all
select * from (select top 100 percent * , row_number() over(order by sal desc) ord from #ta order by sal desc
) as tt
If you want the result set ordered in a particular way, then the outermost SELECT needs an ORDER BY.
You can control the ordering by including multiple keys in the outer ORDER BY. If I'm reading the query correctly, the only difference is the order by, so put the logic for the queries in a CTE:
WITH sf as (
SELECT s.*,
SQRT( POWER(#Longitude - s.[Longitude], 2) + POWER(#Latitude - s.[Latitude], 2)) as [DistanceFromUser]
FROM [dbo].[Follow] f LEFT JOIN
[dbo].[Story] s
ON f.[Followed] = s.[ProfileID]
WHERE f.[Follower] = #ProfileID and
s.IsDraft = 0
)
SELECT sf.*
FROM ((SELECT TOP (6) sf.*, 1 as ord
FROM sf
ORDER BY [DistanceFromUser] ASC
) UNION ALL
(SELECT TOP (6) sf.*, 2 as ord
FROM sf
ORDER BY Time DESC
)
) sf
ORDER BY ord,
(CASE WHEN ord = 1 THEN DistanceFromUser END) ASC,
(CASE WHEN ord = 2 THEN Time END) DESC;
You can also do this with window functions:
WITH sf as (
SELECT s.*,
SQRT( POWER(#Longitude - s.[Longitude], 2) + POWER(#Latitude - s.[Latitude], 2)) as [DistanceFromUser]
FROM [dbo].[Follow] f LEFT JOIN
[dbo].[Story] s
ON f.[Followed] = s.[ProfileID]
WHERE f.[Follower] = #ProfileID and
s.IsDraft = 0
)
SELECT sf.*
FROM (SELECT sf.*,
ROW_NUMBER() OVER (ORDER BY DistanceFromUser) as seqnum_d,
ROW_NUMBER() OVER (ORDER BY Time DESC) as seqnum_t
FROM sf
)
) sf
WHERE seqnum_d <= 6 OR seqnum_t <= 6
ORDER BY ord,
(CASE WHEN seqnum_d <= 6 THEN DistanceFromUser END) ASC,
(CASE WHEN seqnum_t <= 6 THEN Time END) DESC;
Your version could include the same row twice. This version will not duplicate rows that are in the top 6 for both conditions.
So I ended with next solution thnx to everyone
WITH TempTable as
(
SELECT [dbo].[Story].*,
SQRT( POWER(#Longitude - [dbo].[Story].[Longitude], 2) + POWER(#Latitude - [dbo].[Story].[Latitude], 2)) as [DistanceFromUser]
FROM
[dbo].[Follow]
LEFT JOIN
[dbo].[Story]
ON [dbo].[Follow].[Followed] = [dbo].[Story].[ProfileID]
WHERE [dbo].[Follow].[Follower] = #ProfileID and
[dbo].[Story].IsDraft = 0
)
SELECT
first.*
FROM
(
SELECT top 6
*,
1 as ord
FROM
TempTable
ORDER BY
[DistanceFromUser] asc
) first
UNION ALL
SELECT
last.*
FROM
(
SELECT TOP 100 PERCENT
*,
row_number() over(order by Time desc) as ord
FROM
TempTable
ORDER BY
Time desc
) last

Applying LIMIT and OFFSET to MS SQL server 2008 queries

I need to apply LIMIT and OFFSET to original query (without modifying it) in MSSQL server 2008.
Let's say the original query is:
SELECT * FROM energy_usage
(But it can be any arbitrary SELECT query)
That's what I came up with so far:
1. It does what I need, but the query generates extra column row_number which I don't need.
WITH OrderedTable AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, * FROM energy_usage
)
SELECT * FROM OrderedTable WHERE row_number BETWEEN 1 AND 10
2. This one doesn't work for some reason and returns the following error.
SELECT real_sql.* FROM (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, * FROM (SELECT * FROM energy_usage) as real_sql) as subquery
WHERE row_number BETWEEN 1 AND 10
More common case is:
SELECT real_sql.* FROM (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, * FROM (real sql query) as real_sql) as subquery
WHERE row_number BETWEEN {offset} + 1 AND {limit} + {offset}
Error:
The column prefix 'real_sql' does not match with a table name or alias
name used in the query.
Simply do not put it on SELECT list:
WITH OrderedTable AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, *
FROM energy_usage
)
SELECT col1, col2, col3 FROM OrderedTable WHERE row_number BETWEEN 1 AND 10;
SELECT * is common anti-pattern and should be avoided anyway. Plus ORDER BY (SELECT 1) will not give you guarantee of stable sort between executions.
Second if you need only ten rows use:
SELECT TOP 10 *
FROM energy_usage
ORDER BY ...
Unfortunately you won't get something nice as Selecting all Columns Except One
WITH OrderedTable AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, *
FROM energy_usage
)
SELECT * EXCEPT row_number FROM OrderedTable WHERE row_number BETWEEN 1 AND 10;
This would solve the problem.
DECLARE #offset INT = 1;
DECLARE #limit INT = 10;
WITH Filtered AS (
SELECT TOP (#offset + #limit) *
FROM energy_usage
ORDER BY 1 ASC
), Results AS (
SELECT TOP (#limit) *
FROM Filtered
ORDER BY 1 DESC
)
SELECT *
FROM Results
ORDER BY 1 ASC;

SQL query how to get the local variable

I have this query and I want to get "page" variable which is created:
SELECT *, (rowNum / 5) + 1 as page
FROM (
SELECT *, Row_Number() OVER (ORDER BY items.id_item ASC) as rowNum
FROM items INNER JOIN utilizadores ON items.autor = utilizadores.id_user
) x
WHERE (rowNum / 1000) + 1 = 1
How can I get it in the query to make a procedure to search.
CREATE PROCEDURE get_items_by_page
#page int
AS
SELECT *, (rowNum / 5) + 1 as page
FROM (
SELECT *, Row_Number() OVER (ORDER BY items.id_item ASC) as rowNum
FROM items INNER JOIN utilizadores ON items.autor = utilizadores.id_user
) x
WHERE (rowNum / 1000) + 1 = 1 //the required condition to query is in bottom
GO
and page = #page;
CREATE PROCEDURE get_items_by_page
#page int
AS
SELECT x.*, (rowNum / 5) + 1 as page
FROM (
SELECT *, Row_Number() OVER (ORDER BY items.id_item ASC) as rowNum
FROM items INNER JOIN utilizadores ON items.autor = utilizadores.id_user
) x
WHERE (rowNum / 5) + 1 = #page
GO

SQL - Limit results

I was wondering if you could do something like this is Microsoft SQL Server 2k8 R2
Say I have a query which returns 100 rows of data.
Is their a way I can pass in some variables for example #lower_limit and #upper_limit.
Then I want the query to record the rows between the lower and upper limit
For example:
#lower_limit = 5
#upper_limt 10
Would return me rows 5 - 10 from the 100 records.
You can assign a ROW_NUMBER() over your result set and then use the BETWEEN statement to limit the rows.
A contrived example:
WITH data AS
(
SELECT
ID
,YourColumn
,ROW_NUMBER() OVER (ORDER BY ID) AS RowNum
FROM
YourTable
)
SELECT
*
FROM
data
WHERE
RowNum BETWEEN 5 AND 10
EDIT: For standard paging, here's exactly the technique I use in all the applications I develop:
DECLARE #PageNumber int = /* The page number you want */
DECLARE #PageSize int = /* The number of records per page */
WITH paged AS
(
SELECT
ROW_NUMBER() OVER(ORDER BY [OrderByColumns]) AS RowNum
,*
FROM
[YourSource]
)
SELECT
[Column1]
,[Column2]
,...
FROM
paged
WHERE
RowNum BETWEEN (#PageNumber - 1) * #PageSize + 1 AND #PageNumber * #PageSize
ORDER BY
[OrderByColumns] -- Same as used in ROW_NUMBER()
select *
from
(
select *, row_number() over(order by someColToOrderBy) RowNum
from yourTable
) a
where RowNum between #lower_limit and #uppder_limit
Something like this should work:
SELECT ID, Foo, Bar
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY ID ASC) AS Row, ID, Foo, Bar
FROM SomeTable
)
tmp
WHERE Row >= #RowRangeStart AND Row <= #RowRangeEnd

SQL WITH Statement, Unknown Column in where clause

I ve got the following query which is throwing the following error
Unkown Column 'RowNum'
WITH Employees AS
(
SELECT
(keyTblSp.RANK * 3) AS [Rank],
sp.*,
addr.Street,
addr.PostOfficeBox,
addr.StreetNumber
FROM Employee sp
INNER JOIN
FREETEXTTABLE(Employee, *, 'something', 1000) AS keyTblSp
ON sp.EmployeeId = keyTblSp.[KEY]
LEFT OUTER JOIN [Address] addr ON addr.EmployeeId = sp.EmployeeId
UNION ALL
SELECT
(keyTblAddr.RANK * 2) AS [Rank],
sp.*,
addr.Street,
addr.PostOfficeBox,
addr.StreetNumber
FROM Employee sp
LEFT OUTER JOIN [Address] addr ON addr.EmployeeId = sp.EmployeeId
INNER JOIN
FREETEXTTABLE([Address], *, 'something', 1000) AS keyTblAddr
ON addr.AddressId = keyTblAddr.[KEY]
)
SELECT ROW_NUMBER() OVER (ORDER BY [Rank] DESC) AS RowNum, *
FROM Employees
WHERE RowNum BETWEEN (1 - 1) * 10 + 1 AND 1 * 10
ORDER BY Rank DESC
This is because aliases are not recognized in WHERE clauses. Instead, use the full query like this:
WHERE ROW_NUMBER() OVER (ORDER BY [Rank] DESC) BETWEEN (1 - 1) * 10 + 1 AND 1 * 10
Try wrpping up your query to get the name usable in the where clause
SELECT
*
FROM
(SELECT
ROW_NUMBER() OVER (ORDER BY [Rank] DESC) AS RowNum
, *
FROM
Employees) AS Results
WHERE
RowNum BETWEEN (1 - 1) * 10 + 1 AND 1 * 10
ORDER BY
Rank
Your WHERE clause cannot refer to a window or aggregate function like ROW_NUMBER(). If you want to filter on the result of ROW_NUMBER(), you need to do so in the HAVING clause:
...
SELECT ROW_NUMBER() OVER (ORDER BY [Rank] DESC) AS RowNum, *
FROM Employees
HAVING RowNum BETWEEN (1 - 1) * 10 + 1 AND 1 * 10
ORDER BY Rank DESC
How about:
select top 10 *
from Employees
order by Rank Desc
Alternatively, does it work without the where rownum clause. (why is your between so tricky?).