Updating Values of a table from same table without using a select query - sql

My Requirement
Updating Values of a table from same table without using a select query
this query won't effect any rows.
My aim : Update val2 of #table where slno=1 with the value of val2 of slno=2
Is there any other way without doing this method
Declare #val2 nvarchar(50)
select #val2=val2 from #table where slno=2
update #table set val2=#val2 where slno=1
create table #table
(
slno int identity(1,1),
val nvarchar(50),
val2 nvarchar(50)
)
insert into #table(val,val2)values('1',newID())
insert into #table(val,val2)values('1',newID())
insert into #table(val,val2)values('1',newID())
select * from #table
update #table set val2=T.val2
from #table T where slno=1 and T.slno=2
drop table #table
I have lot of records in the table.
So If i am selecting and update it may effect the performance.

Please, provide more info.
Do you have only 2 rows in your table?
Why do you need this kind of update?
I suppose, that your db structure is wrong, but I can't tell exactly, until you explain why do you need this.
Anyway I can suggest a poor way to do this without using select. You can self join the table. It would be better to have addition column, but if you don't have it, how's you should do
UPDATE T1
SET T1.val2 = T2.val2
FROM #table T1 INNER JOIN #table T2
ON T1.slno = 1 AND T2.slno = 2

Related

Track changes on 2+ columns using OUTPUT clause

I have a dbo.ChangeLog table that we insert changes to certain columns.
The normal way I do this is something along the lines of:
UPDATE dbo.Table
SET FirstName = NewFirstName
OUTPUT
Deleted.Id
'FirstName' as Type
Deleted.FirstName as OldValue
Inserted.FirstName as NewValue
INTO dbo.ChangeLog
FROM dbo.Table as t
INNER JOIN dbo.Table2 as t2 on t.Id = t2.Id
When I need to update FirstName AND LastName, for example, I usually do 2 update statements, but I am wondering if it's possible to update multiple columns in the same update statement, while also inserting the changes into the dbo.ChangeLog table.
If you can change the definition of dbo.ChangeLog it becomes trivial, but I'm assuming that's not really what you want.
I'd handle this with a table variable and "unpivot" the data using CROSS APPLY (but would probably work fine using UNPIVOT too...):
SELECT 1 as id, 'Dan ' as FirstName, 'Field ' as LastName INTO #tbl
DECLARE #changes TABLE (id int, old1 varchar(250), new1 varchar(250), old2 varchar(250), new2 varchar(250))
UPDATE #tbl
SET FirstName = 'asdf', LastName = NEWID()
OUTPUT deleted.id
,deleted.FirstName, inserted.FirstName
,deleted.LastName, inserted.LastName
INTO #changes(id, old1, new1, old2, new2)
-- change this to an INSERT dbo.ChangeLog
SELECT unp.*
FROM #changes
CROSS APPLY (VALUES (id, old1, new1)
,(id, old2, new2)) unp(id, oldval, newval)
DROP TABLE #tbl
Just change that last SELECT into an INSERT. And note that this requires an intermediate table variable or temp table, because you can't use CROSS APPLY or UNPIVOT in an OUTPUT clause.

Replace empty values as 0 instead of null while joining two tables in sql

I have join two tables t1 and t2. The output produces some null records since there is no data in the table t2. Instead of showing null I want to show 0 since I have to perform some arithmetic operation in the crystal reports.
please help me.....
sample example
declare #t table (ID int)
declare #t1 table (ID int)
insert into #t (id) values (1)
select t.ID,ISNULL(TT.ID,0)id from #t t
LEFT JOIN #t1 tt
ON t.ID = tt.ID
Use the COALESCE function which automatically replace null values as 0.
Sample
SELECT COALESCE(total_amount, 0) from #Temp1

INSERT ONLY SPECIFIC COLUMN FROM A STORED PROCEDURE RESULT

I want to know if it is possible to insert to a table from a specific column of result from a stored procedure?
Something like:
declare #temp as table(
id int
)
insert #temp
exec getlistofStudents --returns multiple columns
this is an example only, Thanks for the help..
You can take a 2 step approach. First INSERT INTO a #TempTable, then populate the #TempVariable with another INSERT INTO, selecting the single column.
DECLARE #temp AS TABLE
(
ID int
);
CREATE TABLE #tempTable1
(
Column1 int,
Column2 int
);
INSERT INTO #tempTable1
Exec getlistofStudents
INSERT INTO #temp
SELECT Column1 FROM #tempTable1

SQL:Updating a value (coming from destination table) in the source table after copying the data from source to destination table

