Ordered data before update in SQL Server 2008 R2 - sql

I have a temp table inside a stored procedure.
What I am doing is fetching data from different table and inserting it into temp table using
INSERT INTO #TempTable
SELECT * FROM t1
INNER JOIN t2 ON t1.ID = t2.ID
After inserting, I need to update one column. Before updating the temp table I want to order the temp table data by another column.
How can I apply ORDER By clause before update clause on temp table?

Make sure your temporary table has an IDENTITY field.
CREATE TABLE #TempTable
(
ID int identity(1,1) primary key
...
)
Put an order by on your initial insertion:-
INSERT INTO
#TempTable
SELECT * FROM t1
INNER JOIN
t2
ON t1.ID = t2.ID
ORDER BY [Your Field]
Rationale:-
Some may argue that putting the identity field in is unnecessary, or indeed that the order of the rows in the temp table is unimportant. I'd disagree.
First, having an ID field allows you to do joins like :-
SELECT
t1.*, t2.SpecificThing
FROM
#TempTable t1
INNER JOIN
#TempTable t2
ON t1.ID = ( t2.ID + 1)
Which can be handy for any running total / cumulation tricks.
As for ordering not being important in a temp table, I'd disagree - at least on SQL Server. The UPDATE statement on SQL Server updates rows in order. If it didn't, this fascinating (and very speedy) running total trick would never work.
CREATE TABLE #CustomerInfo
(
ID int identity(1,1) primary key,
CustomerId int,
SaleValue money,
RunningTotal money
)
-- Assume customer is populated
DECLARE #RunningTotal decimal(18,4)
DECLARE #CustomerId INT
SELECT #CustomerId = CustomerId FROM #CustomerInfo WHERE Id = 1
SET #RunningTotal = 0
-- This only works in SQL Server because under the hood, the engine is updating
-- iteratively in order.
--
-- Starts at row 0, then row 1 - with each row the value of #RunningTotal
-- is reassigned.
UPDATE #CustomerInfo
SET
#RunningTotal =
RunningTotal =
CASE WHEN
-- Are we still looking at the same customer?
#CustomerId = CustomerId
THEN
-- Yes. Add sale value to #RunningTotal
#RunningTotal + SaleValue
ELSE
-- No, reset #RunningTotal to SaleValue of this row
SaleValue
END
,#CustomerId = CustomerId
FROM #CustomerInfo

Related

SQL Server: automatically add a unique identifier to all rows inserted at one time

