Is it possible to use MAX in update statement using sql? - sql-server

i am trying to use the MAX function in sql statement. Here is what i am trying to do:
something like this:
UPDATE MainTable
SET [Date] = GETDATE()
where [ID] = Max
I know this is wrong specially where i put the where condition but cannot figure out how to use max and update in the same statement. thanks

UPDATE MainTable
SET [Date] = GETDATE()
where [ID] = (SELECT MAX([ID]) FROM MainTable)

One way
DECLARE #MaxID INT = (select MAX(id) FROM MainTable)
UPDATE MainTable
SET [Date] = GETDATE()
where [ID] = #MaxID
That is SQL 2008 syntax, in 2005 you need to do the declaraion and assignment of the variable in two steps
You could also use a common table expression
;WITH cte
AS (
SELECT TOP 1 * FROM MainTable
ORDER BY ID DESC
)
UPDATE cte SET [Date] = GETDATE()
Example you can run
CREATE TABLE testNow(id int)
INSERT testNow VALUES(1)
INSERT testNow VALUES(2)
;WITH cte
AS (
SELECT TOP 1 * FROM testNow
ORDER BY ID DESC
)
-- id with 2 will become 5
UPDATE cte SET ID = 5
SELECT * FROM testNow
Output
1
5

UPDATE MainTable
SET [Date] = GETDATE()
WHERE [ID] = (SELECT MAX(your column) FROM yourtable)

Related

What is the optimal way to get only latest ID's from table in SQL

