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
Related
I have a table [Order] that has records with sequential ID (in odd number only, i.e. 1,3,5,7...989, 991, 993, 995, 997, 999), it is seen that a few records were accidentally deleted and should be inserted back, first thing is to find out what records are missing in the current table, there are hundreds of records in this table
Don't know how to write the query, can anyone kindly help, please?
I am thinking if I have to write a stored procedure or function but would be better if I can avoid them for environment reasons.
Below peuso code is what I am thinking:
set #MaxValue = Max(numberfield)
set #TestValue = 1
open cursor on recordset ordered by numberfield
foreach numberfield
while (numberfield != #testvalue) and (#testvalue < #MaxValue) then
Insert #testvalue into #temp table
set #testvalue = #textvalue + 2
Next
Next
UPDATE:
Expected result:
Order ID = 7 should be picked up as the only missing record.
Update 2:
If I use
WHERE
o.id IS NULL;
It returns nothing:
Since I didn't get a response from you, in the comments, I've altered the script for you to fill in accordingly:
declare #id int
declare #maxid int
set #id = 1
select #maxid = max([Your ID Column Name]) from [Your Table Name]
declare #IDseq table (id int)
while #id < #maxid --whatever you max is
begin
insert into #IDseq values(#id)
set #id = #id + 1
end
select
s.id
from #IDseq s
left join [Your Table Name] t on s.id = t.[Your ID Column Name]
where t.[Your ID Column Name] is null
Where you see [Your ID Column Name], replace everything with your column name and the same goes for [Your Table Name].
I'm sure this will give you the results you seek.
We can try joining to a number table, which contains all the odd numbers which you might expect to appear in your own table.
DECLARE #start int = 1
DECLARE #end int = 1000
WITH cte AS (
SELECT #start num
UNION ALL
SELECT num + 2 FROM cte WHERE num < #end
)
SELECT num
FROM cte t
LEFT JOIN [Order] o
ON t.num = o.numberfield
WHERE
o.numberfield IS NULL;
I am having six select queries with different where conditions if first select query returns null it should check the next select query and follows. what is the best approach to follow for writing it as stored procedure in SQL server.
You can use ##rowcount
DECLARE #OperatorID INT = 4, #CurrentCalendarView VARCHAR(50) = 'month';
declare #t table (operatorID int, CurrentCalendarView varchar(50));
insert into #t values (2, 'year');
select operatorID - 1, CurrentCalendarView from #t where 1 = 2
if (##ROWCOUNT = 0)
begin
select operatorID + 1, CurrentCalendarView from #t where 1 = 1
end
If I understand your question correctly then you can achieve this like below sample. You can go in this way.
if NOT EXISTS (SELECT TOP(1) 'x' FROM table WHERE id =#myId)
BEGIN
IF NOT EXISTS (SELECT TOP(1) 'x' FROM table2 WHERE id = #myId2)
BEGIN
IF NOT EXISTS (SELECT TOP(1) 'x' FROM table 3 WHERE id = #myID3)
BEGIN
END
END
END
I m having a trouble with my trigger, it works for single row update, but for multiple updates it gives error that sub query is returning more then one value. how to handle this.
GO
ALTER TRIGGER [dbo].[OnpaymentUpdate]
ON [dbo].[paymentData]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #customerID NCHAR(50), #lastpaymentDate DATETIME, #stat nchar(50), #month int;
SET #customerID= (SELECT customerID FROM inserted)
SET #stat= (SELECT stat FROM inserted) --table inserted contains inserted rows (or new updated rows)
set #lastpaymentDate = (SELECT MAX(paymentDate) FROM paymentReceipt where customerID=#customerID)
SET #month= (SELECT DATEDIFF(MONTH, #lastpaymentDate,GETDATE()))
DECLARE #balance BIGINT
SET #balance =
(
SELECT (totalprice-(paidAmount+concession))
FROM paymentData
WHERE customerID = #customerID
)
Declare #paid int
SET #paid =
(
SELECT paidAmount
FROM paymentData
WHERE customerID = #customerID
)
UPDATE PaymentData
SET balanceAmount = #balance ,
lastpaymentDate=#lastpaymentDate
WHERE customerID = #customerID
if (#month >=2 and #stat!='Cancel' and #stat!='Refund' And #stat!='Refunded' and #stat!='Transfered' and #stat!='Transfer')
Begin
IF (#month <2 and #stat='Defaulter')
SET #stat='Regular'
IF (#balance<=0 and #paid >0)
SET #stat='Payment Completed'
else
SET #stat='Defaulter'
End
else
Begin
if #stat='Refund'
Set #stat='Refunded'
if #stat='Cancled'
Set #stat='Cancel'
if #stat='Transfer'
Set #stat='Transfered'
End
UPDATE PaymentData
SET stat =#stat
WHERE customerID = #customerID
END
I wouldn't have a trigger at all. I'd rebuild your table and then create a view that mimics your current table definition. Of course, I don't know your current tables, so I can only write my best guess for now. And as I said, I don't understand your status logic at the bottom where #month can apparently be, simultaneously, both >=2 and <2, so I've left that portion incomplete:
create table dbo._PaymentData (
CustomerID nchar(50) not null,
_Status nchar(50) not null,
TotalPrice bigint not null,
PaidAmount bigint not null,
Concession bigint not null,
Balance as TotalPrice - (PaidAmount + Concession)
)
go
create view dbo.PaymentData
with schemabinding
as
with RecentReceipts as (
select CustomerID,MAX(PaymentDate) as LastPayment from dbo.PaymentReceipt group by CustomerID
), MonthsDelinquent as (
select CustomerID,LastPayment,DATEDIFF(month,LastPayment,CURRENT_TIMESTAMP) as Months from RecentReceipts
)
select
pd.CustomerID,
TotalPrice,
PaidAmount,
Concession,
Balance,
LastPayment,
CASE
WHEN _Status in ('Cancel','Refund','Refunded','Transfered','Transfer')
THEN _Status
WHEN md.Months > 2 and Balance<= 0 and PaidAmount > 0
THEN 'Payment Complete'
--More conditions here to work out the actual status
END as Status
from
dbo._PaymentData pd
left join
MonthsDelinquent md
on
pd.CustomerID = md.CustomerID
go
And now, we don't need the trigger - the table and/or the view are always up correct (although a trigger may be required on the view to allow Status/_Status to be updated - it's not currently clear whether that's necessary or whether _Status actually needs to exist at all)
Could their be more than one CustomerID added in the process? This line is trouble:
SET #customerID= (SELECT customerID FROM inserted)
SET #stat= (SELECT stat FROM inserted) --table inserted contains inserted
If customerID and stat are guaranteed to be consistent for all rows, you can fix it with a MAX, like:
SET #customerID= (SELECT MAX(customerID) FROM inserted)
SET #stat= (SELECT MAX(stat) FROM inserted) --table inserted contains inserted
But, if those items aren't guaranteed to be consistent for all rows inserted, you will run into trouble. If that's the case, you'll need a cursor to cursor through the values.
Also Change to this:
SET #balance =
(
SELECT SUM( (totalprice-(paidAmount+concession)) )
FROM paymentData
WHERE customerID = #customerID
)
Declare #paid int
SET #paid =
(
SELECT SUM(paidAmount)
FROM paymentData
WHERE customerID = #customerID
)
Can someone help please I dont know what I am doing wrong:
IF EXISTS ( SELECT name
FROM sys.tables
WHERE name = N'MemberIdsToDelete' )
DROP TABLE [MemberIdsToDelete];
GO
SELECT mm.memberid ,
mm.aspnetuserid ,
mm.email ,
mm.RowNum AS RowNum
INTO #MemberIdsToDelete
FROM membership.members AS mm
LEFT JOIN aspnet_membership AS asp ON mm.aspnetuserid = asp.userid
LEFT JOIN trade.tradesmen AS tr ON tr.memberid = mm.memberid
WHERE asp.isapproved = 0
AND tr.ImportDPN IS NOT NULL
AND tr.importDPN <> ''
ORDER BY mm.memberid
DECLARE #MaxRownum INT
SET #MaxRownum = ( SELECT MAX(RowNum)
FROM #MemberIdsToDelete
)
DECLARE #Iter INT
SET #Iter = ( SELECT MIN(RowNum)
FROM #MemberIdsToDelete
)
DECLARE #MemberId INT
DECLARE #TrademId INT
DECLARE #UId UNIQUEIDENTIFIER
DECLARE #Successful INT
DECLARE #OutputMessage VARCHAR(200)
DECLARE #Email VARCHAR(100)
DECLARE #Username VARCHAR(100)
SELECT #MemberId = memberId ,
#UId = AspNetUserId
FROM MemberIdsToDelete
SELECT #TrademId = TradesManId
FROM trade.TradesMen
WHERE memberId = #MemberId;
WHILE #Iter <= #MaxRownum
BEGIN
SELECT *
FROM #MemberIdsToDelete
WHERE RowNum = #Iter
--more code here
SET #Iter = #Iter + 1
END
I just want to check if my table MemberIdsToDelete exists, if so drop it,
create MemberIdsToDelete with the results set from the select
loop through MemberIdsToDelete table and perform operations
I am getting error that RowNum does not exist
For a start, to check if a table exists and then drop accordingly, you need to use something like
IF EXISTS (SELECT name
FROM sys.tables
WHERE name = N'MemberIdsToDelete')
DROP TABLE [MemberIdsToDelete];
GO
as for the error, your RowNum column does not exist when you are attempting to reference it. Include it in the SELECT statement
select mm.memberid, mm.aspnetuserid, mm.email, mm.RowNum AS RowNum
into #MemberIdsToDelete
from membership.members as mm
left join aspnet_membership as asp
on mm.aspnetuserid=asp.userid
left join trade.tradesmen as tr
on tr.memberid=mm.memberid
where asp.isapproved = 0 and tr.ImportDPN IS NOT NULL
and tr.importDPN <> ''
order by mm.memberid;
GO
I hope this helps.
Edit. Based on you additional error from your comment. You are now attempting to access a temporary table that does not exist. You must first populate the temporary table #MemberIdsToDelete before attempting to read from it. The invalid column error is down to the same problem. You are attempting to read a column called RowNum from the temporary table which does not exist.
Edit2. Remove the '#' from the #MemberIdsToDelete. You are inserting into a table not a temporary table. Or, Add a # to the select into above (see the code above). This will make it a temporary table as required.
You don't have a RowNum column in that table.
Try:
select mm.memberid, mm.aspnetuserid, mm.email, row_number() over (order by (select 1)) as RowNum
....
This should solve your problem, but I wouldnt actually recommend this idea of looping through the ones to delete.
I would like to know if there is a way to use an order by clause when updating a table. I am updating a table and setting a consecutive number, that's why the order of the update is important. Using the following sql statement, I was able to solve it without using a cursor:
DECLARE #Number INT = 0
UPDATE Test
SET #Number = Number = #Number +1
now what I'd like to to do is an order by clause like so:
DECLARE #Number INT = 0
UPDATE Test
SET #Number = Number = #Number +1
ORDER BY Test.Id DESC
I've read: How to update and order by using ms sql The solutions to this question do not solve the ordering problem - they just filter the items on which the update is applied.
Take care,
Martin
No.
Not a documented 100% supported way. There is an approach sometimes used for calculating running totals called "quirky update" that suggests that it might update in order of clustered index if certain conditions are met but as far as I know this relies completely on empirical observation rather than any guarantee.
But what version of SQL Server are you on? If SQL2005+ you might be able to do something with row_number and a CTE (You can update the CTE)
With cte As
(
SELECT id,Number,
ROW_NUMBER() OVER (ORDER BY id DESC) AS RN
FROM Test
)
UPDATE cte SET Number=RN
You can not use ORDER BY as part of the UPDATE statement (you can use in sub-selects that are part of the update).
UPDATE Test
SET Number = rowNumber
FROM Test
INNER JOIN
(SELECT ID, row_number() OVER (ORDER BY ID DESC) as rowNumber
FROM Test) drRowNumbers ON drRowNumbers.ID = Test.ID
Edit
Following solution could have problems with clustered indexes involved as mentioned here. Thanks to Martin for pointing this out.
The answer is kept to educate those (like me) who don't know all side-effects or ins and outs of SQL Server.
Expanding on the answer gaven by Quassnoi in your link, following works
DECLARE #Test TABLE (Number INTEGER, AText VARCHAR(2), ID INTEGER)
DECLARE #Number INT
INSERT INTO #Test VALUES (1, 'A', 1)
INSERT INTO #Test VALUES (2, 'B', 2)
INSERT INTO #Test VALUES (1, 'E', 5)
INSERT INTO #Test VALUES (3, 'C', 3)
INSERT INTO #Test VALUES (2, 'D', 4)
SET #Number = 0
;WITH q AS (
SELECT TOP 1000000 *
FROM #Test
ORDER BY
ID
)
UPDATE q
SET #Number = Number = #Number + 1
The row_number() function would be the best approach to this problem.
UPDATE T
SET T.Number = R.rowNum
FROM Test T
JOIN (
SELECT T2.id,row_number() over (order by T2.Id desc) rowNum from Test T2
) R on T.id=R.id
update based on Ordering by the order of values in a SQL IN() clause
Solution:
DECLARE #counter int
SET #counter = 0
;WITH q AS
(
select * from Products WHERE ID in (SELECT TOP (10) ID FROM Products WHERE ID IN( 3,2,1)
ORDER BY ID DESC)
)
update q set Display= #counter, #counter = #counter + 1
This updates based on descending 3,2,1
Hope helps someone.
I had a similar problem and solved it using ROW_NUMBER() in combination with the OVER keyword. The task was to retrospectively populate a new TicketNo (integer) field in a simple table based on the original CreatedDate, and grouped by ModuleId - so that ticket numbers started at 1 within each Module group and incremented by date. The table already had a TicketID primary key (a GUID).
Here's the SQL:
UPDATE Tickets SET TicketNo=T2.RowNo
FROM Tickets
INNER JOIN
(select TicketID, TicketNo,
ROW_NUMBER() OVER (PARTITION BY ModuleId ORDER BY DateCreated) AS RowNo from Tickets)
AS T2 ON T2.TicketID = Tickets.TicketID
Worked a treat!
I ran into the same problem and was able to resolve it in very powerful way that allows unlimited sorting possibilities.
I created a View using (saving) 2 sort orders (*explanation on how to do so below).
After that I simply applied the update queries to the View created and it worked great.
Here are the 2 queries I used on the view:
1st Query:
Update MyView
Set SortID=0
2nd Query:
DECLARE #sortID int
SET #sortID = 0
UPDATE MyView
SET #sortID = sortID = #sortID + 1
*To be able to save the sorting on the View I put TOP into the SELECT statement. This very useful workaround allows the View results to be returned sorted as set when the View was created when the View is opened. In my case it looked like:
(NOTE: Using this workaround will place an big load on the server if using a large table and it is therefore recommended to include as few fields as possible in the view if working with large tables)
SELECT TOP (600000)
dbo.Items.ID, dbo.Items.Code, dbo.Items.SortID, dbo.Supplier.Date,
dbo.Supplier.Code AS Expr1
FROM dbo.Items INNER JOIN
dbo.Supplier ON dbo.Items.SupplierCode = dbo.Supplier.Code
ORDER BY dbo.Supplier.Date, dbo.Items.ID DESC
Running: SQL Server 2005 on a Windows Server 2003
Additional Keywords: How to Update a SQL column with Ascending or Descending Numbers - Numeric Values / how to set order in SQL update statement / how to save order by in sql view / increment sql update / auto autoincrement sql update / create sql field with ascending numbers
SET #pos := 0;
UPDATE TABLE_NAME SET Roll_No = ( SELECT #pos := #pos + 1 ) ORDER BY First_Name ASC;
In the above example query simply update the student Roll_No column depending on the student Frist_Name column. From 1 to No_of_records in the table. I hope it's clear now.
IF OBJECT_ID('tempdb..#TAB') IS NOT NULL
BEGIN
DROP TABLE #TAB
END
CREATE TABLE #TAB(CH1 INT,CH2 INT,CH3 INT)
DECLARE #CH2 INT = NULL , #CH3 INT=NULL,#SPID INT=NULL,#SQL NVARCHAR(4000)='', #ParmDefinition NVARCHAR(50)= '',
#RET_MESSAGE AS VARCHAR(8000)='',#RET_ERROR INT=0
SET #ParmDefinition='#SPID INT,#CH2 INT OUTPUT,#CH3 INT OUTPUT'
SET #SQL='UPDATE T
SET CH1=#SPID,#CH2= T.CH2,#CH3= T.CH3
FROM #TAB T WITH(ROWLOCK)
INNER JOIN (
SELECT TOP(1) CH1,CH2,CH3
FROM
#TAB WITH(NOLOCK)
WHERE CH1 IS NULL
ORDER BY CH2 DESC) V ON T.CH2= V.CH2 AND T.CH3= V.CH3'
INSERT INTO #TAB
(CH2 ,CH3 )
SELECT 1,2 UNION ALL
SELECT 2,3 UNION ALL
SELECT 3,4
BEGIN TRY
WHILE EXISTS(SELECT TOP 1 1 FROM #TAB WHERE CH1 IS NULL)
BEGIN
EXECUTE #RET_ERROR = sp_executesql #SQL, #ParmDefinition,#SPID =##SPID, #CH2=#CH2 OUTPUT,#CH3=#CH3 OUTPUT;
SELECT * FROM #TAB
SELECT #CH2,#CH3
END
END TRY
BEGIN CATCH
SET #RET_ERROR=ERROR_NUMBER()
SET #RET_MESSAGE = '#ERROR_NUMBER : ' + CAST(ERROR_NUMBER() AS VARCHAR(255)) + '#ERROR_SEVERITY :' + CAST( ERROR_SEVERITY() AS VARCHAR(255))
+ '#ERROR_STATE :' + CAST(ERROR_STATE() AS VARCHAR(255)) + '#ERROR_LINE :' + CAST( ERROR_LINE() AS VARCHAR(255))
+ '#ERROR_MESSAGE :' + ERROR_MESSAGE() ;
SELECT #RET_ERROR,#RET_MESSAGE;
END CATCH