T-SQL Statement OVER Clause Ranking Functions - sql

I have a table which looks like this:
Field1 Field2 ValidFrom
200 a 01.01.1999
200 b 01.01.2015
210 c 01.01.2015
210 c 01.01.2010
Now I try to generate a select-statement with an additional column, that increments when Field1 stays the same, but Field2 changes. The order of the value in the extraColumn should depend on the ValidFrom, which means e.g. 1 for 01.01.1999 and 2 for 01.01.2015 (and not the other way around!).
When Field1 changes, the value in the extraColumn should start with 1 again. It should keep the same value, when the combination of Field1 and Field2 doesn't change.
So the result I'd like to have would look like this:
extraColumn Field1 Field2 ValidFrom
1 200 a 01.01.1999
2 200 b 01.01.2015
1 210 c 01.01.2015
1 210 c 01.01.2010
I tried to get this result by using this query with the RANK() function:
select RANK() OVER (
PARTITION BY [Field1], [Field2]
ORDER BY [ValidFrom] DESC
) as 'extraColumn'
,Field1 ,Field2 ,ValidFrom
FROM table1
Unfortunately this did not work as I expected it to and it did kind of the opposite of which I wanted, so my result looked like:
extraColumn Field1 Field2 ValidFrom
1 200 a 01.01.1999
1 200 b 01.01.2015
1 210 c 01.01.2015
2 210 c 01.01.2010
Any ideas what I did wrong?

Here I have answered a similar problem. Below is the adaptation to your conditions:
-- Preparation
declare #t table (
Field1 int,
Field2 char,
ValidFrom date
);
insert into #t (Field1, Field2, ValidFrom)
values
(200, 'a', '19990101'),
(200, 'b', '20150101'),
(210, 'c', '20150101'),
(210, 'c', '20100101');
-- The query
with cte as (
select t.*,
lag(t.Field2) over(partition by t.Field1 order by t.ValidFrom) as [Prev2]
from #t t
)
select c.Field1, c.Field2, c.ValidFrom,
sum(case when c.Prev2 = c.Field2 then 0 else 1 end)
over(partition by c.Field1 order by c.ValidFrom) as [ExtraColumn]
from cte c;
I only hope you aren't going to run this against millions or records, because 2 partitionings won't make it easy on the CPU and memory. Oh yes, and you need SQL Server 2012 or later for this to work.

Use DENSE_RANK, you need to use Field2 in order by to get the required result not ValidFrom
DENSE_RANK() OVER (PARTITION BY [Field1] ORDER BY [Field2])

Try it like this...
WITH
cte_MaxDate AS (
SELECT
*,
MaxDate = MAX(td.ValidFrom) OVER (PARTITION BY td.Feild1, td.Field2)
FROM
#TestData td
)
SELECT
DENSE_RANK() OVER (PARTITION BY md.Feild1 ORDER BY md.MaxDate, md.Field2),
md.Feild1, md.Field2, md.ValidFrom--, md.MaxDate
FROM
cte_MaxDate md;

I think you will have to use recursion here to calculate row-by-row whatever field2 changed or not.
WITH CTE_RN AS
(
SELECT *
, ROW_NUMBER() OVER (PARTITION BY Field1 ORDER BY ValidFrom) RN
FROM Table1
)
, RCTE AS
(
SELECT *, 1 AS ExtraColumn
FROM CTE_RN WHERE RN = 1
UNION ALL
SELECT c.*
, CASE WHEN r.Field2 = c.Field2 THEN r.ExtraColumn ELSE r.ExtraColumn + 1 END
FROM RCTE r
INNER JOIN CTE_RN c ON r.Field1 = c.Field1 AND r.RN + 1 = c.RN
)
SELECT *
FROM RCTE
ORDER BY Field1, ValidFrom
OPTION (MAXRECURSION 0)
SQLFiddle DEMO
(I added few more rows for more complex sample)

Related

Find the Contents of a row by provided Row Number? [duplicate]

