Adding conditions to Triggers in SQL Server - sql

I have these tables in my SQL Server database:
CREATE TABLE weather
(
weatherId integer Identity(1,1) primary key,
weatherDate datetime,
rainout BIT,
temperature float,
inchesOfRain float
)
CREATE TABLE weather_audit
(
weatherAuditId integer Identity(1,1) primary key,
weatherId integer,
date datetime,
rainout BIT,
temperature float,
inchesOfRain float
)
CREATE TABLE maintenance
(
maintenanceId integer Identity(1,1) primary key,
maintenanceDescription nvarchar(100),
dateRequested datetime,
dateResolved datetime,
currentStatus nvarchar(20),
estimatedCost decimal,
)
CREATE TABLE maintenence_audit
(
mainteneceAuditId integer Identity(1,1) primary key,
maintenanceId integer,
description nvarchar(100),
dateRequested datetime,
dateResolved datetime,
currentStatus nvarchar(20),
estimatedCost decimal,
updatedOn datetime
)
I want to set up a trigger which fires when a row is inserted into the weather table whose inchesOfRain is > 4. This is what I have now, but it isn't working:
CREATE TRIGGER tr_weather_ForInsertUpdate
ON weather
FOR INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON
IF (SELECT TOP 1 inchesOfRain FROM weather) > 4
INSERT INTO weather_audit (weatherId, weatherDate, rainout, temperature, inchesOfRain)
SELECT
i.weatherId, i.weatherDate, i.rainout, i.temperature, i.inchesOfRain
FROM
Inserted AS I
END
So if I were to insert this
INSERT INTO dbo.weather (weatherDate, rainout, temperature, inchesOfRain)
VALUES ('4/21/2018', '0', '70', '6');
it would add a row to the weather table and also to the weather_audit table since the inches of rain is 6

You need to depend on your Inserted pseudo table to make the decision whether or not to add a row to the audit table - try this:
CREATE TRIGGER tr_weather_ForInsertUpdate
ON weather
FOR INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON
INSERT INTO weather_audit (weatherId, weatherDate, rainout, temperature, inchesOfRain)
SELECT
i.weatherId, i.weatherDate, i.rainout, i.temperature, i.inchesOfRain
FROM
Inserted AS I
WHERE
i.inchesOfRain > 4 -- just insert those rows with inchesOfRain > 4
END

Your trigger is all good, it just needs to be
CREATE TRIGGER tr_weather_ForInsertUpdate
ON weather
FOR INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON
--Modified Code Start
DECLARE #inchesOfRain float
SELECT #inchesOfRain = inchesOfRain FROM inserted
IF #inchesOfRain > 4
BEGIN
---------------Modified COde End
INSERT INTO weather_audit (weatherId, weatherDate, rainout, temperature, inchesOfRain)
SELECT
i.weatherId, i.weatherDate, i.rainout, i.temperature, i.inchesOfRain
FROM
Inserted AS I
END
END
Update: Marc S code snippet is the perfect answer.

Related

How to auto increment for a coulmn in sql server limited to partition [duplicate]

Database question here.
Is it possible to make an autoincrement on a secondary or a thirtiary ID?
I need to make something versionbased, so imagine this:
ID Phrase PhraseID PhraseVersion
1 "" 1 1
2 "" 1 2
3 "" 1 3
4 "" 2 1
PhraseID can be the same number, when added to the database.
If the PhraseID exists, i want PhraseVersion to autoincrement in number.
If the PhraseID doesnt exist, i want PhraseVersion to start over, counting from 1.
I this possible?
I would go with a computed column for PhraseVersion, that will take the count of rows with the same PhraseID and Id lower or equal to the current row.
To do that, you need to create a UDF to calculate the PhraseVersion:
CREATE FUNCTION dbo.GetPhraseVersion (
#PhraseId int,
#id int
)
RETURNS INT
AS
BEGIN
RETURN (
SELECT COUNT(*)
FROM T
WHERE PhraseId = #PhraseId
AND Id <= #id
)
END
GO
Then, Create the table with the computed column:
CREATE TABLE T
(
id int identity(1,1),
PhraseId int,
PhraseVersion as dbo.GetPhraseVersion(PhraseId, id)
)
GO
Now for the test - insert 4 records:
INSERT INTO T (PhraseId) VALUES(1),(1),(1),(2)
Select:
SELECT *
FROM T
Results:
id PhraseId PhraseVersion
1 1 1
2 1 2
3 1 3
4 2 1
You can see a live demo on rextester.
This can be accomplished via an insert trigger on the table:
CREATE TABLE Phrases (
ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
PhraseID INT NOT NULL DEFAULT(0),
PhraseVersion INT NOT NULL DEFAULT(0))
GO
-- ==========================================================================================
-- Author: Donna Landy
-- Create Date: 21 Nov 2019
-- Purpose: To populate the PhraseVersion column (subordinate numeric key) on Insert
-- Note: Must cater for >1 row being inserted when this trigger is called
-- Strategy: Construct a temp table with the rows we need to consider, then update one row at a time
-- ==========================================================================================
CREATE TRIGGER Phrases_Insert ON Phrases AFTER INSERT
AS
BEGIN
DECLARE #ID INT
DECLARE #PhraseID INT
DECLARE #PhraseVersion INT
DECLARE #i INT
DECLARE #iMax INT
-- Create and populate temp table
IF OBJECT_ID('tempdb..#phrases', 'U') IS NOT NULL DROP TABLE #phrases
CREATE TABLE #phrases (i INT IDENTITY(1,1) PRIMARY KEY, ID INT, PhraseID INT)
INSERT INTO #phrases (ID, PhraseID) SELECT ID, PhraseID FROM inserted
-- Scan temp table
SET #i=1
SELECT #iMax=MAX(i) FROM #phrases
WHILE #i <= #iMax BEGIN
-- Fetch PhraseID & PhraseVersion for the row we are going to update
SELECT #ID=ID, #PhraseID=PhraseID FROM #phrases WHERE i=#i
-- Find the highest current Ref
SELECT #PhraseVersion=ISNULL(MAX(PhraseVersion),0) FROM Phrases WHERE PhraseID=#PhraseID
-- Update the row
UPDATE Phrases SET PhraseVersion=#PhraseVersion+1 WHERE ID=#ID
-- Increment loop counter
SET #i+=1
END
-- Remove temp table
IF OBJECT_ID('tempdb..#phrases', 'U') IS NOT NULL DROP TABLE #phrases
END
GO

