I have a program that captures orders into two SQL tables. One is a header table with order info that has OrderID as auto increment PK. The other is a details table which captures vendor, part_number, and quantity data.
I have an after insert trigger on the header table which is suppose to update a table in another database with order info and a total quantity of parts in that order.
Everything works fine except when the trigger goes to calculate the total number of parts in the details table. What is happening is the trigger fires before the data is written to the details table and always returns NULLs for the total quantity of parts. What's the best workaround for this? Am I using a wrong trigger?
OrderHeader Table
ID int
TransCode int
CustID int
StoreID int
Date datetime
OrderDetail
OrderID int
MFG nchar(3)
PartNumber nchar(35)
Qty int
ALTER TRIGGER [dbo].[trig_Update_PRIME]
on [Cleansweep].[dbo].[OrderHeader]
after insert
AS
BEGIN
declare #ID as int
declare #DateInput as date
declare #CustID as int
declare #transcode as int
declare #storeID as int
declare #TotItems as int
select #ID = (select ID from inserted)
select #DateInput = (select date from inserted)
select #CustID = (select CustID from inserted)
select #transcode = (select TransCode from inserted)
select #storeID = (select StoreID from inserted)
set #TotItems = ( select SUM(qty) FROM [Cleansweep].[dbo].[OrderDetail] where OrderID = #ID )
if #transcode = 0
begin
insert into PRIME.dbo.PRIME_RepCalls (Customer, Cores)
values (#CustID, #TotItems)
end
if #transcode = 1
begin
insert into PRIME.dbo.PRIME_RepCalls (Customer, NewReturns)
values (#CustID, #TotItems))
end
END
I belive that there two insert process in your DB - first one is when insert into Header table, and the second when is insert to Details table.
If you do insert Header first - than your trigger works before details of your documents were inserted in Db. That's why SUM can return NULL
Related
I'm using databases to connect with PowerApps and PowerBI and I had a question about triggers.
I have a table (Table A) that contains three columns: ID, TotalQty & Date. I would like to create a trigger based on the three main row actions: Insert, Delete & Update.
Example: New row is inserted into Table A from PowerApps (ID = 1000000 & TotalQty = 3 & Date = Today)
This should fire the trigger three times to insert a row into Table B (with rows ID, QrderQty, and Date):
ID = 1000000, OrderQty = 1 of 3, Date = Today
ID = 1000000, OrderQty = 2 of 3, Date = Today
ID = 1000000, OrderQty = 3 of 3, Date = Today
Similarly, if the date column is updated on Table A for this row, I need the three corresponding rows to update their respective date values as well. Or if the row in Table A is deleted, I need the three rows to be removed.
Could anybody give me an example query of this?
You need to create a Trigger first this link contains some tutorial on how to create a trigger.
Then according to your question you need to do some kind of loop for generating values for the OrderQty, I have done a while loop in this answer that exists as part of TSQL
Assuming that you create TableA and TableB with following schemas:
create table [dbo].[TableA]
(
ID integer not null,
TotalQty integer not null,
Date Date not null
)
go
create table [dbo].[TableB]
(
ID integer not null,
OrderQty nvarchar(10) not null,
Date Date not null
)
go
You can create the trigger as follows to do the job for you:
CREATE TRIGGER [dbo].[InsertFromAToB]
ON [dbo].[TableA]
AFTER INSERT
AS
BEGIN
Declare #counter as integer;
set #counter=1;
Declare #qty as integer;
set #qty = (Select TotalQty from inserted);
While(#counter <= #qty)
Begin
Insert into TableB(ID,OrderQty,Date) select ID,(CONVERT(nvarchar(10),#counter) + ' of ' + Convert(nvarchar(10),#qty)) as OrderQty,Date From inserted
set #counter=#counter+1;
END
END
I'm setting up a storekeeping program in which I have 2 tables, one for products and another for materials.
In the products table, each product has several materials. Is there any way to select these rows and decrement materials availability?
I tried to use a foreach loop but I couldn't implement it and store each rows data
CREATE TABLE materials
(
materialID INT PRIMARY KEY IDENTITY,
materialName NVARCHAR(100) NULL,
materialAmount INT NULL,
)
CREATE TABLE productStack
(
ID INT PRIMARY KEY IDENTITY,
productsID INT NULL,
materialID INT NULL,
amount INT NULL
)
GO;
CREATE PROCEDURE updateMaterials
(#ID INT,
#AMOUNT INT)
AS
BEGIN
UPDATE materials
SET materialAmount = (materialAmount - #AMOUNT)
WHERE materialID = #ID
END
You could use a temp table and while loop such as :
SELECT * INTO #TEMP_A FROM PRODUCTSTACK
DECLARE #ID INT,#AMOUNT INT
WHILE EXISTS(SELECT * FROM #TEMP_A)
BEGIN
SET #ID = (SELECT TOP 1 ID FROM PRODUCTSTACK)
SET #AMOUNT = (SELECT TOP 1 AMOUNT FROM PRODUCTSTACK WHERE ID = #ID)
EXEC UPDATEMATERIALS #ID,#AMOUNT
DELETE FROM #TEMP_A WHERE ID = #ID
END
As we have no sample to base this on, this is a guess. Like I said, however, seems like a table-type parameter would do this:
CREATE TYPE dbo.MaterialAmount AS table (ID int, Amount int);
GO
CREATE PROC dbo.UpdateMaterials #Materials dbo.MaterialAmount READONLY AS
BEGIN
UPDATE M
SET materialAmount = MA.Amount
FROM dbo.materials M
JOIN #Materials MA ON M.materialID = MA.ID;
END;
GO
--Example of usage:
DECLARE #Materials dbo.MaterialAmount;
INSERT INTO #Materials
VALUES(1,100),
(5,20);
EXEC dbo.UpdateMaterials #Materials;
I have a Product table and a ProductPriceLog table to maintain price of a product.
Assume product table has 3 fields [ProductId, ProductName, ProductPrice] and price history just has 2 fields [ProductId(fk), productprice]
I am attempting to write a trigger which would add a log entry each time we update the price of the product.
CREATE TRIGGER price_change_trg
ON PRODUCT
AFTER INSERT OR UPDATE OF ProductPrice ON PRODUCT
BEGIN
INSERT INTO ProductPriceLog ( #idd , ProductPrice)
END;
Specifically, how to I fetch the productId from the Product table's row which was updated?
You can refer my solution. Hope to help, my friend.
CREATE TRIGGER price_change_trg
ON PRODUCT
AFTER INSERT, UPDATE
AS
BEGIN
declare #productId int;
declare #ProductPrice decimal(10,2);
select #productId = i.ProductId from inserted i;
select #ProductPrice = i.ProductPrice from inserted i;
INSERT INTO ProductPriceLog VALUES ( #productId , #ProductPrice )
END;
If i understood your question you must use inserted table that has data that just inserted in product tableselect #idd=productid from inserted
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
I'm writing a SQL script to generate test data for our database. I'm generating the data in table variables (so I can track it later) and then inserting it into the real tables. The problem is, I need to track which rows I've added to the parent table, so that I can generate its child data later on in the script. For example:
CREATE TABLE Customer (
CustomerId INT IDENTITY,
Name VARCHAR(50)
)
CREATE TABLE Order (
OrderId INT IDENTITY,
CustomerId INT,
Product VARCHAR(50)
)
So, in my script, I create equivalent table variables:
DECLARE #Customer TABLE (
CustomerId INT IDENTITY,
Name VARCHAR(50)
) -- populate customers
DECLARE #Order TABLE (
OrderId INT IDENTITY,
CustomerId INT,
Product VARCHAR(50)
) -- populate orders
And I generate and insert sample data into each table variable.
Now, when I go to insert customers from my table variable into the real table, the CustomerId column in the table variable will become meaningless, as the real table has its own identity seed for its CustomerId column.
Is there a way I can track the new identity of each row inserted into the real table, in my table variable, so I can use a proper CustomerId for the order records? Or, is there a better way I should be going about this?
(Note: I originally started with an application to generate the test data, but it ran too slow during insert as > 1,000,000 records need to be generated.)
WHy do you need identity values on the table variables? If you use just int, you can isnert the ids after the insert is done. Grab them using the output clause. YOu might need an input values and an output values table varaiable to get this just right like this:
DECLARE #CustomerInputs TABLE (Name VARCHAR(50) )
DECLARE #CustomerOutputs TABLE (CustomerId INT ,Name VARCHAR(50) )
INSERT INTO CUSTOMERS (name)
OUTPUT inserted.Customerid, inserted.Name INTO #CustomerOutputs
SELECT Name FROM #CustomerInputs
SELECT * from #CustomerOutputs
You can insert the data to the table with a cursor and use the built-in function SCOPE_IDENTITY() to get the last id which was inserted in the current scope (by your script).
See this MSDN article for more information on SCOPE_IDENTITY.
Here is one way of doing it. If you can use it depends on your situation. You should not do it in production environment when users use your db.
-- Get the next identity values for Customer and Order
declare #NextCustomerID int
declare #NextOrderID int
set #NextCustomerID = IDENT_CURRENT('Customer')+1
set #NextOrderID = IDENT_CURRENT('Order')+1
-- Create tmp tables
create table #Customer (CustomerID int identity, Name varchar(50))
create table #Order (OrderID int identity, CustomerID int, Product varchar(50))
-- Reseed the identity columns in temp tables
dbcc checkident(#Customer, reseed, #NextCustomerID)
dbcc checkident(#Order, reseed, #NextOrderID)
-- Populate #Customer
-- Populate #Order
-- Allow insert to identity column on Customer
set identity_insert Customer on
-- Add rows to Customer
insert into Customer(CustomerId, Name)
select CustomerID, Name
from #Customer
-- Restore identity functionality on Customer
set identity_insert Customer off
-- Add rows to Order
set identity_insert [Order] on
insert into [Order](OrderID, CustomerID, Product)
select OrderID, CustomerID, Product
from #Order
set identity_insert [Order] off
-- Drop temp tables
drop table #Customer
drop table #Order
-- Check result
select * from [Order]
select * from Customer
The way I'd do it its first obtain the MAX(CustomerId) from your Customer Table. Then I'd get rid of the IDENTITY column on your variable table and do my own CustomerId using ROW_NUMBER() and the MaxCustomerId. It should be something like this:
DECLARE #MaxCustomerId INT
SELECT #MaxCustomerId = ISNULL(MAX(CustomerId),0)
FROM Customer
DECLARE #Customer TABLE (
CustomerId INT,
Name VARCHAR(50)
)
INSERT INTO #Customer(CustomerId, Name)
SELECT #MaxCustomerId + ROW_NUMBER() OVER(ORDER BY SomeColumn), Name
FROM YourDataTable
Or insert the values on a temp table, so you can use the same ids to fill your Order table.