SQL Server : assign sequential number not working - sql-server

I'm trying to execute the following code in order add sequential number to a table.
declare #myvar2 int
set #myvar2 = 1
update _TEST_DATA
set ID = #myvar2, #myvar2 = #myvar2 + 1;
When I run this, I get the same ID assigned to multiple rows in my table. Any suggestions?
I'm on SQL Server 2012

your increment needs to be done in its own set statement, NOT within the "set" clause of the update statement.
Try this:
update _TEST_DATA set ID = #myvar2
-- update is done, now increment
set #myvar2 = #myvar2 + 1

You can use row number to generate a sequence which increments by 1:
DECLARE #myvar2 INT;
SET #myvar2 = 1;
UPDATE td
SET ID = rn
FROM _TEST_DATA td
INNER JOIN (SELECT ID,
#myvar2 + ROW_NUMBER() OVER (ORDER BY ID) rn
FROM _TEST_DATA
) a ON a.ID = td.ID;

Related

MS SQL Server Extract Integer from column, increment by 1 and load the number back into column

I have a table with a column (Integer). In a MS Sql Server 2005 stored procedure I want to take the value in that column, increment it by 1 and update the column to be the incremented value. This column is a counter so each row in the table will have a different counter, IE: I can't just put a trigger on the column to auto increment it or something like that. I have some code as listed below, but all it does is update the column to be 1.
SELECT ControlNo = #c
FROM MedicaidInterchange
WHERE 1=1
AND Interkey = #InterKey;
SET #c = #c +1;
UPDATE MedicaidInterchange
SET InterDate = #d
, InterTime = #t
, ControlNo = #c
WHERE 1=1
AND Interkey = #InterKey;
Will someone please help me figure out where my code is failing? Thanks in advance for any and all help.
The reason your code doesn't work is this line:
SELECT ControlNo = #c
This is the same as:
SELECT #c as ControlNo
Per the documentation:
| column_alias = expression
You probably meant to write:
SELECT #c = ControlNo
But not sure if you need a variable here. You can refer to old values on the right hand side of SET:
UPDATE MedicaidInterchange
SET ControlNo = ControlNo + 1
WHERE Interkey = #InterKey

Update row with values from select on condition, else insert new row

