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
Related
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
I have a list like this example:
abc, efg, rty
and a table with following data:
1 abcd
2 efgh
3 abcd
4 rtyu
5 efgh
now I want to find the first-row which start with list item in the table. my expected result is:
1 abcd
2 efgh
4 rtyu
This is a complete script to do the job
Declare #v_List Table
(
Text nvarchar(100)
)
Declare #v_Data Table
(
Number int,
Text nvarchar(100)
)
Insert Into #v_List values(N'abc')
Insert Into #v_List values(N'efg')
Insert Into #v_List values(N'rty')
Insert Into #v_Data values(1, N'abcd')
Insert Into #v_Data values(2, N'efgh')
Insert Into #v_Data values(3, N'abcd')
Insert Into #v_Data values(4, N'rtyu')
Insert Into #v_Data values(5, N'efgh')
;with CTE as
(
Select D.Number,
D.Text,
ROW_NUMBER() OVER (PARTITION BY L.Text Order By D.Number) as Row_No
From #v_Data D
Join #v_List L
On D.Text like L.Text + '%'
)
Select CTE.Number,
CTE.Text
From CTE
Where CTE.Row_No = 1
select * from TableName
where Id in
(
select min(Id) from
(
select Id,
case
when Val like 'abc%' then 1
when Val like 'efg%' then 2
when Val like 'rty%' then 3
else 0 end temp
from TableName
)t where temp > 0
group by temp
)
You can use a windowed ROW_NUMBER to generate a sequential number by each different value, then just display the first one only.
;WITH RowNumbersByValue AS
(
SELECT
T.ID,
T.Value,
RowNumber = ROW_NUMBER() OVER (PARTITION BY T.Value ORDER BY T.ID)
FROM
YourTable AS T
)
SELECT
R.ID,
R.Value
FROM
RowNumbersByValue AS R
WHERE
R.Value IN ('abcd', 'efgh', 'rtyu') AND
R.RowNumber = 1
For SQL Server I prefer this version, which does not require a subquery:
SELECT TOP 1 WITH TIES ID, Value
FROM yourTable
WHERE Value LIKE 'abc%' OR Value LIKE 'efg%' OR Value LIKE 'rty%'
ORDER BY ROW_NUMBER() OVER (PARTITION BY Value ORDER BY ID);
SELECT * INTO #temp FROM (VALUES
(1 ,'abcd'),
(2 ,'efgh'),
(3 ,'abcd'),
(4 ,'rtyu'),
(5 ,'efgh'))a([id], [name])
You can use min and group by function
SELECT MIN(id), name FROM #temp GROUP BY name
You may use this, there are so many ways to achieve this, use whichever suits you better.
using subquery
select id, col from
(select Row_number() over (partition by col order by id) as slno, id, col from yourtable)
as tb where tb.slno=1
using cte
; with cte as (
select row_number() over (partition by col order by id) as Slno, id, col from table)
select id, col from cte where slno=1
using min
select Min(id) , col from table group by col
Note:-
In the end of any above mentioned query you may apply your where clause to filter your records as needed.
This question already has answers here:
How to find third or nᵗʰ maximum salary from salary table?
(56 answers)
Closed 7 years ago.
I have following table with some data to find second largest number.
Table:
ColA
---------
3
23
43
673
173
373
273
Use this:
select * from
(select colA , row_number() over(order by colA desc) as rn from Table) T
where T.rn=2
Thanks to Martin's / dnoeth's comment :
It can be problematic with 2 max inetegers
Use this :
select * from
(select colA , DENSE_RANK( ) OVER (ORDER BY colA ) as rn from Table) T
where T.rn=2
Additional info : (once and for all ( learn it! , I always use it)) :
DECLARE #t TABLE(NAME NVARCHAR(MAX),val money)
insert INTO #t SELECT 'a',100
insert INTO #t SELECT 'a',100
insert INTO #t SELECT 'a',100
insert INTO #t SELECT 'a',100
insert INTO #t SELECT 'b',200
insert INTO #t SELECT 'b',200
insert INTO #t SELECT 'd',400
insert INTO #t SELECT 'e',500
insert INTO #t SELECT 'f',600
select Name,
val,
ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY name),
val/ SUM(val) OVER(PARTITION BY NAME ) AS '1AgainstTotalHimself',
val/ SUM(val) OVER( ) AS '1AgainstOthers' ,
NTILE(2) OVER ( PARTITION BY NAME ORDER BY name) AS 'ntile2' ,
NTILE(2) OVER ( ORDER BY name) AS 'ntile' , -- ( 9%2=1 , so group #1 will get more number)
RANK( ) OVER ( ORDER BY name ) AS Rank,
DENSE_RANK( ) OVER (ORDER BY name) AS DENSERANK
from #t
Result :
One way to solve this would be
with cte as (
select ColA,
Row_number() over(order by ColA DESC) As rn
FROM Table
)
SELECT ColA
FROM cte
WHERE rn = 2
Update
After Martin's correct comment, here is a better answer:
Select top 1 ColA
From YourTable
WHERE ColA < (
SELECT MAX(ColA)
FROM YourTable
)
ORDER BY ColA DESC
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
I have data in a table in following format
ID DocNumber RevOrder
1 DOC-001 NULL
2 DOC-001 NULL
3 DOC-001 NULL
4 DOC-002 NULL
5 D0C-002 NULL
6 D0C-003 NULL
I need to update the RevOrder column in to following format
ID DocNumber RevOrder
1 DOC-001 3
2 DOC-001 2
3 DOC-001 1
4 DOC-002 2
5 D0C-002 1
6 D0C-003 1
Logic is: DocNumber can be duplicated and the DocNumber with the max ID gets the RevOrder = 1, next get the RevOrder = 2 etc... how can I achieve the above scenario?
Use this UPDATE statement based on a ROW_NUMBER() over a partition:
;WITH UpdateData AS
(
SELECT
ID, DocNumber,
ROW_NUMBER() OVER(PARTITION BY DocNumber ORDER BY ID DESC) AS 'RowNum'
FROM dbo.YourTable
)
UPDATE dbo.YourTable
SET RevOrder = RowNum
FROM dbo.YourTable t
INNER JOIN UpdateData ud ON t.ID = ud.ID
from SQL Server version 2005 and above, you can use
RANK() OVER ( PARTITION BY DocNumber ORDER BY ID ASC) AS RevOrder
I think a row_number() function with partition by clause will work
declare #temp as table(
ID int not null
,Doc_Num varchar(20) not null
,RevOrder int null
)
insert into #temp
values
(1,'DOC-001',null)
,(2,'DOC-001',null)
,(3,'DOC-001',null)
,(4,'DOC-002',null)
,(5,'DOC-002',null)
,(6,'DOC-003',null)
select * from #temp
select
t.ID
,t.Doc_Num
,ROW_NUMBER() over(partition by t.Doc_num order by t.ID desc)
from
#temp t
order by t.ID
Use ROW_NUMBER() & PARTITION BY
SELECT ID,
DocNumber,
ROW_NUMBER() OVER(PARTITION BY DocNumber ORDER BY DocNumber DESC) AS RevOrder
FROM #TestTable
You can view/test a working example here: https://data.stackexchange.com/stackoverflow/q/116435/