Using Insert and Update statements in a related operation - sql

I want to insert a record to a table called Payment which has column ID as the primary key(Auto Increment) and then I want to get that ID to use in a WHERE clause of another update statement.
var insertSatement = #"BEGIN INSERT INTO Payment (payment_type, reference, payment_date, total_records, total_amount) VALUES(#type, #reference, #date, #totalRecords, #totalAmount ) ";
var updateStatement = #"UPDATE SalaryTrans SET payment_id = (SELECT TOP 1 id FROM Payment ORDER BY Payment.id) WHERE SalaryTrans.id = #paramID ";
These two statements could not be merged as the update is going to update multiple rows. It will update all matching rows of the SalaryTrans table. So I'm using a foreach loop.
//open connection, add parameters
sqlCommand.CommandText = insertStatement;
sqlCommand.ExecuteNonQuery(); // This inserts...
foreach(PaymentInfo p in paymentList)
{
paramID.value = p.id;
sqlCommand.CommandText = updateStatement;
sqlCommand.ExecuteNonQuery();
}
In the loop each time "SELECT TOP 1 id..." is also executed. To avoid it, is there a way to use SCOPE_IDENTITY() to get the last updated ID from Payment table and use it in the loop?
Would there be a difference if I change update statement as follows in this context (performance wise) ?
DECLARE #ID INT = (SELECT TOP 1 id FROM Payment ORDER BY Payment.id)
UPDATE SalaryTrans SET payment_id = #ID WHERE SalaryTrans.id = 1
Or else should I separate this SELECT from the UPDATE to keep it outside the loop?
NOTE : My main concentration here is the performance factor.

What you can also try is, change your statement like below
var insertSatement = #"BEGIN INSERT INTO Payment (payment_type, reference, payment_date, total_records, total_amount) VALUES(#type, #reference, #date, #totalRecords, #totalAmount ); SELECT CAST(scope_identity() AS int) ";
Then in your excecute non query get the return value
sqlCommand.CommandText = insertStatement;
int id = (int) sqlCommand.ExecuteScalar(); // This inserts...
You can use the id in the loop

You can use SCOPE_IDENTITY
It will contain the latest value of the identity column from the newly inserted row
http://msdn.microsoft.com/en-us/library/ms190315.aspx

Related

Enumerate the multiple rows in a multi-update Trigger

I have something like the table below:
CREATE TABLE updates (
id INT PRIMARY KEY IDENTITY (1, 1),
name VARCHAR (50) NOT NULL,
updated DATETIME
);
And I'm updating it like so:
INSERT INTO updates (name, updated)
VALUES
('fred', '2020-11-11),
('fred', '2020-11-11'),
...
('bert', '2020-11-11');
I need to write an after update Trigger and enumerate all the name(s) that were added and add each one to another table but can't work out how enumerate each one.
EDIT: - thanks to those who pointed me in the right direction, I know very little SQL.
What I need to do is something like this
foreach name in inserted
look it up in another table and
retrieve a count of the updates a 'name' has done
add 1 to the count
and update it back into the other table
I can't get to my laptop at the moment, but presumably I can do something like:
BEGIN
SET #count = (SELECT UCount from OTHERTAB WHERE name = ins.name)
SET #count = #count + 1
UPDATE OTHERTAB SET UCount = #count WHERE name = ins.name
SELECT ins.name
FROM inserted ins;
END
and that would work for each name in the update?
Obviously I'll have to read up on set based SQL processing.
Thanks all for the help and pointers.
Based on your edits you would do something like the following... set based is a mindset, so you don't need to compute the count in advance (in fact you can't). It's not clear whether you are counting in the same table or another table - but I'm sure you can work it out.
Points:
Use the Inserted table to determine what rows to update
Use a sub-query to calculate the new value if its a second table, taking into account the possibility of null
If you are really using the same table, then this should work
BEGIN
UPDATE OTHERTAB SET
UCount = COALESCE(UCount,0) + 1
WHERE [name] in (
SELECT I.[name]
FROM Inserted I
);
END;
If however you are using a second table then this should work:
BEGIN
UPDATE OTHERTAB SET
UCount = COALESCE((SELECT UCount+1 from OTHERTAB T2 WHERE T2.[name] = OTHERTAB.[name]),0)
WHERE [name] in (
SELECT I.[name]
FROM Inserted I
);
END;
Using inserted and set-based approach(no need for loop):
CREATE TRIGGER trg
ON updates
AFTER INSERT
AS
BEGIN
INSERT INTO tab2(name)
SELECT name
FROM inserted;
END

How i can update Click Counter of Record with in select stored Procedure