The tables I have are;
TableA {TableA_OID, TableB_OID, SomeFields} //Source Table
TableB{TableB_OID, SomeFields} //Destination Table
I have to copy some data from source table to destination table, and on success i want to take the primary key identity field(TableB_OID) of destination table back to update (TableB_OID) field in the source table.
I think the following will work, but I'd play with it with some reasonable size data sets first, to be sure:
DECLARE #TA TABLE (ID INT IDENTITY(1,1), AID INT)
INSERT #TA(AID) SELECT TableA_OID FROM TABLEA -- ORDER BY data desc
DECLARE #TB TABLE (ID INT IDENTITY(1,1), BID INT)
INSERT TableB( data )
OUTPUT Inserted.TableB_OID INTO #TB(BID)
SELECT data
FROM #TA TA JOIN TableA ON TA.AID=TableA.TableA_OID ORDER BY TA.ID
SELECT * FROM #TA
SELECT * FROM #TB
UPDATE TableA
SET TableB_OID=TB.BID
FROM #TB TB
JOIN #TA TA ON TB.ID=TA.ID
JOIN TableA ON TA.AID=TableA.TableA_OID
SELECT * FROM TableA
SELECT * FROM TableB
First of all we're going to impose an order on the data we pull from table A, and use an identity column in a temporary table to record that order, linked to the original table A records. We'll then insert data into table B using that order, and record the resulting output into another temporary table. Again, we'll use an identity to record the sequence. We'll then use the identity values from the two temporary tables to link the tableA and tableB rows
I think you want to select the scope_identity()?
This will do a single row:
INSERT INTO TableB (
something
)
VALUES (
'Some Value'
)
DECLARE #Id int
SET #Id = scope_identity()
UPDATE TableA SET tableB_OID = #Id WHERE TableA_OID = TableAId
If you need to copy more than one row at once, something like the following will work:
DECLARE #data TABLE(ID int, data varchar(50))
INSERT TableB( data )
OUTPUT Inserted.TableB_OID, INSERTed.data INTO #data
SELECT data FROM TableA
UPDATE TableA
SET TableB_OID=D.ID
FROM #data D JOIN TableA ON D.DATA=TableA.data
It does make an assumption though, that there is a unique key in your "SomeField" (column "data") in my example, otherwise you can't relate the identity data back into tableA. If there is, then fine, otherwise, as Steph said, you'll need to add a TableA_OID field into TableB to be able to do the join to write back the data

How to avoid inserting duplicate records when using a T-SQL Merge statement

I am attempting to insert many records using T-SQL's MERGE statement, but my query fails to INSERT when there are duplicate records in the source table. The failure is caused by:
The target table has a Primary Key based on two columns
The source table may contain duplicate records that violate the target table's Primary Key constraint ("Violation of PRIMARY KEY constraint" is thrown)
I'm looking for a way to change my MERGE statement so that it either ignores duplicate records within the source table and/or will try/catch the INSERT statement to catch exceptions that may occur (i.e. all other INSERT statements will run regardless of the few bad eggs that may occur) - or, maybe, there's a better way to go about this problem?
Here's a query example of what I'm trying to explain. The example below will add 100k records to a temp table and then will attempt to insert those records in the target table -
EDIT
In my original post I only included two fields in the example tables which gave way to SO friends to give a DISTINCT solution to avoid duplicates in the MERGE statement. I should have mentioned that in my real-world problem the tables have 15 fields and of those 15, two of the fields are a CLUSTERED PRIMARY KEY. So the DISTINCT keyword doesn't work because I need to SELECT all 15 fields and ignore duplicates based on two of the fields.
I have updated the query below to include one more field, col4. I need to include col4 in the MERGE, but I only need to make sure that ONLY col2 and col3 are unique.
-- Create the source table
CREATE TABLE #tmp (
col2 datetime NOT NULL,
col3 int NOT NULL,
col4 int
)
GO
-- Add a bunch of test data to the source table
-- For testing purposes, allow duplicate records to be added to this table
DECLARE #loopCount int = 100000
DECLARE #loopCounter int = 0
DECLARE #randDateOffset int
DECLARE #col2 datetime
DECLARE #col3 int
DECLARE #col4 int
WHILE (#loopCounter) < #loopCount
BEGIN
SET #randDateOffset = RAND() * 100000
SET #col2 = DATEADD(MI,#randDateOffset,GETDATE())
SET #col3 = RAND() * 1000
SET #col4 = RAND() * 10
INSERT INTO #tmp
(col2,col3,col4)
VALUES
(#col2,#col3,#col4);
SET #loopCounter = #loopCounter + 1
END
-- Insert the source data into the target table
-- How do we make sure we don't attempt to INSERT a duplicate record? Or how can we
-- catch exceptions? Or?
MERGE INTO dbo.tbl1 AS tbl
USING (SELECT * FROM #tmp) AS src
ON (tbl.col2 = src.col2 AND tbl.col3 = src.col3)
WHEN NOT MATCHED THEN
INSERT (col2,col3,col4)
VALUES (src.col2,src.col3,src.col4);
GO
Solved to your new specification. Only inserting the highest value of col4: This time I used a group by to prevent duplicate rows.
MERGE INTO dbo.tbl1 AS tbl
USING (SELECT col2,col3, max(col4) col4 FROM #tmp group by col2,col3) AS src
ON (tbl.col2 = src.col2 AND tbl.col3 = src.col3)
WHEN NOT MATCHED THEN
INSERT (col2,col3,col4)
VALUES (src.col2,src.col3,src.col4);
Given the source has duplicates and you aren't using MERGE fully, I'd use an INSERT.
INSERT dbo.tbl1 (col2,col3)
SELECT DISTINCT col2,col3
FROM #tmp src
WHERE NOT EXISTS (
SELECT *
FROM dbo.tbl1 tbl
WHERE tbl.col2 = src.col2 AND tbl.col3 = src.col3)
The reason MERGE fails is that it isn't checked row by row. All non-matches are found, then it tries to INSERT all these. It doesn't check for rows in the same batch that already match.
This reminds me a bit of the "Halloween problem" where early data changes of an atomic operation affect later data changes: it isn't correct
Instead of GROUP BY you can use an analytic function, allowing you to select a specific record in the set of duplicate records to merge.
MERGE INTO dbo.tbl1 AS tbl
USING (
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY col2, col3 ORDER BY ModifiedDate DESC) AS Rn
FROM #tmp
) t
WHERE Rn = 1 --choose the most recently modified record
) AS src
ON (tbl.col2 = src.col2 AND tbl.col3 = src.col3)