I'm currently using this trigger in SQL server. Out of 16,000 orders, this is working 99.9% of the time. I've noticed that 0.1% of the time I get orders where the trigger does not run correctly and the UPDATE to the stock isn't made. I think what's happening is that the operation is getting locked while trying to find all the tables in the INNER JOIN and moves on to the second UPDATE operation while it's waiting for the first one to finish. It updates the stock_adjusted to 1 then comes back to the first UPDATE but now that stock_adjusted is 1 it no longer qualifies for the WHERE clause and doesn't update the stock. The delay was added to try and address the "async" issue.
I've read that SQL server is synchronous so I'm wondering if such a thing is even possible?
ALTER TRIGGER [dbo].[adjustStock]
ON [dbo].[payment]
AFTER INSERT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for trigger here
-- run query to adjust stock on product
UPDATE product WITH(ROWLOCK)
SET stock = stock - item.qty
FROM product
INNER JOIN item ON product.id = item.product
INNER JOIN cart WITH(NOLOCK) ON item.cart = cart.id
INNER JOIN inserted i ON cart.id = i.cart
WHERE item.stock_adjusted = 0 AND
product.open_box = 1;
WAITFOR DELAY '00:00:01';
-- mark line item as stock adjusted
UPDATE item
SET item.stock_adjusted = 1
FROM item
INNER JOIN cart WITH(NOLOCK) ON item.cart = cart.id
INNER JOIN product ON item.product = product.id
INNER JOIN inserted i ON cart.id = i.cart
WHERE item.stock_adjusted = 0 AND
product.open_box = 1;
END
Related
I have to retrieve some data from a MSSQL 2014 server through a propriatery application which then uses an odbc data source. The only thing I can modify is the query the application uses. I cannot modify the application or how the application handles the results.
The following query is doing what I want if I execute it directly e.g. in Heidi.
USE MY_DB;
BEGIN TRANSACTION
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
DECLARE #myvar1 INT = 2;
DECLARE #myvar2 INT = 2;
PRINT #myvar1;
SELECT TOP 20 [vwPGIA].[OrNum],[vwPGIA].[DBM],[vwPGIA].[MBM],[vwPGIA].[MN],[NOMID],[Priority],SUBSTRING([Comment],0,254) AS Comment,[TLSAP],[Box],[SequenceNumber]
INTO #tmp_tbl
FROM [MY_DB].[dbo].[vwPGIA]
INNER JOIN [MY_DB].[dbo].[tblDLA] ON [dbo].[tblDLA].[OrNum]=[dbo].[vwPGIA].[OrNum]
INNER JOIN [dbo].[tblMDM] ON [vwPGIA].[MBM]=[tblMDM].[MBM]
WHERE ([TLSAP] = #myvar1)
AND [vwPGIA].[MBM] NOT IN (SELECT [MBM] FROM [MY_DB].[dbo].[vwDPS])
AND [vwPGIA].[OrNum] NOT IN (SELECT [OrNum] FROM [MY_DB].[dbo].[vwDPS] WHERE [MY_DB].[dbo].[vwDPS].[TLR] <> #myvar1)
ORDER BY [SequenceNumber];
SELECT TOP 1 [OrNum],[DBM],[MBM],[MN],[NOMID],[Priority],[Comment],[TLSAP],[Box],[WTT],[SequenceNumber]
FROM #tmp_tbl
INNER JOIN [dbo].[tblTBN] ON [Box]=[BoxN]
WHERE ([WTT]=#myvar2)
ORDER BY [SequenceNumber];
INSERT INTO [dbo].[tblDPS]
(OrNum,DBM,MBM,State,StateStartTime,Info,TLR)
SELECT TOP 1 [OrNum],[DBM],[MBM],'1',GETDATE(),'info',#myvar1
FROM #tmp_tbl
INNER JOIN [dbo].[tblTBN] ON [Box]=[BoxN]
WHERE ([WTT]=#myvar2)
ORDER BY [SequenceNumber]
;
DROP TABLE #tmp_tbl;
COMMIT TRANSACTION
Running this through the ODBC interface results in an empty result. The problem seems to be, that I am doing a batch request which results in multiple result sets. The application probably only handles the first result set or maybe cannot handle more than one result set.
Finally the question: Is there a way or workaround to reduce the result sets to only the one returned by the SELECT TOP 1 ... part?
I have this simple view set in place which joins 3 different tables and displays data within my program. I noticed however, that I can update columns from the table PRODCODE like so:
update PRODCODE_VW set MAXGH = 5.00 where ORIGREC = 114406 --RETURNS 1 UPDATED
And it will update 1 record. However, when I do an update on ExpectedLevels, it will update 0 rows. I'm assuming this is because it is left joined. Is there a way to get around this by only altering the way the view is set up and not the update statement?
update PRODCODE_VW set EMIN = 5.00 where ORIGREC = 114406 --RETURNS 0 UPDATED
This is the view I have set in place:
IF EXISTS (SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.VIEWS
WHERE TABLE_NAME = N'PRODCODE_vw')
DROP VIEW PRODCODE_vw
GO
CREATE VIEW dbo.PRODCODE_vw
AS
SELECT PRODCODE.STUFF,PRODCODE.ORIGREC,PRODCODE.PRODCODE,PRODCODE.PRODNAM,
C1JMASTER.C1JTYPE, EXPECTEDLEVELS.EMIN, EXPECTEDLEVELS.EMAX
FROM PRODCODE (NOLOCK)
LEFT JOIN C1JMASTER (NOLOCK)
ON PRODCODE.c1jcode = C1JMASTER.c1jcode
LEFT JOIN EXPECTEDLEVELS
ON PRODCODE.PRODCODE = EXPECTEDLEVELS.PRODCODE
GO
When you select ORIGREC = 114406 from the view, is there any value for EMIN, or it is null? Most likely, the ORIGREC 114406 does not have a matching record in the left joined table. Your update statement effectively filters on WHERE ORIGREC = 114406 AND PRODCODE.PRODCODE = EXPECTEDLEVELS.PRODCODE
You can target the left joined tables as long as your affected columns are all sourced from the same table in the view. However, if the left join would not return a EXPECTEDLEVELS row for the ORIGREC, your rows updated will be zero, because there is no matching row in the target table.
I have to maintain an integer updated consistently, so I want to make a trigger (or something related) that calculates the data and keeps it updated every time.
This is:
I have a table with the attributes: Working, Balance and Must_Work. I want to keep Working and Balance updated.
Working is the SUM of values stored in other tables.
Balance is the difference between Working and Must_work.
So far I've got this
CREATE TRIGGER calculateWorkload
AFTER INSERT ON SEMESTER_WORKLOAD
FOR EACH ROW MODE DB2SQL
BEGIN
UPDATE SEMESTER_WORKLOAD SET WORKING =
(SELECT SUM(WORKING_HOURS) FROM
(SELECT DISTINCT ID_MODULE, WORKING_HOURS FROM LECTURE WHERE ID_SW = ????))
WHERE ID_SW = ????
INSERT INTO t1.ACCOUNT (SELECT t1.ACADEMIC_SEMESTER, t1.ID_LECTURER, t1.WORKLOAD, t3.WORKING_HOURS FROM SEMESTER_WORKLOAD t1
INNER JOIN LECTURER t2 ON t1.ID_LECTURER = t2.ID_LECTURER
INNER JOIN LECTURE t3 ON t1.ID_SW = t3.ID_SW WHERE
END
Problem is, how can I make it do it for every value and not only for a given value? And after that, how do I make it so it keeps the data updated?
I have a very suspicious feeling that this update trigger is updating ALL rows on the target, not just those that satisfy the "update(shape)" test. Performance was fine 'till I added the second operation. A single spatial join occurs much faster, and this is a not a spatial index issue, as well, there are only a few records in this dataset.
ALTER TRIGGER [dbo].[GRSM_WETLANDS_Point_GIS_tbl_locations_update]
ON [dbo].[GRSM_WETLANDS_POINT]
after update
AS
BEGIN
SET NOCOUNT ON;
if UPDATE (shape)
update GRSM_WETLANDS_Point
set X_Coord =CASE WHEN u.shape.STDimension() = 2 THEN u.shape.STCentroid().STX ELSE u.shape.STEnvelope().STCentroid().STX END,
Y_Coord =CASE WHEN u.shape.STDimension() = 2 THEN u.shape.STCentroid().STY ELSE u.shape.STEnvelope().STCentroid().STY END
from inserted i
inner join GRSM_WETLANDS_POint u on i.GIS_Location_ID = u.GIS_Location_ID;
--second spatial operation
update GRSM_WETLANDS_Point
set QuadName = grsm.dbo.USGS_24K_TOPOMAP_BOUNDARIES.name
FROM GRSM_WETLANDS_POint i
inner join grsm.dbo.USGS_24K_TOPOMAP_BOUNDARIES
on i.GIS_Location_ID = i.GIS_Location_ID
WHERE (USGS_24K_TOPOMAP_BOUNDARIES.Shape.STContains(i.SHAPE) = 1) ;
end
Is my suspicion right?
Upated: Based on suggestion from Aaron...solves the fire on all rows issue.
update GRSM_WETLANDS_Point
set QuadName = grsm.dbo.USGS_24K_TOPOMAP_BOUNDARIES.name
FROM inserted i inner join GRSM_WETLANDS_POint u on i.GIS_Location_ID = u.GIS_Location_ID
left outer join grsm.dbo.USGS_24K_TOPOMAP_BOUNDARIES
on i.GIS_Location_ID = i.GIS_Location_ID
WHERE (USGS_24K_TOPOMAP_BOUNDARIES.Shape.STContains(i.SHAPE) = 1);
If Shape can't be NULL, a better way to see if it has changed is to check if the values in inserted and deleted are different. For example:
IF EXISTS
(
SELECT 1 FROM inserted AS i
INNER JOIN deleted AS d
ON i.GIS_Location_ID = d.GIS_Location_ID
WHERE i.Shape.STEquals(d.Shape) = 0
)
BEGIN
...
END
If Shape is nullable then you just have to add more conditions there to check, e.g.
WHERE
(
(i.Shape IS NULL AND d.Shape IS NOT NULL
OR (i.Shape IS NOT NULL AND d.Shape IS NULL)
OR (i.Shape.STEquals(d.Shape) = 0)
)
(You might not care if Shape has been updated to NULL, I'm just illustrating how to test for that case.)
Since the operation can occur on multiple rows, and this condition will only identify that at least one such update has occurred (but not that ALL rows meet the condition), it may be better to have your operations include similar criteria in the WHERE clause. In fact I think you can perform both updates in a single operation, e.g.
ALTER TRIGGER [dbo].[GRSM_WETLANDS_Point_GIS_tbl_locations_update]
ON [dbo].[GRSM_WETLANDS_POINT]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE p SET
X_Coord = CASE WHEN i.shape.STDimension() = 2
THEN i.shape.STCentroid().STX
ELSE i.shape.STEnvelope().STCentroid().STX
END,
Y_Coord = CASE WHEN i.shape.STDimension() = 2
THEN i.shape.STCentroid().STY
ELSE i.shape.STEnvelope().STCentroid().STY
END,
QuadName = COALESCE(b.name, p.QuadName)
FROM
dbo.GRSM_WETLANDS_Point AS p
INNER JOIN
inserted AS i
ON i.GIS_Location_ID = p.GIS_Location_ID
LEFT OUTER JOIN grsm.dbo.USGS_24K_TOPOMAP_BOUNDARIES AS b
ON b.Shape.STContains(i.Shape) = 1
WHERE EXISTS
(
SELECT 1 FROM inserted AS i2
INNER JOIN deleted AS d
ON i2.GIS_Location_ID = d.GIS_Location_ID
WHERE i2.GIS_Location_ID = i.GIS_Location_ID
AND i2.Shape.STEquals(d.Shape) = 0
-- ...and NULL handling if necessary
);
END
GO
In general, you seem to be having a lot of troubles implementing triggers, and make a lot of guesses about how the syntax should work. Have you considered forcing data updates to occur via stored procedures, where you can control all of this business logic but eliminate the complexity that the inserted and deleted pseudo tables add?
if UPDATE (shape) will fire even if the values don't change, if the column is present in the update statement it will fire
And you are not joining with INSERTED in your second update
Trying to execute an update of a single field where the current db wants to pull values from last night's backup. Should be something close to:
update myTable
set status = (select status
from .lastBackup.dbo.myTable as t
where t.idField = idField)
Help?
Try this:
update t
set status = b.status
from myTable t
inner join lastBackup.dbo.myTable b
on t.idField = b.idField