I am trying to record no of click on Product.I already have a stored procedure that selects the details of products from DB.
I want to update the viewcount column when the stored procedure whenever it selects the record.
ALTER PROCEDURE [dbo].[Weportal_sp_Product_SelectOne]
#iProductId int,
#iErrorCode int OUTPUT
AS
SET NOCOUNT ON
-- SELECT an existing row from the table.
update [dbo].[Weportal_Product] set viewcount =((select viewcount from [dbo].[Weportal_Product] where ProductId=#iProductId)+1) where ProductId=#iProductId;
SELECT * FROM [dbo].[Weportal_Product]
WHERE ProductId = #iProductId
-- Get the Error Code for the statement just executed.
SELECT #iErrorCode=##ERROR
I already tried this code update the view counter but by four. Each time the record accessed its increase 4 in view counter column.
I need to select the product record and update the number of views against the products id.
You don't need a subquery for this. Just update the value:
update [dbo].[Weportal_Product]
set viewcount = viewcount + 1
where ProductId = #iProductId;
You may try this.
update WP SET WP.VIEWCOUNT = P.VIEWCOUNT + 1
FROM [dbo].[Weportal_Product] AS WP
INNER JOIN
(SELECT ProductId, MAX(VIEWCOUNT) AS VIEWCOUNT
FROM [dbo].[Weportal_Product] GROUP BY ProductId) AS P
ON WP.ProductId = P.ProductId --- you may apply further conditions over there to filter out the results
WHERE WP.ProductId = #iProductId

where condition in Insert/Update Trigger not working

This is my trigger:
Create trigger Points
on Posts
after insert, update
As
declare #Id int;
declare #value int;
select #value= Count(i.Message) from Posts i;
select #Id = [PostedBy] from inserted;
update AspNetUsers set User_points = #value * 3
where #Id = #Id
Here, at the last line, where condition always fails.Its not picking correct Id and updating same value in User_Points column in all rows not in particular rows.
I have written an insert statement to check what value i get back like this:
insert into Employee_Demo(PostedBy, TotalCount)
values (#postedby,#value );
here, i am getting correct #postedby value in table.
Previosly, i was trying this:
create trigger Points
on Posts
after insert, update
As
declare #value int
declare #postedby int
select #value= Count(Message) from Posts
select #postedby = PostedBy from inserted
update AspNetUsers set User_points = #value * 3
where Id = #postedby
please please someone help me. how to update only single row based on Id.
One more thing, PostedBy is not the primary key in post table.It is foreign key to aspnetuser table and it contains id value of user who has posted the message as u can see in below image.
tried this too:
update AspNetUsers set User_points = #value * 3
FROM INSERTED INNER JOIN Posts ON INSERTED.PostedBy = Posts.PostedBy
INNER JOIN AspNetUsers ON AspNetUsers.Id = inserted.PostedBy
Currently, you are comparing same Variable in where clause, where you are supposed to compare DB field with Variable.
Finally got it working with this query dont know how it happens
update AspNetUsers set User_points = #value * 3
where Id = (Select PostedBy from inserted)
and removing the declaration of #Id or #PostedBy. when i declared those variable and then assign value from inserted to those variable and then use that variable value in where then it was not working.
Instead, directly getting value from inserted table in where condition works.

SQL Server update value in another table if value in (inserted) first table is greater

I have the following tables: (simplified for clarity)
AppWorkHeader with columns: (ProjectID (FK), ProgPercent, + 40 others not relevant)
AppProjects with columns: (ProjectID (PK), ProgPercent + 9 others not relevant)
I am trying to create a trigger that after insert, the ProgPercent value from the AppWorkHeader table updates the ProgPercent value in the AppProjects table only if it's greater than the existing value. I can get it to work with single insertions with the following:
-- Only works on single row insert
CREATE TRIGGER [dbo].[AppWorkHeader_project_trigger]
ON
[dbo].[AppWorkHeader]
AFTER INSERT AS
IF ##ROWCOUNT = 0
RETURN
SET NOCOUNT ON
-- Get the current project completion percentage from the AppProjects table
DECLARE #inserted_projectID int = (SELECT i.ProjectID FROM inserted i)
DECLARE #current_percent decimal(5,4) = (SELECT ProgPercent FROM AppProjects WHERE ProjectID = #inserted_projectID)
UPDATE AppProjects
SET ProgPercent = inserted.ProgPercent
FROM inserted
WHERE AppProjects.ProjectID = inserted.ProjectID AND inserted.ProgPercent > #current_percent
I can get multiple insertions to work with the following code. However, the greater-than part of my where clause seems to be ignored. Multiple insertions with the same ProjectID are updated to lower values.
-- Multiple row insert
CREATE TRIGGER [dbo].[AppWorkHeader_project_trigger]
ON
[dbo].[AppWorkHeader]
AFTER INSERT AS
IF ##ROWCOUNT = 0
RETURN
SET NOCOUNT ON
UPDATE AppProjects
SET ProgPercent = inserted.ProgPercent
FROM inserted
WHERE AppProjects.ProjectID = inserted.ProjectID AND inserted.ProgPercent > (SELECT ProgPercent FROM AppProjects WHERE ProjectID = (SELECT inserted.ProjectID))
I can't figure out how to get the existing value in the AppProjects table without using a variable, and I can't seem to get multiple insertions to work if I use a variable. Where am I going wrong?
For each inserted ProjectID find the maximum ProgPercent (if there are several rows inserted with the same ProjectID).
Then join table with new values to the table with old values.
CREATE TRIGGER [dbo].[AppWorkHeader_project_trigger]
ON [dbo].[AppWorkHeader]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
WITH
CTE_InsertedProjects
AS
(
SELECT
inserted.ProjectID
,MAX(inserted.ProgPercent) AS MaxProgPercent
FROM inserted
GROUP BY inserted.ProjectID
)
,CTE_AllProjects
AS
(
SELECT
AppProjects.ProjectID
,AppProjects.ProgPercent AS OldProgPercent
,CTE_InsertedProjects.MaxProgPercent AS NewProgPercent
FROM
AppProjects
INNER JOIN CTE_InsertedProjects ON CTE_InsertedProjects.ProjectID = AppProjects.ProjectID
WHERE
CTE_InsertedProjects.MaxProgPercent > AppProjects.ProgPercent
)
UPDATE CTE_AllProjects
SET OldProgPercent = NewProgPercent;
END
While Tring to Update Use JOIN To match the column ProjectID from AppProjects on AppWorkHeader ..
Update AP
set AP.ProgPercent=AWH.ProgPercent
FROM AppProjects AP Left JOIN AppWorkHeader AWH on AP.ProjectID=AWH.ProjectID
Where AP.ProgPercent < AWH.ProgPercent
This will only Update those rows where ProgPercent in AppProjects is Lesser than ProgPercent in AppWorkHeader

Incremental count column based on another column contents

I need to populate a column with the running count based on another column contents. The table is like this:
count seq_num
1 123-456-789
1 123-456-780
1 123-456-990
2 123-456-789
2 123-456-990
So, as the seq_num column changes, the counter resets to '1' and as the column repeats, the counter increments by 1.
This is using SQL2000, and the seq_num field is varchar.
Any ideas?
If you're inserting, you can use a subquery:
insert into
table (count, seq_num)
values
((select count(*)+1 from table where seq_num = #seq)
,#seq)
Otherwise, you'll need to have a date on there or some way of telling it how to determine what was first:
update table
set count =
(select count(*)+1 from table t2
where t2.seq_num = table.seq_num
and t2.insertdate < table.insertdate)
if you need to be able to continue updating this in the future, you might try this. It's a few steps but would fix it AND set it up for future use. (probably need to check my syntax - I mess with ORacle more now, so I may have mixed up some things - but the logic should work.)
first, create a table to contain the current counter level per sequence:
Create newTable (counter int, sequence varchar)
then, fill it with data like this:
insert into newTable
(select distinct 0 as Counter, sequence
from table)
This will put each sequence number in the table one time and the counter for each will be set at 0.
Then, create an update proc with TWO update statements and a bit of extra logic:
Create procedere counterUpdater(#sequence varchar) as
Declare l_counter as int;
select l_counter = counter
from newTable
where sequence = #sequence
--assuming you have a primary key in the table.
Declare #id int;
Select top 1 #id = id from table
where sequence = #sequence
and counter is null;
--update the table needing edits.
update table
set counter = l_counter + 1
where id = #id
--update the new table so you can keep track of which
--counter you are on
update newTable
set counter = l_counter + 1
where id = #id
Then run a proc to execute this proc for each record in your table.
Now you should have a "newTable" filled with the currently used counter for each record in the table. Set up your insert proc so that anytime a new record is created, if it is a sequence not already in the newTable, you add it with a count of 1 and you put a count of 1 in the main table. If the sequence DOES already exist, use the logic above (increment the count already in use the "newTable" and place that count as the counter value in the newTable and the mainTable.
Basically, this method decided to use memory in place of querying the existing table. It will become most beneficial if you have a large table with lots of repeated sequence numbers. If your sequence numbers only happen two or three times, you probably want to do a query instead when you update and then later insert:
First, to update:
--find out the counter value
Declare l_counter int
select l_counter = max(counter)
from table where sequence = #sequence
update table
set counter = l_counter + 1
where id = (select top 1 id from table where sequence = #sequence
and counter is null)
then run that for each record.
Then, when inserting new records:
Declare l_counter int
select l_counter = max(counter)
from table where sequence = #sequence
IsNull(l_counter, 0)
Insert into table
(counter, sequence) values (l_counter + 1, #sequence)
Again, I'm positive I've mixed-and-matched my syntaxes here, but the concepts should work. OF course, it's a "one at a time" approach instead of set based, so it might be a little inefficient, but it will work.