Update row based on previous row id - sql-server

I have an update query like so
UPDATE myTable
SET ParentID = X
I need X to be the ID of the previous row that is currently being updated.
Any ideas?

If by "previous" row you mean the row where the id is the biggest value less than the value in the current row, you can use the lag() function in SQL Server 2012 or a correlated subquery:
UPDATE myTable
SET ParentID = (select top 1 id
from mytable m2
where m2.id < myTable.id
order by id desc
)

Maybe the following script will be useful (Assuming that the first value not have previous value then will be NULL), you can try this HERE:
CREATE TABLE TEST(
ID INT);
INSERT INTO TEST VALUES(10);
INSERT INTO TEST VALUES(20);
INSERT INTO TEST VALUES(30);
INSERT INTO TEST VALUES(40);
INSERT INTO TEST VALUES(50);
INSERT INTO TEST VALUES(60);
/*HERE THE SCRIPT*/
WITH temp AS (
SELECT x.ID,
ROW_NUMBER() over (order by x.ID) AS n
FROM TEST x
)
UPDATE t
SET t.ID = (SELECT temp.ID FROM temp WHERE temp.n = t.n - 1)
FROM (
SELECT x.ID,
ROW_NUMBER() over (order by x.ID) AS n
FROM TEST x
) t
SELECT * FROM TEST
NOTE: maybe this can be solved in an easier way, but it was the first thing that occurred to me with what you have posted

Related

Update Table with Duplicates

My Table, #MetaData looks like this
Table_Name Element Join_prefix
Incident hold_reason h
Incident impact i
Incident incident_state i
Incident notify n
Incident severity s
Incident state s
Change impact i
Change incident_state i
I want to update the join_prefix where it is the same, to the first 2 characters of the element, within the Table_Name. So the table looks like
Table_Name Element Join_prefix
Incident hold_reason h
Incident impact im
Incident incident_state in
Incident notify n
Incident severity se
Incident state st
Change impact im
Change incident_state in
I've been using the following sql but it updates all the rows
update #MetaData
set join_prefix=substring(element,1,2)
where exists(
select [Table_Name],[Join_prefix]
from #MetaData
group by [Table_Name],[Join_prefix]
having count(join_prefix)>1)
One method would be to use an updatable CTE. Within the CTE you can use a windowed COUNT to count how many rows have the same prefix, and then update those rows:
SELECT *
INTO dbo.YourTable
FROM (VALUES('Incident','hold_reason ',CONVERT(varchar(4),'h')),
('Incident','impact ',CONVERT(varchar(4),'i')),
('Incident','incident_state',CONVERT(varchar(4),'i')),
('Incident','notify ',CONVERT(varchar(4),'n')),
('Incident','severity ',CONVERT(varchar(4),'s')),
('Incident','state ',CONVERT(varchar(4),'s')),
('Change ','impact ',CONVERT(varchar(4),'i')),
('Change ','incident_state',CONVERT(varchar(4),'i')))V(Table_Name,Element,Join_prefix)
GO
WITH CTE AS(
SELECT Element,
Join_prefix,
COUNT(Join_prefix) OVER (PARTITION BY Join_prefix) AS C
FROM dbo.YourTable)
UPDATE CTE
SET Join_prefix = LEFT(Element,2)
WHERE C > 1;
GO
SELECT *
FROM dbo.YourTable;
GO
DROP TABLE dbo.YourTable;
Your subquery is not correlated to the outside, so it always returns true. You need a WHERE
Note that exists doesn't need to select anything, you can select 1.
Also count(*) and count(non_null_value) is the same
update m1
set join_prefix = substring(element, 1, 2)
from #MetaData m1
where exists (select 1
from #MetaData m2
where m2.join_prefix = m1.join_prefix
group by m2.Table_Name, m2.Join_prefix
having count(*) > 1
);
A better method would be an updatable CTE
with CTE as (
select *,
cnt = count(*) over (partition by m.Table_Name, m.join_prefix)
from #MetaData m
)
update CTE
set join_prefix = substring(element,1,2)
from #MetaData m1
where t.cnt > 1;

Create a SELECT with defined number of rows