I'm trying to get only a single row per Appointment Number in a table storing a history of appointments. It works fine with a few rows but then gets slower? Is this the best way to do this kind of check and I'm just missing some indexes or is there a better way?
DECLARE #temptable TABLE
(
id INT PRIMARY KEY NOT NULL
, ApptNumber INT NOT NULL
, ApptDate DATE NOT NULL
, Notes VARCHAR(50) NULL
)
INSERT INTO #temptable VALUES (1,1,'01-DEC-2018','First Appointment')
INSERT INTO #temptable VALUES (2,1,'01-DEC-2018','')
INSERT INTO #temptable VALUES (3,1,'01-DEC-2018','Rescheduled')
INSERT INTO #temptable VALUES (4,2,'02-DEC-2018','Second Appointment')
INSERT INTO #temptable VALUES (5,2,'02-DEC-2018','Cancelled')
INSERT INTO #temptable VALUES (6,3,'03-DEC-2018','Third Appointment')
INSERT INTO #temptable VALUES (7,4,'04-DEC-2018','Fourth Appointment')
SELECT * FROM #temptable
SELECT MAX(id) FROM #temptable GROUP BY ApptNumber
SELECT tt.* FROM #temptable tt
INNER JOIN (SELECT MAX(id) [Id] FROM #temptable GROUP BY ApptNumber) appts ON appts.Id = tt.id
Solution 1:
select * from (
SELECT f1.*, row_number() over(partition by ApptNumber order by id desc ) rang FROM #temptable f1
) tmp where rang=1
Solution 2:
with tmp as (
select ApptNumber, max(ID) MaxID
from #temptable
group by ApptNumber
)
select f1.* from #temptable f1 inner join tmp f2 on f1.ID=f2.MaxID
Solution 3:
select distinct f3.* from #temptable f1
cross apply
(
select top 1 * from #temptable f2
where f1.ApptNumber=f2.ApptNumber
order by f2.ID desc
) f3
Window function
SELECT tt.*
FROM (
SELECT *, row_number() over (partition by ApptNumber order by id desc) as rn
) tt
where tt.rn = 1

Creating duplicates with a different ID for test in SQL

I have a table with 1000 unique records with one of the field as ID. For testing purpose, my requirement is that To update the last 200 records ID value to the first 200 records ID in the same table. Sequence isn't mandatory.
Appreciate help on this.
Typically I charge for doing other ppls homework, don't forget to cite your source ;)
declare #example as table (
exampleid int identity(1,1) not null
, color nvarchar(255) not null
);
insert into #example (color)
select 'black' union all
select 'green' union all
select 'purple' union all
select 'indigo' union all
select 'yellow' union all
select 'pink';
select *
from #example;
declare #max int = (select max(exampleId) from #example);
declare #min int = #max - 2
;with cte as (
select top 2 color
from #example
)
update #example
set color = a.color
from cte a
where exampleid <= #max and exampleid > #min;
select *
from #example
This script should solve the issue and will cover scenarios even if the id column is not sequential.I have included the comments to help you understand the joins and the flow of the script.
declare #test table
(
ID int not null,
Txt char(1)
)
declare #counter int = 1
/*******This variable is the top n or bottom n records in question it is 200 ,
for test purpose setting it to 20
************/
declare #delta int = 20
while(#counter <= 50)
begin
Insert into #test values(#counter * 5,CHAR(#counter+65))
set #counter = #counter + 1
end
/************Tag the records with a row id as we do not know if ID's are sequential or random ************/
Select ROW_NUMBER() over (order by ID) rownum,* into #tmp from #test
/************Logic to update the data ************/
/*Here we first do a self join on the tmp table with first 20 to last 20
then create another join to the test table based on the ID of the first 20
*/
update t
set t.ID = tid.lastID
--Select t.ID , tid.lastID
from #test t inner join
(
Select f20.rownum as first20 ,l20.rownum as last20,f20.ID as firstID, l20.ID lastID
from #tmp f20
inner join #tmp l20 on (f20.rownum + #delta) = l20.rownum
)tid on tid.firstID = t.ID and tid.first20 < = #delta
Select * from #test
drop table #tmp

How to UPDATE TOP(n) with ORDER BY giving a predictable result?

I'm trying to read the top 100 items of a database table that is being used like a queue. As I do this I'm trying to mark the items as done like this:
UPDATE TOP(#qty)
QueueTable WITH (READPAST)
SET
IsDone = 1
OUTPUT
inserted.Id,
inserted.Etc
FROM
QueueTable
WHERE
IsDone = 0
ORDER BY
CreatedDate ASC;
The only problem is, according to UPDATE (Transact-SQL) on MSDN, the ORDER BY is not valid in an UPDATE and:
The rows referenced in the TOP expression used with INSERT, UPDATE, or
DELETE are not arranged in any order.
How can I achieve what I need which is to update the items at the top of the queue while also selecting them?
SQL Server allows you to update a derived table, CTE or view:
UPDATE x
SET
IsDone = 1
OUTPUT
inserted.Id,
inserted.Etc
FROM (
select TOP (N) *
FROM
QueueTable
WHERE
IsDone = 0
ORDER BY
CreatedDate ASC;
) x
No need to compute a set of IDs first. This is faster and usually has more desirable locking behavior.
Tested in SSMS, it works fine. You may need to do some modification accordingly.
--create table structure
create table #temp1 (
id int identity(1,1),
value int
)
go
--insert sample data
insert #temp1 values (1)
go 20
--below is solution
declare #qty int = 10
declare #cmd nvarchar(2000) =
N'update #temp1
set value= 100
output inserted.value
where id in
(
select top '+ cast(#qty as nvarchar(5)) +' id from #temp1
order by id
)';
execute sp_executesql #cmd
You can use ranking function (for example row_number).
update top (100) q
set IsDone = 1
output
inserted.Id,
inserted.Etc
from (
select *, row_number() over(order by CreatedDate asc, (select 0)) rn
from QueueTable) q
where rn <= 100

Updating Next_ID column

I have the following table:
VehicleID Reg_ID Next_RegID EntryDate
330034 9111 NULL 2010-12-06 00:00:00
330034 9113 NULL 2010-12-09 00:00:00
On the first row I need to update the Next_RegId column with the Reg_ID of the second row where VehicleId or (VIN/ChassisNumber) is the same. The Next_RegID column on the last entry should remain Null.
I've created a while loop procedure which works perfectly, but with millions of records in the table it takes ages to complete. Therefore, I was wondering if any of you dealt with this kind of a problem and have a solution for it.
Here's the procedure I wrote, and thanks in advance for all your help:
Declare #i as integer;
Declare #x as integer;
Declare #y as integer
Set #i= (Select Max(RID) from TempRegistration)
Set #x= 0
Set #y= 1
Declare #curChassis as nvarchar(100)
Declare #nextChassis as nvarchar(100)
While (#x <= #i)
Begin
set #curChassis = (Select ChassisNumber from TempRegistration where RID = #x)
set #nextChassis = (Select ChassisNumber from TempRegistration where RID = #y)
If (#curChassis = #nextChassis)
Begin
Update Registration set NextRegistrationId = (Select RegistrationId from TempRegistration where RID = #y)
Where RegistrationId = (Select RegistrationId from TempRegistration where RID = #x)
End
Set #x = #x + 1
Set #y = #y + 1
Print(#x)
End
TempRegistration is a temporary table I've created to assign a row_id which guides the while loop to assign the Reg_ID to the Next_RegId on the previous row.
This can be done with one UPDATE query. You haven't mentioned your RDBMS so...
For MSSQL:
Update Registration as t1
set NextRegistrationId = (Select TOP 1 RegistrationId
from Registration
where RID = t1.RID
and EntryDate>t1.EntryDate
order by EntryDate DESC)
For MySQL
Update Registration as t1
set NextRegistrationId = (Select RegistrationId
from Registration
where RID = t1.RID
and EntryDate>t1.EntryDate
order by EntryDate DESC
LIMIT 1)
If RID's are increasing with EntryDate then
Update Registration as t1
set NextRegistrationId = (Select MIN(RegistrationId)
from Registration
where RID = t1.RID
and EntryDate>t1.EntryDate
)
Tested and it seems to be working but this version uses a CTE (SQL Server)
with RegDetails as
(
select VehicleID, Reg_ID, ROW_NUMBER() OVER(PARTITION BY VehicleID ORDER BY EntryDate) AS ROWNUMBER
FROM dbo.Vehicle)
UPDATE a SET a.Next_RegID = b.Reg_ID
FROM RegDetails b
INNER JOIN dbo.Vehicle a ON (a.VehicleID = b.VehicleID)
WHERE b.ROWNUMBER = 2 and a.Next_RegID IS NULL and a.Reg_ID != b.Reg_ID

T-SQL Update From Order By

I am trying to write a Stored Procedure that will give me Audit information between a date range. The Audit table stores the Audit Date, Column Name and Old Value. I want to display the Old Value and the New Value in the result set. I need to get the new value from the next most recent Audit entry or from the entity itself. The stored procedure is a multi step approach to get the result set I need.
Create a #results temp table with the Audit records from within the date range.
Create a #currentValues temp table with the current values from the entity.
Update the #results table to store the new value
Here is the structure of the Audit Table:
AuditId uniqueidentifier NEWID()
AuditDate datetime GETDATE()
UserId uniqueidentifier
EntityId uniqueidentifier
ColumnName nvarchar(100)
OldValue nvarchar(MAX)
Here is the sql:
CREATE PROC GetAuditSummary
#StartDate datetime = NULL,
#EndDate datetime = NULL
AS
DECLARE #Results table(
AuditId uniqueidentifier,
AuditDate datetime,
UserId uniqueidentifier,
EndityId uniqueidentifier,
ColumnName nvarchar(100),
OldValue nvarchar(MAX),
NewValue nvarchar(MAX)
INSERT INTO #Results(AuditId, AuditDate, UserId, EntityId, ColumnName, OldValue)
SELECT AuditId, AuditDate, UserId, EntityId, ColumnName, OldValue
FROM Audit
WHERE (AuditDate >= #StartDate) AND (AuditDate < #EndDate)
DECLARE #CurrentValues table(
EntityId uniqueidentifier,
ColumnName nvarchar(100),
Value nvarchar(MAX)
)
--Lengthy Code to fill #CurrentValues temp table. Assume #CurrentValues is populated
UPDATE #Results
SET NewValue = n.Value
FROM #Results r INNER JOIN
(SELECT AUditId, AuditDate, EntityId, ColumnName, OldValue AS Value
FROM Audit
UNION ALL
SELECT NULL, GETDATE(), EntityId, ColumnName, Value
FROM #CurrentValues
ORDER BY AuditDate DESC
) n ON n.EntityId = r.EntityId AND
n.ColumnNmae = r.ColumName NAD
n.AuditDate > r.AuditDate
SELECT * FROM #Results ORDER BY AuditDate DESC
Now, and correct me if I'm wrong, when the update statement executes, the NewValue should be set to the last matching row in the joined result set and since I have the subquery ordered by AuditDate, the AuditDate closest to the current record from #Results should be the value that's set to NewValue. I've tried this, but I get an error telling me I can't use an Order By in a subquery. Is there another way to do this? I'm open to any suggestions, but I need to take performance into consideration as there is a chance of having thousands of rows in the result.
--EDIT
Here is one way to get it working, but I'm not sure it's the best on performance.
UPDATE #Results
SET NewValue = COALESCE(
(SELECT TOP 1 a.OldValue
FROM Audit a
WHERE (a.EntityId = r.EntityId) AND
(a.ColumnName = r.ColumnName) AND
(a.AuditDate > r.AuditDate)
ORDER BY a.AuditDate),
(SELECT TOP 1 c.Value
FROM #CurrentValues c
WHERE (c.EntityId = r.EntityId) AND
(c.ColumnName = r.ColumnName))
FROM #Results r
I would use Row_Number or Rank function to get the row after last matching row.
Following example should work, you may want to change (order by n.AuditDate) to (order by n.AuditDate desc) if you want most recent record after matching date.
UPDATE #Results
SET NewValue = n.Value
FROM #Results r
INNER JOIN
(
select n.EntityId, n.ColumnName, n.Value, Row_Number() over(partition by n.EntityId, n.ColumnName order by n.AuditDate) RowNumber
from
#Results ir
inner join (
SELECT AuditDate, EntityId, ColumnName, OldValue AS Value
FROM Audit
UNION ALL
SELECT GETDATE(), EntityId, ColumnName, Value
FROM #CurrentValues
) inn on inn.EntityId = ir.EntityId AND
inn.ColumnNmae = ir.ColumName NAD
inn.AuditDate > ir.AuditDate
) n ON n.EntityId = r.EntityId AND
n.ColumnNmae = r.ColumName AND
n.RowNumber = 1

Resources