I found one question answered with the Row_Number() function in the where clause. When I tried one query, I was getting the following error:
"Msg 4108, Level 15, State 1, Line 1
Windowed functions can only appear in the SELECT or ORDER BY clauses."
Here is the query I tried. If somebody knows how to solve this, please let me know.
SELECT employee_id
FROM V_EMPLOYEE
WHERE row_number() OVER ( ORDER BY employee_id ) > 0
ORDER BY Employee_ID
To get around this issue, wrap your select statement in a CTE, and then you can query against the CTE and use the windowed function's results in the where clause.
WITH MyCte AS
(
select employee_id,
RowNum = row_number() OVER ( order by employee_id )
from V_EMPLOYEE
ORDER BY Employee_ID
)
SELECT employee_id
FROM MyCte
WHERE RowNum > 0
SELECT employee_id
FROM (
SELECT employee_id, ROW_NUMBER() OVER (ORDER BY employee_id) AS rn
FROM V_EMPLOYEE
) q
WHERE rn > 0
ORDER BY
Employee_ID
Note that this filter is redundant: ROW_NUMBER() starts from 1 and is always greater than 0.
Select * from
(
Select ROW_NUMBER() OVER ( order by Id) as 'Row_Number', *
from tbl_Contact_Us
) as tbl
Where tbl.Row_Number = 5
I think you want something like this:
SELECT employee_id
FROM (SELECT employee_id, row_number()
OVER (order by employee_id) AS 'rownumber'
FROM V_EMPLOYEE) TableExpressionsMustHaveAnAliasForDumbReasons
WHERE rownumber > 0
In response to comments on rexem's answer, with respect to whether a an inline view or CTE would be faster I recast the queries to use a table I, and everyone, had available: sys.objects.
WITH object_rows AS (
SELECT object_id,
ROW_NUMBER() OVER ( ORDER BY object_id) RN
FROM sys.objects)
SELECT object_id
FROM object_rows
WHERE RN > 1
SELECT object_id
FROM (SELECT object_id,
ROW_NUMBER() OVER ( ORDER BY object_id) RN
FROM sys.objects) T
WHERE RN > 1
The query plans produced were exactly the same. I would expect in all cases, the query optimizer would come up with the same plan, at least in simple replacement of CTE with inline view or vice versa.
Of course, try your own queries on your own system to see if there is a difference.
Also, row_number() in the where clause is a common error in answers given on Stack Overflow. Logicaly row_number() is not available until the select clause is processed. People forget that and when they answer without testing the answer, the answer is sometimes wrong. (A charge I have myself been guilty of.)
I feel like all the answers showing use of a CTE or Sub Query are sufficient fixes for this, but I don't see anyone getting to the heart of why OP has a problem. The reason why what OP suggested doesn't work is due to logical query processing order here:
FROM
ON
JOIN
WHERE
GROUP BY
WITH CUBE/ROLLUP
HAVING
SELECT
DISTINCT
ORDER BY
TOP
OFFSET/FETCH
I believe this contributes to the answer greatly, because it explains why issues like this one occur. WHERE is always processed before SELECT making a CTE or Sub Query necessary for many functions. You will see this a lot in SQL Server.
WITH MyCte AS
(
select
employee_id,
RowNum = row_number() OVER (order by employee_id)
from V_EMPLOYEE
)
SELECT employee_id
FROM MyCte
WHERE RowNum > 0
ORDER BY employee_id
Using CTE (SQL Server 2005+):
WITH employee_rows AS (
SELECT t.employee_id,
ROW_NUMBER() OVER ( ORDER BY t.employee_id ) 'rownum'
FROM V_EMPLOYEE t)
SELECT er.employee_id
FROM employee_rows er
WHERE er.rownum > 1
Using Inline view/Non-CTE Equivalent Alternative:
SELECT er.employee_id
FROM (SELECT t.employee_id,
ROW_NUMBER() OVER ( ORDER BY t.employee_id ) 'rownum'
FROM V_EMPLOYEE t) er
WHERE er.rownum > 1
based on OP's answer to question:
Please see this link. Its having a
different solution, which looks
working for the person who asked the
question. I'm trying to figure out a
solution like this.
Paginated query using sorting on different columns using ROW_NUMBER() OVER () in SQL Server 2005
~Joseph
"method 1" is like the OP's query from the linked question, and "method 2" is like the query from the selected answer. You had to look at the code linked in this answer to see what was really going on, since the code in the selected answer was modified to make it work. Try this:
DECLARE #YourTable table (RowID int not null primary key identity, Value1 int, Value2 int, value3 int)
SET NOCOUNT ON
INSERT INTO #YourTable VALUES (1,1,1)
INSERT INTO #YourTable VALUES (1,1,2)
INSERT INTO #YourTable VALUES (1,1,3)
INSERT INTO #YourTable VALUES (1,2,1)
INSERT INTO #YourTable VALUES (1,2,2)
INSERT INTO #YourTable VALUES (1,2,3)
INSERT INTO #YourTable VALUES (1,3,1)
INSERT INTO #YourTable VALUES (1,3,2)
INSERT INTO #YourTable VALUES (1,3,3)
INSERT INTO #YourTable VALUES (2,1,1)
INSERT INTO #YourTable VALUES (2,1,2)
INSERT INTO #YourTable VALUES (2,1,3)
INSERT INTO #YourTable VALUES (2,2,1)
INSERT INTO #YourTable VALUES (2,2,2)
INSERT INTO #YourTable VALUES (2,2,3)
INSERT INTO #YourTable VALUES (2,3,1)
INSERT INTO #YourTable VALUES (2,3,2)
INSERT INTO #YourTable VALUES (2,3,3)
INSERT INTO #YourTable VALUES (3,1,1)
INSERT INTO #YourTable VALUES (3,1,2)
INSERT INTO #YourTable VALUES (3,1,3)
INSERT INTO #YourTable VALUES (3,2,1)
INSERT INTO #YourTable VALUES (3,2,2)
INSERT INTO #YourTable VALUES (3,2,3)
INSERT INTO #YourTable VALUES (3,3,1)
INSERT INTO #YourTable VALUES (3,3,2)
INSERT INTO #YourTable VALUES (3,3,3)
SET NOCOUNT OFF
DECLARE #PageNumber int
DECLARE #PageSize int
DECLARE #SortBy int
SET #PageNumber=3
SET #PageSize=5
SET #SortBy=1
--SELECT * FROM #YourTable
--Method 1
;WITH PaginatedYourTable AS (
SELECT
RowID,Value1,Value2,Value3
,CASE #SortBy
WHEN 1 THEN ROW_NUMBER() OVER (ORDER BY Value1 ASC)
WHEN 2 THEN ROW_NUMBER() OVER (ORDER BY Value2 ASC)
WHEN 3 THEN ROW_NUMBER() OVER (ORDER BY Value3 ASC)
WHEN -1 THEN ROW_NUMBER() OVER (ORDER BY Value1 DESC)
WHEN -2 THEN ROW_NUMBER() OVER (ORDER BY Value2 DESC)
WHEN -3 THEN ROW_NUMBER() OVER (ORDER BY Value3 DESC)
END AS RowNumber
FROM #YourTable
--WHERE
)
SELECT
RowID,Value1,Value2,Value3,RowNumber
,#PageNumber AS PageNumber, #PageSize AS PageSize, #SortBy AS SortBy
FROM PaginatedYourTable
WHERE RowNumber>=(#PageNumber-1)*#PageSize AND RowNumber<=(#PageNumber*#PageSize)-1
ORDER BY RowNumber
--------------------------------------------
--Method 2
;WITH PaginatedYourTable AS (
SELECT
RowID,Value1,Value2,Value3
,ROW_NUMBER() OVER
(
ORDER BY
CASE #SortBy
WHEN 1 THEN Value1
WHEN 2 THEN Value2
WHEN 3 THEN Value3
END ASC
,CASE #SortBy
WHEN -1 THEN Value1
WHEN -2 THEN Value2
WHEN -3 THEN Value3
END DESC
) RowNumber
FROM #YourTable
--WHERE more conditions here
)
SELECT
RowID,Value1,Value2,Value3,RowNumber
,#PageNumber AS PageNumber, #PageSize AS PageSize, #SortBy AS SortBy
FROM PaginatedYourTable
WHERE
RowNumber>=(#PageNumber-1)*#PageSize AND RowNumber<=(#PageNumber*#PageSize)-1
--AND more conditions here
ORDER BY
CASE #SortBy
WHEN 1 THEN Value1
WHEN 2 THEN Value2
WHEN 3 THEN Value3
END ASC
,CASE #SortBy
WHEN -1 THEN Value1
WHEN -2 THEN Value2
WHEN -3 THEN Value3
END DESC
OUTPUT:
RowID Value1 Value2 Value3 RowNumber PageNumber PageSize SortBy
------ ------ ------ ------ ---------- ----------- ----------- -----------
10 2 1 1 10 3 5 1
11 2 1 2 11 3 5 1
12 2 1 3 12 3 5 1
13 2 2 1 13 3 5 1
14 2 2 2 14 3 5 1
(5 row(s) affected
RowID Value1 Value2 Value3 RowNumber PageNumber PageSize SortBy
------ ------ ------ ------ ---------- ----------- ----------- -----------
10 2 1 1 10 3 5 1
11 2 1 2 11 3 5 1
12 2 1 3 12 3 5 1
13 2 2 1 13 3 5 1
14 2 2 2 14 3 5 1
(5 row(s) affected)
select salary from (
select Salary, ROW_NUMBER() over (order by Salary desc) rn from Employee
) t where t.rn = 2

Selecting unique records from a table and giving weight to repetitions

So I need to select a bunch of document IDs and a Value from a table and then give weight for each of them depending on the Value. It works as follows:
Say the table has the values:
DocID Value
1 1
1 1
2 1
2 2
The select will select each unique DocID and then give it a weight value. A Document with the same value repeated twice will have less weight than a document with 2 different values. So output will be something like this:
DocID Weight
1 1.5
2 2
So as you see, since Document 1 has the value 1 repeated twice it will have the initial +1 to weight from first value and then +0.5 since it repeated once (will get +0.5 again if value repeats more than that). Then Document 2 has the weight of 2 since the value 1 appeared once (+1) and value 2 appeared once (+2)
Any help will be highly appreciated.
First, aggregate the data to get a count per document/value. Then, if I understand the logic correctly, the rest is just an addition aggregation:
select docid,
sum(case when cnt > 1 then 1 + 0.5 * (cnt - 1) else 0 end)
from (select docid, value, count(*) as cnt
from t
group by docid, value
) dv
group by docid;
Actually, I think the outer query could be simplified to:
select docid,
0.5 + 0.5 * sum(cnt)
And, the whole thing could be expressed with count(distinct):
select docid,
(0.5 * count(distinct value) +
0.5 * count(*)
)
from t
group by docid;
Try this,
declare #t table(DocID int, Value int)
insert into #t values
(1, 1)
,(1, 1)
,(2, 1)
,(2, 2)
;
WITH CTE
AS (
SELECT docid
,value
,ROW_NUMBER() OVER (
PARTITION BY docid ORDER BY docid
) rownum
FROM #t
)
,CTE1
AS (
SELECT docid
,value
,rownum
,1.00 wtg
FROM cte
WHERE rownum = 1
UNION ALL
SELECT a.docid
,b.value
,b.rownum + 1
,CASE
WHEN a.Value = b.value
THEN 0.50
ELSE 1
END
FROM CTE A
INNER JOIN cte1 B ON a.docid = b.DocID
AND A.rownum > b.rownum
)
SELECT docid
,SUM(wtg) wtg
FROM cte1
GROUP BY docid

SQL group by if values are close

Class| Value
-------------
A | 1
A | 2
A | 3
A | 10
B | 1
I am not sure whether it is practical to achieve this using SQL.
If the difference of values are less than 5 (or x), then group the rows (of course with the same Class)
Expected result
Class| ValueMin | ValueMax
---------------------------
A | 1 | 3
A | 10 | 10
B | 1 | 1
For fixed intervals, we can easily use "GROUP BY". But now the grouping is based on nearby row's value. So if the values are consecutive or very close, they will be "chained together".
Thank you very much
Assuming MSSQL
You are trying to group things by gaps between values. The easiest way to do this is to use the lag() function to find the gaps:
select class, min(value) as minvalue, max(value) as maxvalue
from (select class, value,
sum(IsNewGroup) over (partition by class order by value) as GroupId
from (select class, value,
(case when lag(value) over (partition by class order by value) > value - 5
then 0 else 1
end) as IsNewGroup
from t
) t
) t
group by class, groupid;
Note that this assumes SQL Server 2012 for the use of lag() and cumulative sum.
Update:
*This answer is incorrect*
Assuming the table you gave is called sd_test, the following query will give you the output you are expecting
In short, we need a way to find what was the value on the previous row. This is determined using a join on row ids. Then create a group to see if the difference is less than 5. and then it is just regular 'Group By'.
If your version of SQL Server supports windowing functions with partitioning the code would be much more readable.
SELECT
A.CLASS
,MIN(A.VALUE) AS MIN_VALUE
,MAX(A.VALUE) AS MAX_VALUE
FROM
(SELECT
ROW_NUMBER()OVER(PARTITION BY CLASS ORDER BY VALUE) AS ROW_ID
,CLASS
,VALUE
FROM SD_TEST) AS A
LEFT JOIN
(SELECT
ROW_NUMBER()OVER(PARTITION BY CLASS ORDER BY VALUE) AS ROW_ID
,CLASS
,VALUE
FROM SD_TEST) AS B
ON A.CLASS = B.CLASS AND A.ROW_ID=B.ROW_ID+1
GROUP BY A.CLASS,CASE WHEN ABS(COALESCE(B.VALUE,0)-A.VALUE)<5 THEN 1 ELSE 0 END
ORDER BY A.CLASS,cASE WHEN ABS(COALESCE(B.VALUE,0)-A.VALUE)<5 THEN 1 ELSE 0 END DESC
ps: I think the above is ANSI compliant. So should run in most SQL variants. Someone can correct me if it is not.
These give the correct result, using the fact that you must have the same number of group starts as ends and that they will both be in ascending order.
if object_id('tempdb..#temp') is not null drop table #temp
create table #temp (class char(1),Value int);
insert into #temp values ('A',1);
insert into #temp values ('A',2);
insert into #temp values ('A',3);
insert into #temp values ('A',10);
insert into #temp values ('A',13);
insert into #temp values ('A',14);
insert into #temp values ('b',7);
insert into #temp values ('b',8);
insert into #temp values ('b',9);
insert into #temp values ('b',12);
insert into #temp values ('b',22);
insert into #temp values ('b',26);
insert into #temp values ('b',67);
Method 1 Using CTE and row offsets
with cte as
(select distinct class,value,ROW_NUMBER() over ( partition by class order by value ) as R from #temp),
cte2 as
(
select
c1.class
,c1.value
,c2.R as PreviousRec
,c3.r as NextRec
from
cte c1
left join cte c2 on (c1.class = c2.class and c1.R= c2.R+1 and c1.Value < c2.value + 5)
left join cte c3 on (c1.class = c3.class and c1.R= c3.R-1 and c1.Value > c3.value - 5)
)
select
Starts.Class
,Starts.Value as StartValue
,Ends.Value as EndValue
from
(
select
class
,value
,row_number() over ( partition by class order by value ) as GroupNumber
from cte2
where PreviousRec is null) as Starts join
(
select
class
,value
,row_number() over ( partition by class order by value ) as GroupNumber
from cte2
where NextRec is null) as Ends on starts.class=ends.class and starts.GroupNumber = ends.GroupNumber
** Method 2 Inline views using not exists **
select
Starts.Class
,Starts.Value as StartValue
,Ends.Value as EndValue
from
(
select class,Value ,row_number() over ( partition by class order by value ) as GroupNumber
from
(select distinct class,value from #temp) as T
where not exists (select 1 from #temp where class=t.class and Value < t.Value and Value > t.Value -5 )
) Starts join
(
select class,Value ,row_number() over ( partition by class order by value ) as GroupNumber
from
(select distinct class,value from #temp) as T
where not exists (select 1 from #temp where class=t.class and Value > t.Value and Value < t.Value +5 )
) ends on starts.class=ends.class and starts.GroupNumber = ends.GroupNumber
In both methods I use a select distinct to begin because if you have a dulpicate entry at a group start or end things go awry without it.
Here is one way of getting the information you are after:
SELECT Under5.Class,
(
SELECT MIN(m2.Value)
FROM MyTable AS m2
WHERE m2.Value < 5
AND m2.Class = Under5.Class
) AS ValueMin,
(
SELECT MAX(m3.Value)
FROM MyTable AS m3
WHERE m3.Value < 5
AND m3.Class = Under5.Class
) AS ValueMax
FROM
(
SELECT DISTINCT m1.Class
FROM MyTable AS m1
WHERE m1.Value < 5
) AS Under5
UNION
SELECT Over4.Class,
(
SELECT MIN(m4.Value)
FROM MyTable AS m4
WHERE m4.Value >= 5
AND m4.Class = Over4.Class
) AS ValueMin,
(
SELECT Max(m5.Value)
FROM MyTable AS m5
WHERE m5.Value >= 5
AND m5.Class = Over4.Class
) AS ValueMax
FROM
(
SELECT DISTINCT m6.Class
FROM MyTable AS m6
WHERE m6.Value >= 5
) AS Over4

Duplicate Counts - TSQL

I want to get All records that has duplicate values for SOME of the fields (i.e. Key columns).
My code:
CREATE TABLE #TEMP (ID int, Descp varchar(5), Extra varchar(6))
INSERT INTO #Temp
SELECT 1,'One','Extra1'
UNION ALL
SELECT 2,'Two','Extra2'
UNION ALL
SELECT 3,'Three','Extra3'
UNION ALL
SELECT 1,'One','Extra4'
SELECT ID, Descp, Extra FROM #TEMP
;WITH Temp_CTE AS
(SELECT *
, ROW_NUMBER() OVER (PARTITION BY ID, Descp ORDER BY (SELECT 0))
AS DuplicateRowNumber
FROM #TEMP
)
SELECT * FROM Temp_cte
DROP TABLE #TEMP
The last column tells me how many times each row has appeared based on ID and Descp values.
I want that row but I ALSO need another column* that indicates both rows for ID = 1 and Descp = 'One' has showed up more than once.
So an extra column* (i.e. MultipleOccurances (bool)) which has 1 for two rows with ID = 1 and Descp = 'One' and 0 for other rows as they are only showing up once.
How can I achieve that? (I want to avoid using Count(1)>1 or something if possible.
Edit:
Desired output:
ID Descp Extra DuplicateRowNumber IsMultiple
1 One Extra1 1 1
1 One Extra4 2 1
2 Two Extra2 1 0
3 Three Extra3 1 0
SQL Fiddle
You say "I want to avoid using Count" but it is probably the best way. It uses the partitioning you already have on the row_number
SELECT *,
ROW_NUMBER() OVER (PARTITION BY ID, Descp
ORDER BY (SELECT 0)) AS DuplicateRowNumber,
CASE
WHEN COUNT(*) OVER (PARTITION BY ID, Descp) > 1 THEN 1
ELSE 0
END AS IsMultiple
FROM #Temp
And the execution plan just shows a single sort
Well, I have this solution, but using a Count...
SELECT T1.*,
ROW_NUMBER() OVER (PARTITION BY T1.ID, T1.Descp ORDER BY (SELECT 0)) AS DuplicateRowNumber,
CASE WHEN T2.C = 1 THEN 0 ELSE 1 END MultipleOcurrences FROM #temp T1
INNER JOIN
(SELECT ID, Descp, COUNT(1) C FROM #TEMP GROUP BY ID, Descp) T2
ON T1.ID = T2.ID AND T1.Descp = T2.Descp

SQL Row_Number() function in Where Clause

I found one question answered with the Row_Number() function in the where clause. When I tried one query, I was getting the following error:
"Msg 4108, Level 15, State 1, Line 1
Windowed functions can only appear in the SELECT or ORDER BY clauses."
Here is the query I tried. If somebody knows how to solve this, please let me know.
SELECT employee_id
FROM V_EMPLOYEE
WHERE row_number() OVER ( ORDER BY employee_id ) > 0
ORDER BY Employee_ID
To get around this issue, wrap your select statement in a CTE, and then you can query against the CTE and use the windowed function's results in the where clause.
WITH MyCte AS
(
select employee_id,
RowNum = row_number() OVER ( order by employee_id )
from V_EMPLOYEE
ORDER BY Employee_ID
)
SELECT employee_id
FROM MyCte
WHERE RowNum > 0
SELECT employee_id
FROM (
SELECT employee_id, ROW_NUMBER() OVER (ORDER BY employee_id) AS rn
FROM V_EMPLOYEE
) q
WHERE rn > 0
ORDER BY
Employee_ID
Note that this filter is redundant: ROW_NUMBER() starts from 1 and is always greater than 0.
Select * from
(
Select ROW_NUMBER() OVER ( order by Id) as 'Row_Number', *
from tbl_Contact_Us
) as tbl
Where tbl.Row_Number = 5
I think you want something like this:
SELECT employee_id
FROM (SELECT employee_id, row_number()
OVER (order by employee_id) AS 'rownumber'
FROM V_EMPLOYEE) TableExpressionsMustHaveAnAliasForDumbReasons
WHERE rownumber > 0
In response to comments on rexem's answer, with respect to whether a an inline view or CTE would be faster I recast the queries to use a table I, and everyone, had available: sys.objects.
WITH object_rows AS (
SELECT object_id,
ROW_NUMBER() OVER ( ORDER BY object_id) RN
FROM sys.objects)
SELECT object_id
FROM object_rows
WHERE RN > 1
SELECT object_id
FROM (SELECT object_id,
ROW_NUMBER() OVER ( ORDER BY object_id) RN
FROM sys.objects) T
WHERE RN > 1
The query plans produced were exactly the same. I would expect in all cases, the query optimizer would come up with the same plan, at least in simple replacement of CTE with inline view or vice versa.
Of course, try your own queries on your own system to see if there is a difference.
Also, row_number() in the where clause is a common error in answers given on Stack Overflow. Logicaly row_number() is not available until the select clause is processed. People forget that and when they answer without testing the answer, the answer is sometimes wrong. (A charge I have myself been guilty of.)
I feel like all the answers showing use of a CTE or Sub Query are sufficient fixes for this, but I don't see anyone getting to the heart of why OP has a problem. The reason why what OP suggested doesn't work is due to logical query processing order here:
FROM
ON
JOIN
WHERE
GROUP BY
WITH CUBE/ROLLUP
HAVING
SELECT
DISTINCT
ORDER BY
TOP
OFFSET/FETCH
I believe this contributes to the answer greatly, because it explains why issues like this one occur. WHERE is always processed before SELECT making a CTE or Sub Query necessary for many functions. You will see this a lot in SQL Server.
WITH MyCte AS
(
select
employee_id,
RowNum = row_number() OVER (order by employee_id)
from V_EMPLOYEE
)
SELECT employee_id
FROM MyCte
WHERE RowNum > 0
ORDER BY employee_id
Using CTE (SQL Server 2005+):
WITH employee_rows AS (
SELECT t.employee_id,
ROW_NUMBER() OVER ( ORDER BY t.employee_id ) 'rownum'
FROM V_EMPLOYEE t)
SELECT er.employee_id
FROM employee_rows er
WHERE er.rownum > 1
Using Inline view/Non-CTE Equivalent Alternative:
SELECT er.employee_id
FROM (SELECT t.employee_id,
ROW_NUMBER() OVER ( ORDER BY t.employee_id ) 'rownum'
FROM V_EMPLOYEE t) er
WHERE er.rownum > 1
based on OP's answer to question:
Please see this link. Its having a
different solution, which looks
working for the person who asked the
question. I'm trying to figure out a
solution like this.
Paginated query using sorting on different columns using ROW_NUMBER() OVER () in SQL Server 2005
~Joseph
"method 1" is like the OP's query from the linked question, and "method 2" is like the query from the selected answer. You had to look at the code linked in this answer to see what was really going on, since the code in the selected answer was modified to make it work. Try this:
DECLARE #YourTable table (RowID int not null primary key identity, Value1 int, Value2 int, value3 int)
SET NOCOUNT ON
INSERT INTO #YourTable VALUES (1,1,1)
INSERT INTO #YourTable VALUES (1,1,2)
INSERT INTO #YourTable VALUES (1,1,3)
INSERT INTO #YourTable VALUES (1,2,1)
INSERT INTO #YourTable VALUES (1,2,2)
INSERT INTO #YourTable VALUES (1,2,3)
INSERT INTO #YourTable VALUES (1,3,1)
INSERT INTO #YourTable VALUES (1,3,2)
INSERT INTO #YourTable VALUES (1,3,3)
INSERT INTO #YourTable VALUES (2,1,1)
INSERT INTO #YourTable VALUES (2,1,2)
INSERT INTO #YourTable VALUES (2,1,3)
INSERT INTO #YourTable VALUES (2,2,1)
INSERT INTO #YourTable VALUES (2,2,2)
INSERT INTO #YourTable VALUES (2,2,3)
INSERT INTO #YourTable VALUES (2,3,1)
INSERT INTO #YourTable VALUES (2,3,2)
INSERT INTO #YourTable VALUES (2,3,3)
INSERT INTO #YourTable VALUES (3,1,1)
INSERT INTO #YourTable VALUES (3,1,2)
INSERT INTO #YourTable VALUES (3,1,3)
INSERT INTO #YourTable VALUES (3,2,1)
INSERT INTO #YourTable VALUES (3,2,2)
INSERT INTO #YourTable VALUES (3,2,3)
INSERT INTO #YourTable VALUES (3,3,1)
INSERT INTO #YourTable VALUES (3,3,2)
INSERT INTO #YourTable VALUES (3,3,3)
SET NOCOUNT OFF
DECLARE #PageNumber int
DECLARE #PageSize int
DECLARE #SortBy int
SET #PageNumber=3
SET #PageSize=5
SET #SortBy=1
--SELECT * FROM #YourTable
--Method 1
;WITH PaginatedYourTable AS (
SELECT
RowID,Value1,Value2,Value3
,CASE #SortBy
WHEN 1 THEN ROW_NUMBER() OVER (ORDER BY Value1 ASC)
WHEN 2 THEN ROW_NUMBER() OVER (ORDER BY Value2 ASC)
WHEN 3 THEN ROW_NUMBER() OVER (ORDER BY Value3 ASC)
WHEN -1 THEN ROW_NUMBER() OVER (ORDER BY Value1 DESC)
WHEN -2 THEN ROW_NUMBER() OVER (ORDER BY Value2 DESC)
WHEN -3 THEN ROW_NUMBER() OVER (ORDER BY Value3 DESC)
END AS RowNumber
FROM #YourTable
--WHERE
)
SELECT
RowID,Value1,Value2,Value3,RowNumber
,#PageNumber AS PageNumber, #PageSize AS PageSize, #SortBy AS SortBy
FROM PaginatedYourTable
WHERE RowNumber>=(#PageNumber-1)*#PageSize AND RowNumber<=(#PageNumber*#PageSize)-1
ORDER BY RowNumber
--------------------------------------------
--Method 2
;WITH PaginatedYourTable AS (
SELECT
RowID,Value1,Value2,Value3
,ROW_NUMBER() OVER
(
ORDER BY
CASE #SortBy
WHEN 1 THEN Value1
WHEN 2 THEN Value2
WHEN 3 THEN Value3
END ASC
,CASE #SortBy
WHEN -1 THEN Value1
WHEN -2 THEN Value2
WHEN -3 THEN Value3
END DESC
) RowNumber
FROM #YourTable
--WHERE more conditions here
)
SELECT
RowID,Value1,Value2,Value3,RowNumber
,#PageNumber AS PageNumber, #PageSize AS PageSize, #SortBy AS SortBy
FROM PaginatedYourTable
WHERE
RowNumber>=(#PageNumber-1)*#PageSize AND RowNumber<=(#PageNumber*#PageSize)-1
--AND more conditions here
ORDER BY
CASE #SortBy
WHEN 1 THEN Value1
WHEN 2 THEN Value2
WHEN 3 THEN Value3
END ASC
,CASE #SortBy
WHEN -1 THEN Value1
WHEN -2 THEN Value2
WHEN -3 THEN Value3
END DESC
OUTPUT:
RowID Value1 Value2 Value3 RowNumber PageNumber PageSize SortBy
------ ------ ------ ------ ---------- ----------- ----------- -----------
10 2 1 1 10 3 5 1
11 2 1 2 11 3 5 1
12 2 1 3 12 3 5 1
13 2 2 1 13 3 5 1
14 2 2 2 14 3 5 1
(5 row(s) affected
RowID Value1 Value2 Value3 RowNumber PageNumber PageSize SortBy
------ ------ ------ ------ ---------- ----------- ----------- -----------
10 2 1 1 10 3 5 1
11 2 1 2 11 3 5 1
12 2 1 3 12 3 5 1
13 2 2 1 13 3 5 1
14 2 2 2 14 3 5 1
(5 row(s) affected)
select salary from (
select Salary, ROW_NUMBER() over (order by Salary desc) rn from Employee
) t where t.rn = 2