I have a stored procedure that should insert some random rows in a table depending on the amount values
#amount1 INT --EligibilityID = 1
#amount2 INT --EligibilityID = 2
#amount3 INT --EligibilityID = 3
Maybe the obvious way is to use TOP(#amount) but there are a lot of amount values and the second select is much larger. So, I was looking for a way to do it in a single statement if possible.
INSERT INTO [dbo].[CaseInfo]
SELECT ([EligibilityID],[CaseNumber],[CaseMonth])
FROM (
SELECT TOP(#amount1) [EligibilityID],[CaseNumber],[CaseMonth]
FROM [dbo].[tempCases]
WHERE [EligibilityID] = 1
)
INSERT INTO [dbo].[CaseInfo]
SELECT ([EligibilityID],[CaseNumber],[CaseMonth])
FROM (
SELECT TOP(#amount2) [EligibilityID],[CaseNumber],[CaseMonth]
FROM [dbo].[tempCases]
WHERE [EligibilityID] = 2
)
INSERT INTO [dbo].[CaseInfo]
SELECT ([EligibilityID],[CaseNumber],[CaseMonth])
FROM (
SELECT TOP(#amount3) [EligibilityID],[CaseNumber],[CaseMonth]
FROM [dbo].[tempCases]
WHERE [EligibilityID] = 3
)
I would recommend to use row_number, partitioned by eligibilityID, and then compare it with a case statement to select the correct variable each time:
INSERT INTO [dbo].[CaseInfo]
SELECT ([EligibilityID],[CaseNumber],[CaseMonth])
FROM (
SELECT [EligibilityID],[CaseNumber],[CaseMonth]
,row_number() over (partition by EligibilityID order by CaseNumber) as rn -- you haven't mentioned an ORDER BY, you can change it here
FROM [dbo].[tempCases]
) as table1
where rn<=case
when EligibilityID=1 then #amount1
when EligibilityID=2 then #amount2
when EligibilityID=3 then #amount3
end

Compare previous date and current date and update the table on basis on condition

I want to update my attendance table on basis of the following condition.
NonWorking type is 1
If its previous day or next attendance type is Absent then I want to mark NonWorking type is LWP in DAOthers Column.
I think you can use LAG() and LEAD() here to peek at the preceding and proceeding values of the attendance type. Then, if one of those should be absent, mark the NonWorking column appropriately.
WITH cte AS (
SELECT *,
LAG(AttendanceType, 1, 'Present') OVER (ORDER BY ADate) AS lag_at,
LEAD(AttendanceType, 1, 'Present') OVER (ORDER BY ADate) AS lead_at
FROM yourTable
)
UPDATE cte
SET NonWorking = 1
WHERE lag_at = 'Absent' OR lead_at = 'Absent'
I am not sure whether you want an sql query to update existing data or a solution which is needed while making an entry.
Use below query to update existing data:
Update AttendanceTable set DaOthers =
(select top 1 'LWP' from AttendanceTable at1
where AttendanceTable.EmployeeId = at1.EmployeeId
and DATEADD(day, -1,AttendanceTable.ADate) = at1.ADate
and at1.NonWorking = 1)
Table befor executing above query:
Table after executing above query:
To update at the time of inserting record:
If you want to update while inserting data then you may need to set a variable first then use that variable while inserting. In the first query you need to use ADate and EmployeeID.The Nonworking is always 1.
DECLARE #DaOthers nvarchar(20) = (select top 1 'LWP' from AttendanceTable at
where DATEADD(day, 1, at.ADate) ='2017-02-04' and at.NonWorking = 1 and EmployeeId = 1)
insert into AttendanceTable
(NonWorking, ADate, AttendanceType, EmployeeId, DaOthers)
values
(0,'2017-02-04', 'Present', 1,#DaOthers)
With CTE as
(
SELECT *,
DATEADD(DAY, 1, Lag(ADate, 1,ADate)
OVER (PARTITION BY DAttendanceId ORDER BY ADate ASC)) AS EndDate
FROM tbl_DailyAttendance where EmployeeId = 1001 and AMonth = 2 and AYear = 2017 and AttendanceType = 'Absent' and NonWorking = 0
)
--select * from CTE
select * from tbl_DailyAttendance tda inner join CTE c on tda.ADate = c.EndDate where tda.EmployeeID = 1001 and tda.NonWorking = 1 order by tda.ADate
This is how i do for checking the conditions
since you hvn't provided sample data,so try to understand my query and correct if anything minor.
;WITH CTE as
(
select *
,isnull((select 1 from tbl_DailyAttendance tdb
where ((tdb.adate=DATEADD(day,-1,tda.adate))
or (tdb.adate=DATEADD(day,1,tda.adate)))
and attendancetype='Absent'
),0) NewnonWorkingType
from tbl_DailyAttendance tda
)
--Testing purpose
--select * from cte
update tda
set nonworking=b.NewnonWorkingType
,daOther=case when b.NewnonWorkingType=1 then 'LWP'
else null end
from tbl_DailyAttendance tda
inner join cte b on tda.id=b.id

rSQL While Loop insert

*Updated - Please see below(Past the picture)
I am really stuck with this particular problem, I have two tables, Projects and Project Allocations, they are joined by the Project ID.
My goal is to populate a modified projects table's columns using the rows of the project allocations table. I've included an image below to illustrate what I'm trying to achieve.
A project can have up to 6 Project Allocations. Each Project Allocation has an Auto increment ID (Allocation ID) but I can't use this ID in a sub-selects because it isn't in a range of 1-6 so I can distinguish between who is the first PA2 and who is PA3.
Example:
(SELECT pa1.name FROM table where project.projectid = project_allocations.projectid and JVID = '1') as [PA1 Name],
(SELECT pa2.name FROM table where project.projectid = project_allocations.projectid and JVID = '1') as [PA2 Name],
The modified Projects table has columns for PA1, PA2, PA3. I need to populate these columns based on the project allocations table. So the first record in the database FOR EACH project will be PA1.
I've put together an SQL Agent job that drops and re-creates this table with the added columns so this is more about writing the project allocation row's into the modified projects table by row_num?
Any advice?
--Update
What I need to do now is to get the row_number added as a column for EACH project in order of DESC.
So the first row for each project ID will be 1 and for each row after that will be 2,3,4,5,6.
I've found the following code on this website:
use db_name
with cte as
(
select *
, new_row_id=ROW_NUMBER() OVER (ORDER BY eraprojectid desc)
from era_project_allocations_m
where era_project_allocations_m.eraprojectid = era_project_allocations_m.eraprojectid
)
update cte
set row_id = new_row_id
update cte
set row_id = new_row_id
I've added row_id as a column in the previous SQL Agent step and this code and it runs but it doesn't produce me a row_number FOR EACH projectid.
As you can see from the above image; I need to have 1-2 FOR Each project ID - effectively giving me thousands of 1s, 2s, 3s, 4s.
That way I can sort them into columns :)
From what I can tell a query using row number is what you are after. (Also, it might be a pivot table..)
Example:
create table Something (
someId int,
someValue varchar(255)
);
insert into Something values (1, 'one'), (1, 'two'), (1, 'three'), (1, 'four'), (2, 'ein'), (2, 'swei'), (3, 'un')
with cte as (
select someId,
someValue,
row_number() over(partition by someId order by someId) as rn
from Something
)
select distinct someId,
(select someValue from cte where ct.someId = someId and rn = 1) as value1,
(select someValue from cte where ct.someId = someId and rn = 2) as value2,
(select someValue from cte where ct.someId = someId and rn = 3) as value3,
(select someValue from cte where ct.someId = someId and rn = 4) as value4
into somethingElse
from cte ct;
select * from somethingElse;
Result:
someId value1 value2 value3 value4
1 one two three four
2 ein swei NULL NULL
3 un NULL NULL NULL

Update Only Rows With Shared/Similar Value

OK - I have a simple table - below - what I am trying to do is to identify only those rows that have a shared value - so anything that has a shared "apc" value - DO x else Do Y
CREATE TABLE #test (hcpcs varchar(10), apc varchar(100), rate money)
INSERT INTO #test (hcpcs, apc)
SELECT '97110', '8009'
UNION ALL
SELECT '71020', '8009'
UNION ALL
SELECT '85025', '8006'
So - from the above - all those rows that share "8009" - I will gram those rows for an update - they will share the same "rate". Row with "8006" will not be a part of that update
You want:
WHERE EXISTS (SELECT *
FROM #test t2
WHERE t2.apc = #test.apc
AND t2.hcpcs != #test.hcpcs)
Make sure apc is indexed so that this can be done effectively.
IMO the most comfortable way to get all desired results is using SQL-Server's OVER Klause.
select *, count(*) over (partition by apc) as CNT
from #test order by CNT desc
will return all rows along with the duplicate information (order clause is just used so you can easily very this).
To turn this into an update, I'd use a CTE:
with T1 as (select hcpcs, count(*) over (partition by apc) as CNT
from #test)
update #test
set rate=#newrate
from #test inner join t1 on #test.hcpcs=T1.hcpcs
where CNT > 1
(please note that the order by clause had to vanish - it's not allowed in CTE and would be useless anyway :))
Just change whatever you want to the CASE expression. You can use the OUTPUT clause to check what it does, then remove it in the actual query.
UPDATE T
SET rate = CASE WHEN SRC.CNT > 1 THEN rate*5.00 ELSE rate*2.00 END
OUTPUT inserted.apc, deleted.rate old_rate, inserted.rate NEW_rate -- This is just to show results for testing
FROM #test T
JOIN (SELECT apc, COUNT(*) CNT
FROM #test
GROUP BY apc) SRC ON SRC.apc = T.apc

Resources