How can I update a column with row_number in SQL Server? - sql

How can I udpate a column with row_number in SQL Server 2008 R2?
BEGIN TRANSACTION
DECLARE #count int
DECLARE #maxcount int
SET #count = 1
SET #maxcount = (SELECT count(*)
FROM Applicant_Detail ad
WHERE ad.identification_code = 1)
PRINT #maxcount
WHILE (#count<#maxcount)
BEGIN
UPDATE ad
SET ad.NRIC_nbr = s.myRowNumber
FROM Applicant_Detail ad
INNER JOIN (
SELECT ROW_NUMBER() OVER (ORDER BY NRIC_nbr ASC) AS myRowNumber
FROM Applicant_Detail ad
)S
ON s.myRowNumber = #count
SET #count = #count+1
END
This query takes a lot of time. I do not have any column in the applicant_detail table which has sequential data? I use the count logic but takes lot of time?
What i want?
Update the column of the table with sequential data like 1,2,3,4,5,6,7,8,9...... max row of the table?

Try this:
declare #count int = (select count(1) from Applicant_Detail)
;with cte as
(select *, row_number() over (order by #count) rn
from Applicant_Detail)
update cte
set NRIC_nbr = rn
select * from Applicant_Detail
Demo

Solution for this problem
BEGIN TRANSACTION
;WITH numbering AS
( SELECT AD.NRIC_nbr,
AD.application_number,
ROW_NUMBER() OVER(ORDER BY AD.application_number) AS ROWNUMBER
FROM Applicant_Detail ad WHERE AD.identification_code=1
)
UPDATE numbering
SET NRIC_nbr=ROWNUMBER

when you want to update the column with row_numebr. it is the perfect solution
WITH TEMP AS
(
SELECT id as rid,
ROW_NUMBER() OVER (ORDER BY [ID] ASC) AS RN
FROM ORG
)
update ORG Set columnname1 ='200'+(select RN FROM TEMP where TEMP.rid=ORG.id)

Related

Selecting data from table where sum of values in a column equal to the value in another column

Sample data:
create table #temp (id int, qty int, checkvalue int)
insert into #temp values (1,1,3)
insert into #temp values (2,2,3)
insert into #temp values (3,1,3)
insert into #temp values (4,1,3)
According to data above, I would like to show exact number of lines from top to bottom where sum(qty) = checkvalue. Note that checkvalue is same for all the records all the time. Regarding the sample data above, the desired output is:
Id Qty checkValue
1 1 3
2 2 3
Because 1+2=3 and no more data is needed to show. If checkvalue was 4, we would show the third record: Id:3 Qty:1 checkValue:4 as well.
This is the code I am handling this problem. The code is working very well.
declare #checkValue int = (select top 1 checkvalue from #temp);
declare #counter int = 0, #sumValue int = 0;
while #sumValue < #checkValue
begin
set #counter = #counter + 1;
set #sumValue = #sumValue + (
select t.qty from
(
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY id ASC) AS rownumber,
id,qty,checkvalue
FROM #temp
) AS foo
WHERE rownumber = #counter
) t
)
end
declare #sql nvarchar(255) = 'select top '+cast(#counter as varchar(5))+' * from #temp'
EXECUTE sp_executesql #sql, N'#counter int', #counter = #counter;
However, I am not sure if this is the best way to deal with it and wonder if there is a better approach. There are many professionals here and I'd like to hear from them about what they think about my approach and how we can improve it. Any advice would be appreciated!
Try this:
select id, qty, checkvalue from (
select t1.*,
sum(t1.qty) over (partition by t2.id) [sum]
from #temp [t1] join #temp [t2] on t1.id <= t2.id
) a where checkvalue = [sum]
Smart self-join is all you need :)
For SQL Server 2012, and onwards, you can easily achieve this using ROWS BETWEEN in your OVER clause and the use of a CTE:
WITH Running AS(
SELECT *,
SUM(qty) OVER (ORDER BY id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS RunningQty
FROM #temp t)
SELECT id, qty, checkvalue
FROM Running
WHERE RunningQty <= checkvalue;
One basic improvement is to try & reduce the no. of iterations. You're incrementing by 1, but if you repurpose the logic behind binary searching, you'd get something close to this:
DECLARE #RoughAverage int = 1 -- Some arbitrary value. The closer it is to the real average, the faster things should be.
DECLARE #CheckValue int = (SELECT TOP 1 checkvalue FROM #temp)
DECLARE #Sum int = 0
WHILE 1 = 1 -- Refer to BREAK below.
BEGIN
SELECT TOP (#RoughAverage) #Sum = SUM(qty) OVER(ORDER BY id)
FROM #temp
ORDER BY id
IF #Sum = #CheckValue
BREAK -- Indicating you reached your objective.
ELSE
SET #RoughAverage = #CheckValue - #Sum -- Most likely incomplete like this.
END
For SQL 2008 you can use recursive cte. Top 1 with ties limits result with first combination. Remove it to see all combinations
with cte as (
select
*, rn = row_number() over (order by id)
from
#temp
)
, rcte as (
select
i = id, id, qty, sumV = qty, checkvalue, rn
from
cte
union all
select
a.id, b.id, b.qty, a.sumV + b.qty, a.checkvalue, b.rn
from
rcte a
join cte b on a.rn + 1 = b.rn
where
a.sumV < b.checkvalue
)
select
top 1 with ties id, qty, checkvalue
from (
select
*, needed = max(case when sumV = checkvalue then 1 else 0 end) over (partition by i)
from
rcte
) t
where
needed = 1
order by dense_rank() over (order by i)

SQL Procedure to get row number to determine 'Rank'

I have a table which contains a column called 'Score'. I want to write a procedure which loops through all rows in this table, and determines where out of all rows, each specific row ranks i.e biggest score is rank 1, smallest score = n.
This is my poor attempt so far:
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #Rank INT = 0;
DECLARE #UserID UNIQUEIDENTIFIER;
DECLARE cur CURSOR FOR SELECT UserID FROM tblMember
OPEN cur
FETCH NEXT FROM cur INTO #UserID
--loop through all users
WHILE ##FETCH_STATUS = 0 BEGIN
SELECT #Rank = ROW_NUMBER() OVER(ORDER BY Score DESC) FROM tblDetails WHERE UserID = #UserID;
PRINT #Rank
Print ' For '
Print #UserID;
UPDATE tblDetails SET Rank = #Rank WHERE UserID = #UserID;
FETCH NEXT FROM cur INTO #UserID
END
END
Unfortauntely this effort ranks each entry as 1 - probably becuase the query has the WHERE clause refining the result set. But I cannot determine what the correct query should be!
WITH q AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY userId ORDER BY score DESC) rn
FROM tblDetail
)
UPDATE q
SET rank = rn
I believe the correct query you want is:
with toupdate as (
select d.*,
row_number() over (order by score desc) as seqnum
from tblDetails
)
update toupdate
set [rank] = seqnum;
I used row_number() because your example does. You might want rank() or dense_rank() if you want scores with the same value to have the same rank:
with toupdate as (
select d.*,
rank() over (order by score desc) as seqnum
from tblDetails
)
update toupdate
set [rank] = seqnum;
The important difference is that there is no partition by clause in the query.
may be basing on the userid you are trying to update or increment the rank column..So as per my understanding what ever the Userid = 1 you want to update Rank column
declare #t INT;
SELECT #t = MAX(Userid) From tblDetails
;
with cte(w) as
(select 1
UNION ALL
Select Userid + 1
From
tblDetails where Userid < #t)
UPdate tblDetails
set [Rank]=cte.w
FROM cte
INNER JOIN tblDetails S
ON S.Userid = cte.w

sql server 2008, cannot use order by in subquery

here's my sql server 2008 stored procedure.
ALTER PROCEDURE [dbo].[GetSharedSmoothies]
#Page INT ,
#Status INT ,
#ItemPerPage INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #X INT
DECLARE #Y INT
SET #X = ( #Page - 1 ) * #ItemPerPage
SET #Y = #Page * #ItemPerPage
SELECT *
FROM ( SELECT S.* ,
U.Avatar ,
U.Displayname ,
( SELECT COUNT(Id)
FROM Vote
WHERE Vote.SmoothieId = S.Id
) AS Votes ,
ROW_NUMBER() OVER ( ORDER BY S.Id ) rownum
FROM dbo.Smoothie AS S
INNER JOIN dbo.[User] AS U ON S.UserId = U.Id
WHERE S.IsPublic = 1
AND S.Status = 3
AND S.UserId > 0
-- ORDER BY S.CreatedDate DESC
) seq
WHERE seq.rownum BETWEEN #X AND #Y
ORDER BY seq.rownum
END
in my code, you will see I comment out the order by
-- ORDER BY S.CreatedDate DESC
because order by will not work in subquery. i need to show the lastest one on the top. is there a way I can use order by in my code?
You may add S.CreatedDate within the Row_NUMBER()
ROW_NUMBER() OVER (PARTITION BY S.Id ORDER BY S.CreatedDate DESC) AS RowNum
That's right. It is not allowed, because it will do nothing.
Having the latest one at the top in the subquery will do nothing to the result set using the subquery.
Add the needed column to the result set ORDER BY:
ORDER BY seq.CreatedDate DESC, seq.rownum
Or:
ORDER BY seq.rownum, seq.CreatedDate DESC

fill empty column with sequence

Is there any way to fill an empty column with a ascending sequence of numbers starting from 1, without using identity?
I tried the following cursor but it is filling the same value (450) for all rows in the column:
declare cur3 cursor for
select new_id from sheet1$
declare #no int
declare #no1 int
set #no1 = 1
open cur3
fetch next from cur3 into #no
while(##FETCH_STATUS = 0)
begin
update sheet1$ set new_id = #no1
set #no1 = #no1 + 1
fetch next from cur3 into #no
end
close cur3
deallocate cur3
The problem is that you are missing a WHERE clause from your update query, so each loop is updating all rows. It should be:
UPDATE sheet1$
SET new_id = #no1
WHERE New_ID = #No -- ONLY UPDATE 1 ROW
Just as an aside, and assuming your SQL-Server tag is correct, you could do this without a cursor using the ROW_NUMBER() Function
WITH CTE AS
( SELECT New_ID, [RN] = ROW_NUMBER() OVER(ORDER BY New_ID)
FROM yourTable
)
UPDATE CTE
SET New_ID = RN
EDIT - EXPLANATION
ROW_NUMBER simply provides a sequence of numbers, the Common Table Expression is like a dynamic view:
If you run:
WITH CTE AS
( SELECT New_ID, [RN] = ROW_NUMBER() OVER(ORDER BY New_ID)
FROM yourTable
)
SELECT *
FROM CTE
you should get a better idea of what is being done within the CTE, then the beauty of CTEs is that you can UPDATE them directly without having to reference back to the original table, so Updating the CTE is equivalent to:
UPDATE yourTable
SET New_ID = RN
FROM yourTable
INNER JOIN
( SELECT New_ID, [RN] = ROW_NUMBER() OVER(ORDER BY New_ID)
FROM yourTable
) n
ON n.New_ID = yourTable.New_ID;
Yep, ROW_NUMBER.
Use like the following :
select ROW_NUMBER()OVER(ORDER BY Somefield) As Id
,*
from SomeTable
You can read all about it : http://msdn.microsoft.com/en-us/library/ms186734(v=sql.105).aspx

SQL Server 2008 - Function error

I am new to sql programming; trying to develop this function to get rows of clients who have a specific number of visits from a view:
ALTER FUNCTION [dbo].[fn_NumberOfVisit]
(
#nv int
)
RETURNS varchar(500)
AS
BEGIN
DECLARE #ret varchar(500)
select *
from (
select
*,
rn = row_number() over (partition by ClientId order by VisitId)
from
Visit
) activityWithRn
inner join vw_MasterView on vw_MasterView.VisitId = activityWithRn.VisitId
where activityWithRn.rn =#nv
RETURN #ret
END
I get the following error:
Select statements included within a function cannot return data to a client
I would appreciate your support. Thanks in advance.
Your problem is here:
set #Count = ( select *
from (
select
*,
#Count is expecting a number - you're giving it a bunch of rows, try:
set #Count = ( select Count(*)
from (
select
The error is telling you that your subquery is returning too many rows. You need to return only one row is you are assigning the result to a variable.
Change
set #Count = ( select *
from (
select
*,
rn = row_number() over (partition by ClientId order by VisitId)
from
Visit
To
set #Count = ( select count(*)
from (
select
*,
rn = row_number() over (partition by ClientId order by VisitId)
from
Visit