SQL Server After Insert and After Update trigger - sql-server

I need to create after insert and after update trigger. If the new row is inserted in table, it should run after insert trigger and put timestamp in other table. But if this row is edited again, it should update the other table also.
The status can be changed to active, pending etc. So when each status changes, I need to put timestamp. And for every new record I need to put new row.
Here is the table structure:
| ID | Name | Status |
|----|------|--------|
| 1 | xyz | Active |
| | | |
| | | |
Let's say this is new row inserted in table, so it should be inserted into another table. But when I change its status, it should update the other table against this ID.
| ID | Name | Active Staus | Other Status |
|----|------|--------------|--------------|
| 1 | xyz | TimeStamp | Time Stamp |
| | | | |
| | | | |`
USE [DemoDB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[test_INSERT]
ON [dbo].[Demo]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE ID BIGINT
Declare #status varchar(50)
SELECT #ID = INSERTED.ID
FROM INSERTED
INSERT INTO [dbo].[LogTble]
VALUES(#ID,'timestamp')
END

Well - your trigger is halfway there.
You're assuming that there's only one row in Inserted - this is NOT generally the case! If your INSERT statements inserts multiple rows, there will be multiple entries in Inserted and code like this:
SELECT #ID = INSERTED.ID FROM INSERTED
will fail miserably.... you'll get one arbitary row selected - and all others are ignored....
You need to be aware that Inserted will contain multiple rows and you need to write your trigger accordingly - in a set-based fashion.
Try this:
ALTER TRIGGER [dbo].[test_INSERT]
ON [dbo].[Demo]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [dbo].[LogTble] (Id, ActiveStatus)
SELECT i.ID, SYSDATETIME()
FROM Inserted i
END

Related

Execute Stored Procedure using Trigger on VIEW when multiple columns are updated with specific values

I have a VIEW (in SQL SERVER) containing the following columns:
itemID[vachar(50)]|itemStatus [vachar(20)]|itemCode[vachar(20)]|itemTime[varchar(5)]
The itemID column contains id values that do not change. The remaining 3 rows however get updated periodically. I understand it is more difficult create a trigger on a VIEW.
An example of the table containing data would be:
|itemID|imtemStatus|itemCode|itemTime|
|------|-----------|--------|--------|
| 1 | OK | 30 | 00:10 |
|------|-----------|--------|--------|
| 2 | OK | 40 | 02:30 |
|------|-----------|--------|--------|
| 3 | STOPPED | 30 | 00:01 |
|------|-----------|--------|--------|
When itemStatus = STOPPED & itemCode = 30
I would like to execute a stored procedure (sp_Alert) passing the itemID as a parameter
Any help would be greatly appreciated
Since a trigger is at least "not easy", I'd like to propose an ugly but functional way out. You can create a stored procedure that checks ItemCode and ItemStatus. If they match your criteria you can start the sp_Alert from this procedure.
create procedure check_status as
if (select 1
from vw_itemstatus
where itemStatus = 'STOPPED'
and itemCode = 30) is not null
begin
declare #item_id int
set #item_id = (select itemID
from vw_itemstatus
where itemStatus = 'STOPPED'
and itemCode = 30)
exec sp_Alert #item_id
end
Depending on how critical this functionality is and how many resources you can use for it, you can schedule this procedure via the SQL Server Agent. If you run this with a short interval, it will work "similar" to what you had in mind.

Stored procedure to create or update

I am working on creating a Web API which will get account as the input parameter, which will have to create / update records in the table in SQL Server. So the web service will need to call the stored procedure which will accept the account. I created a sample table in the database with just two columns called Account and CounterSeq. I am trying to creating a stored procedure to create or update the records in the table.
Each account should have a CounterSeq associated with it. If the Account doesn't exists in the table, create Account name and associate CounterSeq = 001 to it. If the Account name already exists, just update like CounterSeq to CounterSeq + 1
+---------+----------------+
| Account | CounterSeq |
+---------+----------------+
| ABC | 001 |
| DEF | 002 |
+---------+----------------+
For this I create a TableType like this
USE [Demo]
GO
-- Create the data type
CREATE TYPE projectName_TT AS TABLE
(
Account nvarchar(50),
CounterSeq int
)
GO
And the stored procedure as below but I am missing how to insert the new record like for a new Account how to set the CounterSeq to 001 ?
USE [Demo]
GO
ALTER PROCEDURE [dbo].[uspInserorUpdateProjectName]
#projectName_TT AS projectName_TT READONLY
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRANSACTION;
UPDATE prj
SET prj.Account = tt.Account,
prj.CounterSeq = tt.CounterSeq + 1
FROM dbo.[ProjectName] prj
INNER JOIN #projectName_TT tt ON prj.Account = tt.Account
INSERT INTO [dbo].[ProjectName](Account, CounterSeq)
SELECT tt.Account, tt.CounterSeq
FROM #projectName_TT tt
WHERE NOT EXISTS (SELECT 1
FROM [dbo].[ProjectName]
WHERE Account = tt.Account)
COMMIT TRANSACTION;
END;
First, I believe that the table you passed in should only have one field (based on your description If the Account doesn't exists in the Database create Account name and associate CounterSeq 001 to it. If the Account name already exists just update like CounterSeq to CounterSeq+1)
You can use this query (put it in your stored procedure between the BEGIN TRANSACTION, COMMIT TRANSACTION.
MERGE dbo.[ProjectName] prj
USING #projectName_TT tt
ON prj.Account = tt.Account
WHEN MATCHED THEN UPDATE SET prj.CounterSeq = prj.CounterSeq+1
WHEN NOT MATCHED THEN INSERT (Account,CounterSeq)
VALUES (tt.Account, 1);

How to view the history of changed values in a table?

I have the following table (MYAPPTABLE):
--------------------------------------------------
| ColumnName | Column Type |
--------------------------------------------------
| IdApp | INT (PK) |
--------------------------------------------------
| NameApp | NVARCHAR(50) |
--------------------------------------------------
My purpose is to see the change history (Insert, Update, Delete) made on the records of the table, example:
Making an insert:
INSERT INTO MYAPPTABLE (NAMEAPP) VALUES ('StackOverFlowApp')
Making a UPDATE:
UPDATE MYAPPTABLE SET NAMEAPP = 'StackOverFlowAppV2' WHERE NAMEAPP = 'StackOverFlowApp'
What I would like to see in a history Select:
--------------------------------------------------
| IdApp | NameApp |
--------------------------------------------------
| 1 | StackOverFlowApp |
--------------------------------------------------
| 1 | StackOverFlowAppV2 |
--------------------------------------------------
the first row is the insert, and the second is after update.
But if I do this using change tracking, the only thing that throws me is that rows were modified (table version), but no history values ​​modified.
My purpose can be accomplished with any tool SQL Server, or would have to handle it manually?

How to update records with multiple tables linked by a foreign key using XQuery (or OPENXML) in SQL Server?

I have T-SQL code like this:
DECLARE #xml XML = (SELECT CONVERT(xml, BulkColumn, 2) FROM OPENROWSET(Bulk 'C:\test.xml', SINGLE_BLOB) [blah])
-- Data for Table 1
SELECT
ES.value('id-number[1]', 'VARCHAR(8)') IDNumber,
ES.value('name[1]', 'VARCHAR(8)') Name,
ES.value('date[1]', 'VARCHAR(8)') Date,
ES.value('test[1]', 'VARCHAR(3)') Test,
ES.value('testing[1]', 'VARCHAR(511)') Testing,
ES.value('testingest[1]', 'VARCHAR(5)') Testingest
FROM #xml.nodes('xmlnodes/path') AS EfficiencyStatement(ES)
-- Data for Table 2
SELECT
U.value('fork[1]', 'VARCHAR(8)') Fork,
U.value('spoon[1]', 'VARCHAR(3)') Spoon,
U.value('spork[1]', 'VARCHAR(3)') Spork,
FROM #xml.nodes('xmlnodes/path/nextpath') AS Utensils(U)
Now, I've tried what I normally use, and other variants, such as:
AS XML ON xml.[id-number] = [table1].[id-number]
For the record, id-number is unique across the entire document. It can never occur again.
This is good for grabbing the data from my XML file, but there's zero referential integrity. How do I make sure that Table 2 (and up) maintains referential integrity when inserting?
This should be a much better explanation:
I want to load XML values from a file. For INSERT, I have no trouble using OPENXML and binding it based on the id-number using AS XML ON xml.[id-number] = [table1].[id-number] at the end.
I want to update the database record (with all linked tables and their columns) using UPDATE, MERGE, or something -- anything! To do this, I believe I need to find a way to maintain referential integrity based on the Foreign_ID value present in each table. There are dozens of tables which are all linked via Foreign_ID, so how do I update all of these?
Table Example
Table #1
+-------------+-----------+-----------+------------+---------+-----------+------------+
| Primary_Key | ID_Number | Name | Date | Test | Testing | Testingest |
+-------------+-----------+-----------+------------+---------+-----------+------------|
| 70001 | 12345 | Tom | 01/21/14 | Hi | Yep | Of course! |
| 70002 | 12346 | Dick | 02/22/14 | Bye | No | Never! |
| 70003 | 12347 | Harry | 03/23/14 | Sup | Dunno | Same. |
+----^--------+-----------+-----------+------------+---------+-----------+------------+
|
|-----------------|
|
Table #2 | Linked to primary key in the first table.
+-------------+--------v--------+-------------+-------------+------------+
| Primary_Key | Foreign_ID | Fork | Spoon | Spork |
+-------------+-----------------+-------------+-------------+------------+
| 0001 | 70001 | Yes | No | No |
| 0002 | 70002 | No | Yes | No |
| 0003 | 70003 | No | No | Yes |
+-------------+-----------------+-------------+-------------+------------+
After that is inserted, I need to be able to UPDATE the tables and columns from the XML files. After much research, I can't figure out how to update the values of every table linked by Foreign_ID while maintaining referential integrity. This means I am inserting the wrong data in the other tables.
I want the correct data updated. To update it correctly, I need to ensure that XQuery is matching the right data. Some tables have multiple fields for one particular Foreign_ID.
Here's the code I'm using:
DECLARE #xml XML = (SELECT CONVERT(xml, BulkColumn, 2) FROM OPENROWSET(Bulk 'C:\test.xml', SINGLE_BLOB) [blah])
-- Data for Table 1
SELECT
ES.value('id-number[1]', 'VARCHAR(8)') IDNumber,
ES.value('name[1]', 'VARCHAR(8)') Name,
ES.value('date[1]', 'VARCHAR(8)') Date,
ES.value('test[1]', 'VARCHAR(3)') Test,
ES.value('testing[1]', 'VARCHAR(511)') Testing,
ES.value('testingest[1]', 'VARCHAR(5)') Testingest
INTO #TempTable
FROM #xml.nodes('xmlnodes/path') AS EfficiencyStatement(ES)
-- #Serial Error: Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
SET #IDNumber = (SELECT SerialNumber from #TempTable)
SET #Foreign_ID = (SELECT [Foreign_ID] from [table] WHERE [id-number] = #IDNumber)
MERGE dbo.[table1] AS CF
USING (SELECT IDNumber, Name, Date, Test, Testing, Testingest FROM #TempTable) AS src
ON CF.[id-number] = src.IDNumber
-- ID-Number is unique, and is used to setup the initial referential integrity. Foreign_ID does not exist in the XML files, so we are not matching on that.
WHEN MATCHED THEN UPDATE
SET
CF.[id-number] = src.IDNumber
-- and so on...
WHEN NOT MATCHED THEN
-- Insert statements here
GO
This works for the first table. It does not maintain integrity when updating the other tables via Foreign_ID. Note that SET #Serial has an error, but when I set it to anything else, it will update properly.
I am not fully sure what you are asking here, but if you cannot use the suggested article to enforce references in your XML, there is not really a post-op way for you to do it just in XML.
For Table2+ you can do EXISTS checks against TABLE 1 and process accordingly that way (see Referential integrity issue with Untyped XML in TSQL for example)
The only other way that I can think of is to create "real" tables that represent your schema for table 1, table 2...tableN that have the relevant FKs and insert into them.

insert into 2 joined tables in a single effecient query with mssql

I am trying to insert some data into 2 different tables in my ms sql database (ms sql server 2012).
Consider the following 2 tables, that contains information about polls and their choices.
+----------------+
| Polls |
+----------------+
| pollID | The id of a poll (auto increment)
| memberID | The id of the member, owning the poll
| pollTitle | The title/question of the poll
| date | The date of the poll
+----------------+
+----------------+
| PollChoices |
+----------------+
| pollChoiceID | The id of a poll choice (auto increment)
| pollID | The id of the poll, that include this choice
| pollChoice | The name/title of the poll choice
+----------------+
How can I make a query, inserting the data in the most effecient way?
I can always make 2 queries, but can't really figure out how to do it with a single one.
Óne of the main issues for me in this case is getting the id of the poll when inserting the pollchoices.. how can i get this newly inserted "pollID" (auto increment) and use it in the same query?
Also, is it neccesary to make use of transactions or stored procedures (I've read this somewhere)?
Any help will be greatly appreciated.
I don't think what you are aking is possible.
The best way I think to perform the operation you describe, is to wrap it inside a stored procedure. You can use SCOPE_IDENTITY to get the ID of the previously added record and a TRANSACTION together with a TRY-CATCH block to ensure that both insert queries are executed or none at all.
CREATE PROCEDURE [dbo].[usp_InsertPoll] (
-- sproc declaration here, including the following parameters:
-- #memberID, #pollTitle, #date, #pollChoiceID, #pollChoice
)
AS BEGIN
DECLARE #pollID INT
BEGIN TRANSACTION;
BEGIN TRY
INSERT INTO Polls (memberID, pollTitle, date)
VALUES (#memberID, #pollTitle, #date)
-- Get the last identity value inserted into an identity column in the same scope
SET #pollID = SCOPE_IDENTITY();
INSERT INTO PollChoices(pollChoiceID, pollID, pollChoice )
VALUES (#pollChoiceID, #pollID, #pollChoice)
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
END CATCH;
IF ##TRANCOUNT > 0
COMMIT TRANSACTION;
END
Try this query
declare #npi int
INSERT INTO Polls (memberID, pollTitle, date)
VALUES (#memberID, #pollTitle, #date)
set #npi=
(select top 1 PollId
from Polls
order by PollId desc)
INSERT INTO PollChoices(pollChoiceID, pollID, pollChoice )
VALUES (#pollChoiceID, #npi, #pollChoice)

Resources