I'm need to run a calculation for month every day. If the month period, exists already, I need to update it, else I need to create a new row for the new month.
Currently, I've written
declare #period varchar(4) = '0218'
DECLARE #Timestamp date = GetDate()
IF EXISTS(select * from #output where period=#period)
/* UPDATE #output SET --- same calculation as below ---*/
ELSE
SELECT
#period AS period,
SUM(timecard.tworkdol) AS dol_local,
SUM(timecard.tworkdol/currates.cdrate) AS dol_USD,
SUM(timecard.tworkhrs) AS hrs,
#Timestamp AS timestamp
FROM dbo.timecard AS timecard
INNER JOIN dbo.timekeep ON timecard.ttk = timekeep.tkinit
INNER JOIN dbo.matter with (nolock) on timecard.tmatter = matter.mmatter
LEFT JOIN dbo.currates with (nolock) on matter.mcurrency = currates.curcode
AND currates.trtype = 'A'
AND timecard.tworkdt BETWEEN currates.cddate1
AND currates.cddate2
WHERE timekeep.tkloc IN('06','07') AND
timecard.twoper = #period
SELECT * FROM #output;
How can simply update my row with the new data from my select.
Not sure what RDBMS are you using, but in SQL Server something like this would update the #output table with the results of the SELECT that you placed in the ELSE part:
UPDATE o
SET o.dol_local = SUM(timecard.tworkdol),
SET o.dol_USD = SUM(timecard.tworkdol/currates.cdrate),
SET o.hrs = SUM(timecard.tworkhrs),
set o.timestamp = #Timestamp
FROM #output o
INNER JOIN dbo.timecard AS timecard ON o.period = timecard.twoper
INNER JOIN dbo.timekeep ON timecard.ttk = timekeep.tkinit
INNER JOIN dbo.matter with (nolock) on timecard.tmatter = matter.mmatter
LEFT JOIN dbo.currates with (nolock) on matter.mcurrency = currates.curcode
AND currates.trtype = 'A'
AND timecard.tworkdt BETWEEN currates.cddate1
AND currates.cddate2
WHERE timekeep.tkloc IN('06','07') AND
timecard.twoper = #period
Also, I think you want to do an INSERT in the ELSE part, but you are doing just a SELECT, so I guess you should fix that too
The answer to this will vary by SQL dialect, but the two main approaches are:
1. Upsert (if your DBMS supports it), for example using a MERGE statement in SQL Server.
2. Base your SQL on an IF:
IF NOT EXISTS (criteria for dupes)
INSERT INTO (logic to insert)
ELSE
UPDATE (logic to update)

Error in merge statement in T-SQL

I have this my stored procedure:
create procedure [dbo].[Sp_AddPermission]
#id nvarchar(max)
as
declare #words varchar(max), #sql nvarchar(max)
set #words = #id
set #sql = 'merge admin AS target
using (values (''' + replace(replace(#words,';','),('''),'-',''',') + ')) AS source(uname, [add], [edit], [delete], [view],Block)
on target.uname = source.uname
when matched then update set [add] = source.[add], [edit] = source.[edit], [delete] = source.[delete], [view] = source.[view], [Block]=source.[Block];'
exec(#sql);
When executing it, this error is shown:
The MERGE statement attempted to UPDATE or DELETE the same row more
than once. This happens when a target row matches more than one source
row. A MERGE statement cannot UPDATE/DELETE the same row of the target
table multiple times. Refine the ON clause to ensure a target row
matches at most one source row, or use the GROUP BY clause to group
the source rows.
How to resolve this?
Regards
Baiju
The problem is obvious: you a generating a source table with multiple values for the same uname.
Why are you using a merge for this? I think a simple update would do, and I don't think update will return an error when you have multiple identical keys in source:
update t
set [add] = source.[add],
[edit] = source.[edit],
[delete] = source.[delete],
[view] = source.[view],
[Block]=source.[Block]
from target t join
(values(. . . )) s(uname, [add], [edit], [delete], [view],Block)
on t.uname = s.uname;
But, you could fix this if you like by choosing an arbitrary row for the update (which is what the above does):
update t
set [add] = source.[add],
[edit] = source.[edit],
[delete] = source.[delete],
[view] = source.[view],
[Block]=source.[Block]
from target t join
(select s.*,
row_number() over (partition by uname order by uname) as seqnum
from (values(. . . )) s(uname, [add], [edit], [delete], [view],Block)
) s
on t.uname = s.uname and s.seqnum = 1;
Of course, this approach can also be used with the merge.

VB.net access data from 2 select statement in stored procedure

I am trying to access the result from 2 select statement in vb.net, but I can only access the data from the 1st select statement.
CREATE PROCEDURE [dbo].[pr_testproc]
AS
BEGIN
SET NOCOUNT ON;
SELECT
comp_name, p_center, branch_id
FROM
tbl_company B
INNER JOIN
tbl_b_office X ON X.batch_id = B.tran_id
WHERE
X.office_stat = 1
ORDER BY
comp_name ASC;
SELECT
xcomp_name, xp_center, xbranch_id
FROM
tbl_company B
INNER JOIN
tbl_b_office X ON X.batch_id = B.tran_id
WHERE
X.office_stat = 0
AND MONTH(X.post_dt) = MONTH(GETDATE())
AND YEAR(X.post_dt) = YEAR(GETDATE())
ORDER BY
comp_name ASC;
END
Example if I try to get the result for data in 2nd table
getData(intRow).xcomp_name
This gets the error:
Object reference not set to an instance of an object.
But this would get the data
getData(intRow).comp_name
will get the value of the field.
Collect the output data to a Dataset so that, you can take the result of first select query from the DataTable in the 0th index of the dataset.
this May help you:
Dim myDataSet=GetDataSetFromSP("yourSPName")
Dim FirstResult As DataTable=myDataSet.Tables(0)
Dim SecontResult As DataTable=myDataSet.Tables(1)
Here you have to write the function that will execute the SP and return DataSet.
You could also combine the 2 queries into one.
CREATE PROCEDURE [dbo].[pr_testproc]
AS
BEGIN
SET NOCOUNT ON;
SELECT
comp_name, p_center, branch_id, xcomp_name, xp_center, xbranch_id
FROM
tbl_company B
INNER JOIN
tbl_b_office X ON X.batch_id = B.tran_id
WHERE
X.office_stat = 1
OR
(
X.office_stat = 0
AND MONTH(X.post_dt) = MONTH(GETDATE())
AND YEAR(X.post_dt) = YEAR(GETDATE())
)
ORDER BY
comp_name ASC;
END
Then in the code, check the value of office_stat and continue accordingly.

tsql bulk update

MyTableA has several million records. On regular occasions every row in MyTableA needs to be updated with values from TheirTableA.
Unfortunately I have no control over TheirTableA and there is no field to indicate if anything in TheirTableA has changed so I either just update everything or I update based on comparing every field which could be different (not really feasible as this is a long and wide table).
Unfortunately the transaction log is ballooning doing a straight update so I wanted to chunk it by using UPDATE TOP, however, as I understand it I need some field to determine if the records in MyTableA have been updated yet or not otherwise I'll end up in an infinite loop:
declare #again as bit;
set #again = 1;
while #again = 1
begin
update top (10000) MyTableA
set my.A1 = their.A1, my.A2 = their.A2, my.A3 = their.A3
from MyTableA my
join TheirTableA their on my.Id = their.Id
if ##ROWCOUNT > 0
set #again = 1
else
set #again = 0
end
is the only way this will work if I add in a
where my.A1 <> their.A1 and my.A2 <> their.A2 and my.A3 <> their.A3
this seems like it will be horribly inefficient with many columns to compare
I'm sure I'm missing an obvious alternative?
Assuming both tables are the same structure, you can get a resultset of rows that are different using
SELECT * into #different_rows from MyTable EXCEPT select * from TheirTable and then update from that using whatever key fields are available.
Well, the first, and simplest solution, would obviously be if you could change the schema to include a timestamp for last update - and then only update the rows with a timestamp newer than your last change.
But if that is not possible, another way to go could be to use the HashBytes function, perhaps by concatenating the fields into an xml that you then compare. The caveat here is an 8kb limit (https://connect.microsoft.com/SQLServer/feedback/details/273429/hashbytes-function-should-support-large-data-types) EDIT: Once again, I have stolen code, this time from:
http://sqlblogcasts.com/blogs/tonyrogerson/archive/2009/10/21/detecting-changed-rows-in-a-trigger-using-hashbytes-and-without-eventdata-and-or-s.aspx
His example is:
select batch_id
from (
select distinct batch_id, hash_combined = hashbytes( 'sha1', combined )
from ( select batch_id,
combined =( select batch_id, batch_name, some_parm, some_parm2
from deleted c -- need old values
where c.batch_id = d.batch_id
for xml path( '' ) )
from deleted d
union all
select batch_id,
combined =( select batch_id, batch_name, some_parm, some_parm2
from some_base_table c -- need current values (could use inserted here)
where c.batch_id = d.batch_id
for xml path( '' ) )
from deleted d
) as r
) as c
group by batch_id
having count(*) > 1
A last resort (and my original suggestion) is to try Binary_Checksum? As noted in the comment, this does open the risk for a rather high collision rate.
http://msdn.microsoft.com/en-us/library/ms173784.aspx
I have stolen the following example from lessthandot.com - link to the full SQL (and other cool functions) is below.
--Data Mismatch
SELECT 'Data Mismatch', t1.au_id
FROM( SELECT BINARY_CHECKSUM(*) AS CheckSum1 ,au_id FROM pubs..authors) t1
JOIN(SELECT BINARY_CHECKSUM(*) AS CheckSum2,au_id FROM tempdb..authors2) t2 ON t1.au_id =t2.au_id
WHERE CheckSum1 <> CheckSum2
Example taken from http://wiki.lessthandot.com/index.php/Ten_SQL_Server_Functions_That_You_Have_Ignored_Until_Now
I don't know if this is better than adding where my.A1 <> their.A1 and my.A2 <> their.A2 and my.A3 <> their.A3, but I would definitely give it a try (assuming SQL Server 2005+):
declare #again as bit;
set #again = 1;
declare #idlist table (Id int);
while #again = 1
begin
update top (10000) MyTableA
set my.A1 = their.A1, my.A2 = their.A2, my.A3 = their.A3
output inserted.Id into #idlist (Id)
from MyTableA my
join TheirTableA their on my.Id = their.Id
left join #idlist i on my.Id = i.Id
where i.Id is null
/* alternatively (instead of left join + where):
where not exists (select * from #idlist where Id = my.Id) */
if ##ROWCOUNT > 0
set #again = 1
else
set #again = 0
end
That is, declare a table variable for collecting the IDs of the rows being updated and use that table for looking up (and omitting) IDs that have already been updated.
A slight variation on the method would be to use a local temporary table instead of a table variable. That way you would be able to create an index on the ID lookup table, which might result in better performance.
If schema change is not possible. How about using trigger to save off the Ids that have changed. And only import/export those rows.
Or use trigger to export it immediately.

Resources