Handling bulk insert on a table with multiple input sources in SQL

I am performing bulk insert on a table in sql server 2012, at the same time i am picking the last inserted row with max() function and inserting it into another table , how to perform this when my table is getting data from multiple sources because while performing insertion into secondary table there is time delay while insertions are still happening in primary table so next time max() will pick up last updated row and i will loose some rows which are not max() but still inserted into primary table meanwhile.
create table dbo.emp
(
id int primary key identity(1,1),
emp_id int,
name varchar(255),
address varchar(255)
)
create table dbo.empx
(
id int primary key,
emp_id int foreign key references dbo.emp(id),
)
declare #temp int ;
set #temp=1;
while #temp<1000
begin
insert into dbo.emp(emp_id,name,address)values (100+#temp,'Ename'+LTRIM(STR(#temp)),'123 Sample Address'+LTRIM(STR(#temp)));
set #temp=#temp+1;
insert into dbo.empx select max(dbo.emp.id),max(dbo.emp.emp_id) from dbo.emp
end
Use OUTPUT Clause...
CREATE TABLE #empx
(Id INT ,emp_id VARCHAR(50))
DECLARE #temp INT ;
SET #temp=1;
WHILE #temp<1000
BEGIN
INSERT INTO dbo.emp(emp_id,name,address)
OUTPUT INSERTED.Id,INSERTED.emp_id INTO #empx(Id,emp_id)
VALUES (100+#temp,'Ename'+LTRIM(STR(#temp)),'123 Sample Address'+LTRIM(STR(#temp)));
SET #temp=#temp+1;
END
INSERT INTO dbo.empx(Id,emp_id)
SELECT Id,emp_id FROM #empx
Or Use a trigger
CREATE TRIGGER EmpLog
ON dbo.emp
AFTER Insert
AS
BEGIN
SET NOCOUNT ON;
Insert into dbo.empx (id,emp_id) Select id,emp_id from inserted;
END
GO

The INSERT statement conflicted with the constraint

I want to make a date constraint in my table (I use sql server). I want to make sure that the date in one of my columns is later than the current date and time (I know it sounds weird, but it's an assignment so I have no choice). I tried to do it this way:
ALTER TABLE sales ADD CONSTRAINT d CHECK (Date > CURRENT_TIMESTAMP);
but later when inserting DEFAULT into date column I get the following error:
The INSERT statement conflicted with the CHECK constraint "d".
The conflict occurred in database "Newsagents", table "dbo.Sales", column 'Date'.
This is the said table:
CREATE TABLE Sales (
ID INT IDENTITY(1,1) NOT NULL ,
ClientID INT REFERENCES Client(ClientID),
ProductNumber CHAR(10) REFERENCES Product(ProductNumber),
Quantity INT NOT NULL,
Price FLOAT NOT NULL ,
Date TIMESTAMP NOT NULL,
PRIMARY KEY ( ID )
and this how I insert my data into Sales column and get the constraint conflict:
DECLARE #counter INT
DECLARE #quantity int
DECLARE #prodNum varchar(20)
SET #counter = 0
WHILE #counter < 10
BEGIN
SET #quantity = (select FLOOR(RAND()*100))
SET #prodNum = (select TOP 1 ProductNumber from Product Order by NEWID())
insert into Sales (ClientID, ProductNumber, Quantity, Price, Date )
values(
(select TOP 1 ClientID from Client Order by NEWID()),
(select #prodNum),
(select #quantity),
((select #quantity)*(select TOP 1 Price from Product where ProductNumber = #prodNum)),
DEFAULT
)
SET #counter = #counter + 1
END
Is there a different way to do this? Or am I doing something wrong?
ALTER TABLE sales ADD CONSTRAINT d CHECK (Date > GETDATE());
change the Date column to datetime

Insert Records if not exist and then update with identity in one query

I have 2 tables.
create table Sales
(CustomerKey int
,ProductKey int
,CustomersProductsKey int
,SalesAmount decimal(19,4))
Create Table CustomersProducts
(CustomersProductsKey int IDENTITY(1,1),
CustomerKey int,
ProductKey int,
Attribute1 int,
Attribute2 varchar(max))
Currently when I add data to the sales table, I need to insert any new customerkey productkey combinations into the CustomersProducts table and then update the sales table with the resulting CustomersProductsKey identity value. This works.
Is there anyway that I can do this in one step? I don't know if a Merge can do an insert and update on the same if not matched step.
I also could be just looking at this the wrong way as well.
Thanks,
EDIT:
As you can imagine, the fact that I need to use a surrogate key is part of the design. It's needed for a BO report. Otherwise there would really be no need for CustomersProductsKey at all.
If add only one step to make it work,
I think we need create a another table and create trigger on the new table and CustomersProducts
create table CustomersSalesProducts
(CustomerKey int
,ProductKey int
,SalesAmount decimal(19,4)
,Attribute1 int
,Attribute2 varchar(max))
create trigger test1 on CustomersSalesProducts After Insert
as
begin
insert Sales select CustomerKey , ProductKey , 0, SalesAmount from inserted
insert CustomersProducts select CustomerKey , ProductKey , Attribute1, Attribute2 from inserted
end
go
create trigger test2 on CustomersProducts after insert
as
begin
Update Sales set CustomersProductsKey = inserted.CustomersProductsKey
from inserted , Sales
where inserted.CustomerKey = Sales.CustomerKey
and inserted.ProductKey = Sales.ProductKey
end
go
Test Script:
insert CustomersSalesProducts select 3,3,300,3,'Attribute2'

Error Using Temporary Tables With Same Names SQL Server

Here is my scenario. (Following is my stored proc taking #date as an input parameter)
DECLARE #date DATE
If object_id('tempdb..#TempList') is not null drop table #TempList
go
Create table #TempList (MILL_NO VARCHAR(7), SHIP_DATE DATE, BL_STATUS NCHAR(1),
FOOTAGE DECIMAL(12,4))
If #date IS NULL
Insert into #TempList
Select mill_no, null, bl_status,footage from fn_A(0,0)
Select * from #TempList
If object_id('tempdb..#TempList') is not null drop table #TempList
go
Create Table #TempList (MILL_NO VARCHAR(7), SHIP_DATE DATE, BL_STATUS NCHAR(1),
FOOTAGE DECIMAL(12,4))
If #date IS NOT NULL
Insert into #TempList
Select * from fn_B(0,'06/06/2006')
Select * from #TempList
I figured out from one of the posts that I cannot use temporary tables with same names unless I inclide a GO. However, including GO is not taking the parameters I try to pass.
Is there an alternate approach to eliminate this error?
Based on the procedure supplied, you could use TRUNCATE TABLE instead.
I don't see a point dropping and recreating a table if all you want to do is quickly remove the records.
EDIT
You don't drop and recreate your table with the same name; instead of this code:
If object_id('tempdb..#TempList') is not null drop table #TempList go
Create Table #TempList (
MILL_NO VARCHAR(7),
SHIP_DATE DATE,
BL_STATUS NCHAR(1),
FOOTAGE DECIMAL(12,4)
)
Just do this:
TRUNCATE TABLE #TempList
if object_id('tempdb..#TempList') is allways NULL because #TempList is not the name that results created on tempdb's sysobjects table when you do create table #TempList
EDIT
what about this:
CREATE PROC PIRULO(#date as DATE) as
Create table #TempList (MILL_NO VARCHAR(7), SHIP_DATE DATE, BL_STATUS NCHAR(1), FOOTAGE DECIMAL(12,4))
IF #date IS NULL
Insert into #TempList
Select mill_no, null, bl_status,footage from fn_A(0,0)
ELSE
Insert into #TempList
Select * from fn_B(0,'2006/06/06') -- also I changed date order.
Select * from #TempList
I struggled all these days to insert values into same temporary table when a given condition is met.
Given that I am on a project migrating FoxPro(which has cursors declared all over) to SQL Server this small logic needed to be implemented in multiple stored procs.
Finally, what I learned out of this is - Think Straight Before Trying Anything Different (suggestion to my co-beginners in DB migration).
DECLARE #date DATE
SET #date = '06/06/2006' --#date = null
If object_id('tempdb..#TempList') is not null drop table #TempList
Create table #TempList (MILL_NO VARCHAR(7), SHIP_DATE DATE, BL_STATUS NCHAR(1),
FOOTAGE DECIMAL(12,4))
If #date = null
-- Here I am inserting null in place of Ship_Date because fn_A returns only 3 columns in my case
Insert into #TempList
Select mill_no, null, bl_status,footage from fn_A(0,0)
--Select * from #TempList
else
Insert into #TempList
Select * from fn_B(0,#date)
Select * from #TempList
Thanks everyone for giving your inputs. Hope this helps somebody.