The below SQL Server code successfully calculates and inserts the monthly pay for all employees along with their staffID number and inserts it into Tablepayroll.
INSERT INTO Tablepayroll (StaffID,Totalpaid)
(SELECT Tabletimelog.StaffID , Tabletimelog.hoursworked * Tablestaff.hourlypay
FROM Tabletimelog
JOIN Tablestaff ON
Tabletimelog.StaffID = Tablestaff.StaffID)
However, I want to be able to also insert a batchIDso that you can identify each time the above insert has been run and the records inserted by it at that time. Meaning that all staff payroll calculated at the same time would have the same batchID number. Each subsequent batchID should just increase by 1.
Please see image below for visual explanation .
I think that Select MAX(batch_id) + 1 would work , but I don't know how to include it in the insert statement.
You can use subquery to find latest batch_id from your current table using this query:
INSERT INTO TablePayroll (StaffID, TotalPaid, batch_id)
SELECT T1.StaffID
, T1.HoursWorked * T2.HourlyPay
, ISNULL((SELECT MAX(batch_id) FROM TablePayRoll), 0) + 1 AS batch_id
FROM TableTimeLog AS T1
INNER JOIN TableStaff AS T2
ON T1.StaffID = T2.StaffID;
As you can see, I just add 1 to current MAX(batch_id) and that's it.
By the way, learn to use aliases. It will make your life easier
Yet another solution would be having your batch_id as a GUID, so you wouldn't have to create sequences or get MAX(batch_id) from current table.
DECLARE #batch_id UNIQUEIDENTIFIER = NEWID();
INSERT INTO TablePayroll (StaffID, TotalPaid, batch_id)
SELECT T1.StaffID, T1.HoursWorked * T2.HourlyPay, #batch_id
FROM TableTimeLog AS T1
INNER JOIN TableStaff AS T2
ON T1.StaffID = T2.StaffID;
Updated
First of all obtain the maximum value in a large table (based on the name of the table it must be big) can be very expensive. Especially if there is no index on the column batch_id
Secondly, pay attantion your solution SELECT MAX(batch_id) + 1 may behave incorrectly when you will have competitive inserts. Solution from #EvaldasBuinauskas without opening transaction and right isolation level can also lead to same batch_id if you run the two inserts at the same time in parallel.
If your SQL Server ver 2012 or higer you can try SEQUENCE. This at least ensures that no duplicates batch_id
Creating SEQUENCE:
CREATE SEQUENCE dbo.BatchID
START WITH 1
INCREMENT BY 1 ;
-- DROP SEQUENCE dbo.BatchID
GO
And using it:
DECLARE #BatchID INT
SET #BatchID = NEXT VALUE FOR dbo.BatchID;
INSERT INTO Tablepayroll (StaffID,Totalpaid, batch_id)
(SELECT Tabletimelog.StaffID , Tabletimelog.hoursworked * Tablestaff.hourlypay, #BatchID
FROM Tabletimelog
JOIN Tablestaff ON Tabletimelog.StaffID = Tablestaff.StaffID)
An alternative SEQUENCE may be additional table:
CREATE TABLE dbo.Batch (
ID INT NOT NULL IDENTITY
CONSTRAINT PK_Batch PRIMARY KEY CLUSTERED
,DT DATETIME
CONSTRAINT DF_Batch_DT DEFAULT GETDATE()
);
This solution works even on older version of the server.
DECLARE #BatchID INT
INSERT INTO dbo.Batch (DT)
VALUES (GETDATE());
SET #BatchID = SCOPE_IDENTITY();
INSERT INTO Tablepayroll (StaffID,Totalpaid, batch_id)
(SELECT Tabletimelog.StaffID , Tabletimelog.hoursworked * Tablestaff.hourlypay, #BatchID
FROM Tabletimelog ...
And yes, all of these solutions do not guarantee the absence of holes in the numbering. This can happen during a transaction rollback (deadlock for ex.)

SQL Server 2008: Sql Insert/Update into another table using insertion IDs output from another table

I have a procedure for insert in multiple dependent tables (update in case record exist). I have input parameters as comma separated string which I am reading in table.
After 1st insertion I am getting InsertedIds in another table variable.
I am struggling over how to do insert in 2nd table. I have following input parameters for 2nd table:
Declare #IdsToBeUpdated table (primary key identity pkey, id int) -- values are 1,-1,3,-1
Declare #CommentsTobeInserted table( primary key identity pkey, comment varchar (max)) -- values are 'com1', 'com2', 'com3'
-1 input in table #IdsToBeUpdated depicts insertion for the corresponding rows in all input tables and value other than -1 depicts that records at that pkey value in all other tables (#CommentsTobeInserted table as in example) have to be updated.
So after first insertion I am getting the inserted ids for rows with -1 value. #InsertedIds = 4,5
So my Ids list logically would become.. 1,4,3,5.
But I am stuck now how to insert/update records in second table respectively.
2nd table would be like follows:
Pkey Primary key identity, commentIds(coming from inserted ids and #IdsToBeUpdated), comments.
I have added one more InsertedIds column in #CommentsTobeInserted. If I could fill it with right InsertedId against correct row, I guess I would be able to do insert/update in 2nd table. And where the value is Id in new column I would do insert, where it's null, I would perform update using #IdsToBeUpdated table.
But currently none of my approach is working. I am new to SQL. Any help would be highly appreciated.
Following is some portion of script for better understanding. I have added first insertion script as well.
USE [Demo]
GO
/****** Object: StoredProcedure [dbo].[USP_NewRequest_Insert] Script Date: 2/11/2016 2:50:34 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[USP_NewRequest_Insert]
-----------------------------------
--------- SomeInput parameters
---------for first insertion omitted
--------------------------------
#IdsToBeUpdated varchar(MAX), --Values are 23|-1|32|-1|-1
#CommentList AS VARCHAR(MAX), --Values 'C2'|'C4'|'C5'|'C6'|'C7'
#MCodeList VARCHAR(MAX), --Values 'M2'|'M4'|'M5'|'M6'|'M7'
#CustomerIdsList VARCHAR(MAX), ----Values 'Cust2'|'Cust4'|'Cust5'|'Cust6'|'Cust7'
#ReturnValue AS INT OUTPUT,
------------------Data is in the order it has to be inserted.. where IDs exist in #IdsToBeUpdated, updation would be done. For -1 values, insertion.
AS
BEGIN
BEGIN TRANSACTION
--------------------------Split input strings aand insert in Table variable----------------------------
declare #MCodes Table (pkey int primary key identity, MCode varchar(20))
insert into #MCodes select s.Item from [dbo].UDF_SplitString(#MCodeList, '|') s
declare #CusCodes Table (pkey int primary key identity, CusCode varchar(200))
insert into #CusCodes select s.Item from [dbo].UDF_SplitString(#CustomerIdsList, '|') s
declare #ReqDetailsIds Table (pkey int primary key identity, Id Int)
insert into #ReqDetailsIds select Convert(INT,RTRIM(LTRIM(s.Item))) from [dbo].UDF_SplitString(#IdsToBeUpdated, '|') s
where s.Item is not null and RTRIM(LTRIM(s.Item)) <>''
declare #ProductComments Table (pkey int primary key identity, Comment Varchar(max), insertedId int null)
insert into #ProductComments(Comment) select s.Item from [dbo].UDF_SplitString(#CommentList, '|') s
DECLARE #intErrorCode int;
------------------------------------------------------------------------------------------------------------
-----------------First Insertion which returns inserted IDs for 2nd insertion
------------------------------------------------------------------------------------------------------------------
---Insert/Update product details in [RequestDetails]
Declare #InsertedIDList Table (pkey int primary key identity, ID int); --------Table to read inserted Ids. Used in query below
-----------------------Insert query in case Detail id = -1
INSERT INTO [dbo].[RequestDetails]
[MCode]
,[CustomerIds]
,[ExpectedVolume]------Some parameters coming for first insertion in input
,[StatusCode])
Output INSERTED.ReqDetailId Into #InsertedIDList(ID)
SELECT A.MCode, B.CusCode, E.Vol,1
FROM #MCodes A
JOIN #CusCodes B ON B.pkey = A.pkey
JOIN #ExpectedVols E ON E.pkey = A.pkey
JOIN #ReqDetailsIds G ON G.pkey = A.pkey
WHERE G.Id = -1 --If id = -1, insert
---------------------------Update Query for rest records
UPDATE [dbo].[RequestDetails]
SET [MCode] = upd.MCode
,[CustomerIds] = upd.CusCode
,[ExpectedVolume] = upd.ExVol
,[StatusCode] = 1
FROM(
SELECT A.MCode, B.CusCode, E.ExVol, G.Id
FROM #MCodes A
JOIN #CusCodes B ON B.pkey = A.pkey
JOIN #ExpectedVols E ON E.pkey = A.pkey
JOIN #ReqDetailsIds G ON G.pkey = A.pkey
WHERE G.Id <> -1
) upd
WHERE upd.Id = dbo.RequestDetails.ReqDetailId
IF(##Error<>0)
BEGIN
SET #intErrorCode = ##Error
GOTO ERROR
END
ELSE
BEGIN
SET #ReturnValue=1
END
---------------------------------------------------------------------------
----------------------------Now similarly I have to do insert/update in Comments Table. But
----------------------------Comments table has RequestDetails Id column as foreign key. So
----------------------------now the challange is to add the rows with Inserted ID where value was = -1
----------------------------in input. We have got the IDs corresponding to -1 values from above insertion
----------------------------in the #InsertedIDList Table variable
-----------------------------------------------------------------------------------------
----------------------------Following is what I have tried so far. But I am not able to insert
----------------------------correct InsertedId against correct record.
----------------------------------------------------------------------------------------
-----------------------Here I tried to insert the new generated ids against corresponding comments in table variable.
-----------------------So that I can perform insert where value is not null. As NULL would be inserted where new ID has not been created
-----------------------and corresponding updated ID exists in input (Values not equal to -1)
-------------------------------------------------------------------------------------------------
Update #ProductComments set insertedId = i.ID from ---------This query is not working
(select A.pkey, B.id as detailId, row_number() over (order by (select 0)) as row_num from
#ProductComments A
JOIN #ReqDetailsIds B ON B.pkey = A.pkey) as mappedNewIds right join
#InsertedIDList i on i.pkey = mappedNewIds.row_num
where mappedNewIds.pkey = [#ProductComments].pkey
----Insert in CommentsTable for New Comments against request
---------------------------------
INSERT INTO [dbo].CommentsTable
( ReqDetailId, Comments, CreatedOn )
SELECT A.insertedId, A.Comment, GETDATE()
FROM #ProductComments A
where A.insertedId is not null
-----Update Query
------------------------------------------------------------------------------------------
UPDATE [dbo].[CommentsTable]
SET [ReqDetailId] = upd.Id
,[Comments] = upd.Comment
,[CreatedOn] = GetDate()
FROM(
SELECT A.Comment, B.Id
FROM #ProductComments A
JOIN #ReqDetailsIds B ON B.pkey = A.pkey
WHERE A.insertedId is not null
) upd
WHERE upd.Id = dbo.CommentsTable.ReqDetailId
END
select * from CommentsTable;
---------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------
IF(##Error<>0)
BEGIN
SET #intErrorCode = ##Error
GOTO ERROR
END
ELSE
BEGIN
SET #ReturnValue=1
END
COMMIT TRANSACTION
ERROR:
IF (#intErrorCode <> 0) BEGIN
SET #ReturnValue = 0;
ROLLBACK TRANSACTION
END
END

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

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

please help me to create multi insert query

I have got two table
create table t1(cid int, isnews int)
create table t2(nid int,cid int, isnews int)
situations is like this:
if t2 contain t2.cid = t1.cid then the t2.isnews = t1.news and
if t2 not contain cid of t1 then new record should be inserted in t2 and that t1.cid, t1.isnews should be inserted in t2..
and complete table should be done in single query... i have done the updation part but not able to do insertion part..
update query:
UPDATE t22
SET t22.isnews = t11.isnews
FROM t2 AS t22
JOIN t1 AS t11
ON t11.cid= t22.cid
i have prepared below cursor for insert... is it good? :
DECLARE #clntid INT
DECLARE #clntnewsltr INT
DECLARE clientnews CURSOR FOR
SELECT clientid,newsLetter
FROM clients
WHERE clientid NOT IN (SELECT clientid FROM clientprivacy)
OPEN clientnews
FETCH NEXT FROM clientnews INTO #clntid,#clntnewsltr
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO clientprivacy (clientId,tdNewsLetters) VALUES(#clntid, #clntnewsltr)
FETCH NEXT FROM clientnews INTO #clntid,#clntnewsltr
END
CLOSE clientnews
DEALLOCATE clientnews
I think this is the kind of thing you're after:
--INSERT t2 (cid, isnews)
SELECT t1.cid, t1.isnews
FROM t1
LEFT JOIN t2 ON t1.cid = t2.cid
WHERE t2.cid IS NULL
I've commented out the INSERT line - I recommend you run the SELECT on it's own first to check it does give you the correct result (all records from t1 that don't have a matching cid in t2).
I've assumed t2.nid is an IDENTITY column.
You will be so much better off without cursors :) Cursors take MUCH longer to run in large data sets.
It is true you can use a LEFT JOIN, but you can also use a SELECT in your WHERE clause. Most of the time it's a style choice.
CREATE TABLE table1(col_1 int, col_2 int)
CREATE TABLE table2(nid int, col_1 int, col_2 int)
INSERT INTO table2 (col_1,col_2)
SELECT col_1,col_2
FROM table1
WHERE col_1 NOT IN (SELECT col_1 FROM table2)

Use APPLY with an UPDATE statement as the row source

I tried to perform the following in order to update a psuedo-identity value at the same time as using the value to create new rows, but APPLY does not like UPDATE statements as the right table source. What's the most elegant alternative outside of simply using an identity column?
create table Temp1(
id int not null identity(1,1) primary key
,data nvarchar(max) null)
create table Temp2(
id int not null primary key
,fkTemp1 int not null references Temp1(id)
,data nvarchar(max) null)
create table Numbering(
ObjectCode int not null primary key
,AutoKey int)
insert into Temp1(data) values('test string')
insert into Temp1(data) values('another test string')
insert into Numbering(ObjectCode, AutoKey) values(4, 1)
insert into Temp2(id, fkTemp1, data)
select n.AutoKey, t1.id, t1.data
from Temp1 t1
left join Temp2 t2 on t2.fkTemp1 = t1.id
cross apply (update Numbering set AutoKey = AutoKey + 1 output inserted.AutoKey where ObjectCode = 4) n
where t2.id is null -- only insert where a target row does not already exist
You cannot do an INSERT and UPDATE on two different tables in one statement in SQL Server 2005.
In SQL Server 2008 there is MERGE construct, however, it works only on single table.
Just run two statements in a transaction:
BEGIN TRANSACTION
DECLARE #AutoKey INT
SELECT #AutoKey = AutoKey
FROM Numbering WITH (UPDLOCK)
WHERE ObjectCode = 4
INSERT
INTO temp2
SELECT #AutoKey + ROW_NUMBER() OVER (ORDER BY id), id, data
FROM temp1
WHERE id NOT IN
(
SELECT fkTemp1
FROM temp2
)
UPDATE Numbering
SET AutoKey = AutoKey + ##ROWCOUNT
WHERE ObjectCode = 4
COMMIT
Update:
As #Remus Rusanu pointed out, you actually can pipeline UPDATE output clause into a table in SQL Server 2005.
However, it seems you can neither JOIN nor CROSS APPLY the OUTPUT resultset to the result of other queries.
This will do it, but you'll have to fix the "T2.ID IS NULL" problem...
Declare #Key as int
Declare #cnt as int
Begin Transaction
Set #cnt = (Select count(*)
from Temp1 t1 left join Temp2 t2 on t2.fkTemp1 = t1.id
--where t2.id is null -- note: does not work, not sure what is intended
)
update Numbering set #Key = AutoKey = AutoKey + #cnt where ObjectCode = 4
insert into Temp2(id, fkTemp1, data)
select #Key+ROW_NUMBER() over (Order By t1.id)
, t1.id, t1.data
from Temp1 t1
left join Temp2 t2 on t2.fkTemp1 = t1.id
--where t2.id is null -- note: does not work,
Commit Transaction