Trigger after update not working - sql

I'm struggling to get my 'after update' trigger to work properly.
As seen from my simple query, the sum of the production_work matches the sum of the order elements.
# select ident,ud,dp,swrv,sh,jmsw,sw,prrv,mhsw,bmsw,mp,pr,st,completed from orders;
ident | ud | dp | swrv | sh | jmsw | sw | prrv | mhsw | bmsw | mp | pr | st | completed
-------+----+----+------+----+------+----+------+------+------+----+----+----+-----------
2 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | f
(1 row)
# select * from production_work;
ident | order_id | producer_id | day | ud | dp | swrv | sh | jmsw | sw | prrv | mhsw | bmsw | mp | pr | st
-------+----------+-------------+------------+----+----+------+----+------+----+------+------+------+----+----+----
5 | 2 | 1 | 2013-08-09 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0
6 | 2 | 2 | 2013-08-09 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0
(2 rows)
I'm trying to set the 'completed' to true if the sum of the work elements match the order element by using this trigger:
CREATE OR REPLACE FUNCTION update_order_completion_status() RETURNS trigger AS
$BODY$
BEGIN
WITH w AS (
SELECT SUM(ud) AS ud, SUM(dp) AS dp, SUM(swrv) AS swrv, SUM(sh) AS sh, SUM(jmsw) AS jmsw, SUM(sw) AS sw, SUM(prrv) AS prrv,
SUM(mhsw) AS mhsw, SUM(bmsw) AS bmsw, SUM(mp) AS mp, SUM(pr) AS pr, SUM(st) AS st
FROM production_work
WHERE order_id = OLD.order_id
), o AS (
SELECT ud, dp, swrv, sh, jmsw, sw, prrv, mhsw, bmsw, mp, pr, st
FROM orders
WHERE ident = OLD.order_id
)
UPDATE orders
SET completed = (w.ud = o.ud AND w.dp = o.dp AND w.swrv = o.swrv AND w.sh = o.sh AND w.jmsw = o.jmsw AND w.sw = o.sw AND
w.prrv = o.prrv AND w.mhsw = o.mhsw AND w.bmsw = o.bmsw AND w.mp = o.mp AND w.pr = o.pr AND w.st = o.st)
WHERE ident = OLD.order_id;
END;
$BODY$ LANGUAGE plpgsql ;
CREATE TRIGGER update_order_completion_status_trigger
AFTER UPDATE OF ud, dp, swrv, sh, jmsw, sw, prrv, mhsw, bmsw, mp, pr, st
ON production_work
FOR EACH ROW EXECUTE PROCEDURE update_order_completion_status();
I'm not getting any error messages when I do the update to the production_work table, but as you can see the completed column is not being set to true.

The SQL query in your trigger func is needlessly complicated and also incorrect (missing FROM clause in UPDATE). A simplified, correct version would be:
UPDATE orders o
SET completed = (w.ud = o.ud AND w.dp = o.dp AND ...)
FROM (
SELECT SUM(ud) AS ud, SUM(dp) AS dp, ...
FROM production_work
WHERE order_id = NEW.order_id
) w
WHERE o.ident = NEW.order_id
AND o.completed <> (w.ud = o.ud AND w.dp = o.dp AND ...);
Assuming completed NOT NULL.
Note, I am using NEW, not OLD, since you want to use the new values.
But if production_work.order_id can change, you'd have to update orders for OLD and NEW.
The last line is to prevent empty updates.
Alternative: VIEW
If you don't need the redundant column completed to optimize performance, I would consider a VIEW instead:
CREATE VIEW order_plus AS
SELECT o.*, (SUM(w.ud) = o.ud AND SUM(w.dp) = o.dp AND ...) AS completed
FROM orders o
JOIN production_work w ON o.ident = w.ident
GROUP BY o.ident;
Assuming ident PRIMARY KEY.

Related

Filtering out a row from return value

I am currently creating a top 5 list which is ranked by value. I am passing #InfoId = 12 which is returning the values as I am expecting. But I do not want to show the row that #InfoId = #InformationId
So for example, If I pass parameter 12 as InfoId; I do not want to show the first row in the table example below which `InformationId = 12.
Here is an example of the return I am getting when running my select query
+------------------------------------+
| StateId InformationId CurrentValue |
+------------------------------------+
| 1 12 453231 |
| 1 10 394721 |
| 1 341 309425 |
| 1 21 308431 |
| 1 73 301342 |
+------------------------------------+
Here is an example of what I am expecting:
+------------------------------------+
| StateId InformationId CurrentValue |
+------------------------------------+
| 1 10 394721 |
| 1 341 309425 |
| 1 21 309321 |
| 1 73 308431 |
| 1 62 301342 |
+------------------------------------+
Here is my code:
#StateId int
, #InfoId int
, #CurrentValue int -- My new parameter
AS
BEGIN
select top 5
i.StateId,
i.InformationId,
i.CurrentValue,
from dbo.tblInformation i
inner join dbo.tblInformationAnalysis ia
on ia.StateId= i.StateId
and ia.InformationId= i.InformationId
and ia.CurrentValue= i.CurrentValue
inner join (
select
StateId,
InformationId,
CurrentValue,
from dbo.tblInformationAnalysis
where StateId = #StateID
group by StateId,
InformationId,
CurrentValue,
) via
on via.stateId= ia.stateid
and via.informationId= ia.informationId
and via.CurrentValue= ia.CurrentValue
inner join dbo.tblRetailData r
on r.stateId= i.stateid
and r.informationId= i.informationId
and r.CurrentValue= i.CurrentValue
inner join dbo.tblCounty c
on c.stateId= r.stateid
and c.informationId= r.informationId
and c.CurrentValue = 1
inner join dbo.tblRetailDataPublished rp
on rp.stateId= i.stateId
and rp.informationId= i.informationId
and rp.CurrentValue = i.CurrentValue
inner join dbo.RetailCollection rc
on rc.stateId= i.stateId
and rc.informationId= i.informationId
where i.StateId= #StateId
and p.InformationId = #InfoId
END

Conditional calculation in SELECT SQL Server

I have Question_Data table as below:
------------------------------
QuestionID | Attempts | Wrong
------------------------------
1 | 20 | 7
2 | 1 | 4
3 | 14 | 2
4 | 30 | 5
When a request is received, I process it and it results in a table called, "Responses Table":
(Sample Responses Table)
---------------------
QuestionID | Response
---------------------
1 | T
2 | F
3 | F
4 | F
5 | T
Now what i want is that i want to update "Question_Data" table on the basis of "Responses" table as:
New "Question_Data" table:
------------------------------
QuestionID | Attempts | Wrong
------------------------------
1 | 21 | 7
2 | 2 | 5
3 | 15 | 3
4 | 31 | 6
5 | 1 | 0
I checked QuestionID in "Question_Data" table and if it exists in "Question_Data" table I incremented it "Attempts" and if response is "F" increment "Wrong".
But if QuestionID is not present in "Question_Data" table. Insert a new row with same QuestionID and increment its "Attempts" to 1 and if response is "T" set its wrong to 0.
I am new to SQL. Any help will be appreciated.
UPDATE Q
SET Q.Attempts = Q.Attempts + 1 ,
Q.Wrong = Q.Wrong + ( CASE WHEN R.Response = 'F' THEN 1
ELSE 0
END )
FROM Question_Data Q
INNER JOIN Responses_Table R ON Q.QuestionID = R.QuestionID
After update if data is not present in Question_Data run this query
INSERT INTO Question_Data
(QuestionID ,
Attempts ,
Wrong)
SELECT
R.QuestionID ,
1,
(CASE WHEN R.Response = 'F' THEN 1
ELSE 0
END) Wrong
FROM
Responses_Table R
LEFT JOIN
Question_Data Q ON Q.QuestionID = R.QuestionID
WHERE
Q.QuestionID IS NULL
Assuming that the QuestionID is unique in both the Questions_Data and Responses table, you could use the update-join syntax:
UPDATE qd
SET Attempts = Attempts + 1,
Wrong = Wrong + CASE Response WHEN 'F' THEN 1 ELSE 0 END
FROM Questions_Data qd
JOIN Reponse r ON qd.QuestionID = r.QuestionID

row counter with condition in two different columns

I have the following tables with sport results (e.g. football):
tblGoals (RowId, GameRowIdm PlayerRowId, TeamRowId, GoalMinute)
RowId | GameRowId | PlayerRowId | TeamRowId | GoalMinute
--------------------------------------------------------
1 | 1 | 1 | 1 | 25
2 | 1 | 2 | 2 | 45
3 | 1 | 3 | 1 | 66
tblPlayers (RowId, PlayerName)
RowId | PlayerName
------------------
1 | John Snow
2 | Frank Underwood
3 | Jack Bauer
tblGames (RowId, TeamHomeRowId, TeamGuestRowId)
RowId | TeamHomeRowId | TeamGuestRowId | GameDate
---------------------------------------------------
1 | 1 | 2 | 2015-01-01
Now I want get a list of all goals. The list should look like this:
GoalMinute | PlayerName | GoalsHome | GoalsGuest
-----------------------------------------------------
25 | John Snow | 1 | 0
45 | Frank Underwood | 1 | 1
66 | Jack Bauer | 2 | 1
GoalsHome and GoalsGuest should be a counter of the shot goals for the team. So e.g. if you check the last row, the result is 2:1 for home team.
To get this list of goals, I used this statement:
SELECT t_gol.GoalMinute,
t_ply.PlayerName,
CASE WHEN
t_gol.TeamRowId = t_gam.TeamHomeRowId
THEN ROW_NUMBER() OVER (PARTITION BY t_gam.TeamHomeRowId ORDER BY t_gam.TeamHomeRowId)
END AS GoalsHome,
CASE WHEN
t_gol.TeamRowId = t_gam.TeamGuestRowId
THEN ROW_NUMBER() OVER (PARTITION BY t_gam.TeamGuestRowId ORDER BY t_gam.TeamGuestRowId)
END AS GoalsGuest
FROM dbo.tblGoalsFussball AS t_gol
LEFT JOIN dbo.tblPlayersFussball AS t_ply ON (t_ply.RowId = t_gol.PlayerRowId)
LEFT JOIN dbo.tblGames AS t_gam ON (t_gam.RowId = t_gol.GameRowId)
WHERE t_gol.GameRowId = #match_row
But what I get is this here:
GoalMinute | PlayerName | GoalsHome | GoalsGuest
-----------------------------------------------------
25 | John Snow | 1 | NULL
45 | Frank Underwood | NULL | 2
66 | Jack Bauer | 3 | NULL
Maybe ROW_NUMBER() is the wrong approach?
I would do the running total using sum() as a windowed aggregate function with the over ... clause, which works in SQL Server 2012+.
select
g.RowId, g.GameDate, t.GoalMinute, p.PlayerName,
GoalsHome = COALESCE(SUM(case when TeamRowId = g.TeamHomeRowId then 1 end) OVER (PARTITION BY gamerowid ORDER BY goalminute),0),
GoalsGuest = COALESCE(SUM(case when TeamRowId = g.TeamGuestRowId then 1 end) OVER (PARTITION BY gamerowid ORDER BY goalminute),0)
from tblGoals t
join tblPlayers p on t.PlayerRowId = p.RowId
join tblGames g on t.GameRowId = g.RowId
order by t.GameRowId, t.GoalMinute
Another approach (that also works in older versions) is to use a self-join and sum up the rows with lower goalminutes. For ease of reading I've used a common table expression to split the goals into two columns for home and guest team:
;with t as (
select
g.GoalMinute, g.PlayerRowId, g.GameRowId,
case when TeamRowId = ga.TeamHomeRowId then 1 end HomeGoals,
case when TeamRowId = ga.TeamGuestRowId then 1 end GuestGoals
from tblGoals g
join tblGames ga on g.GameRowId = ga.RowId
)
select
g.RowId, g.GameDate, t.GoalMinute, p.PlayerName,
GoalsHome = (select sum(coalesce(HomeGoals,0)) from t t2 where t2.GoalMinute <= t.GoalMinute and t2.GameRowId = t.GameRowId),
GoalsGuest = (select sum(coalesce(GuestGoals,0)) from t t2 where t2.GoalMinute <= t.GoalMinute and t2.GameRowId = t.GameRowId)
from t
join tblPlayers p on t.PlayerRowId = p.RowId
join tblGames g on t.GameRowId = g.RowId
order by t.GameRowId, t.GoalMinute
The CTE isn't necessary though, you could just as well use a derived table
Sample SQL Fiddle
I think the easiest way is with subqueries..
SELECT
tgs.GoalMinute,
tpl.PlayerName,
( SELECT
COUNT(t.RowId)
FROM
tblgoals AS t
WHERE t.GoalMinute <= tgs.GoalMinute
AND t.GameRowId = tgm.RowId
AND t.TeamRowId = tgm.TeamHomeRowId
) AS HomeGoals,
( SELECT
COUNT(t.RowId)
FROM
tblgoals AS t
WHERE t.GoalMinute <= tgs.GoalMinute
AND t.GameRowId = tgm.RowId
AND t.TeamRowId = tgm.TeamGuestRowId
) AS GuestGoals
FROM
tblgoals AS tgs
JOIN tblplayers AS tpl ON tgs.RowId = tpl.RowId
JOIN tblGames AS tgm ON tgm.RowId = tgs.GameRowId
ORDER BY tgs.GoalMinute

Create a new row based on criteria

I am pulling a set of information for a Check Summary Report and I am running into an issue where I need to display the full history of the check. In particular, say for instance I have issued a Check. This check can possibly be Voided. If it is Voided in our program, it will simply change the Status of the check to Void. However, I need instead to be able to create a new row entry and manipulate the data accordingly.
Here is the query I am working with.
SELECT DISTINCT
O.GFNo AS 'FileNumber'
,CASE
WHEN C.VoidDate IS NOT NULL
THEN C.VoidDate
WHEN C.VoidDate IS NULL
THEN C.CheckDate
ELSE NULL
END AS 'Date'
-- Check type descriptions.
,CASE
WHEN C.CheckStatus NOT IN (3,4) AND C.VoidDate IS NULL
THEN CASE
WHEN C.IsTransfer = 0 AND C.IsWire = 0 AND C.IsEFER = 0
THEN 'Check'
WHEN C.IsTransfer = 1
THEN 'Transfer'
WHEN C.IsWire = 1
THEN 'Wire'
WHEN C.IsEFER = 1
THEN 'EFER'
END
-- Stopped checks.
WHEN C.CheckStatus = 3 AND C.VoidDate IS NOT NULL
THEN CASE
WHEN C.IsTransfer = 0 AND C.IsWire = 0 AND C.IsEFER = 0
THEN 'Stopped Check'
WHEN C.IsTransfer = 1
THEN 'Stopped Transfer'
WHEN C.IsWire = 1
THEN 'Stopped Wire'
WHEN C.IsEFER = 1
THEN 'Stopped EFER'
END
-- Voided checks.
WHEN C.CheckStatus = 4 AND C.VoidDate IS NOT NULL
THEN CASE
WHEN C.IsTransfer = 0 AND C.IsWire = 0 AND C.IsEFER = 0
THEN 'Void Check'
WHEN C.IsTransfer = 1
THEN 'Void Transfer'
WHEN C.IsWire = 1
THEN 'Void Wire'
WHEN C.IsEFER = 1
THEN 'Void EFER'
END
END AS 'Description'
-- Check number.
,CASE
WHEN C.IsTransfer = 1
THEN 'XFer'
WHEN C.IsConsolidated = 1 AND C.ConsolidatedCheckID > 0
THEN 'Consolidated'
WHEN C.IsEFER = 1
THEN 'EFER-' + CONVERT(VARCHAR(MAX), C.WireNumber)
WHEN C.IsWire = 1
THEN 'W-' + CONVERT(VARCHAR(MAX), C.WireNumber)
WHEN C.IsCashiersCheck = 1
THEN 'C-' + CONVERT(VARCHAR(MAX), C.CheckNumber)
ELSE CONVERT(VARCHAR(MAX), C.CheckNumber)
END AS 'CheckNumber'
,CONVERT(VARCHAR(MAX), 0) AS 'Receipts'
,CASE
WHEN C.CheckStatus = 4 AND C.VoidDate IS NOT NULL
THEN ISNULL(C.Amount, 0)
ELSE CAST(0.0 - C.Amount AS MONEY)
END AS 'Disbursements'
FROM
Checks C
LEFT JOIN dbo.Orders O
ON C.OrdersID = O.OrdersID
LEFT JOIN CheckSessionDetail CSD
ON C.ChecksID = CSD.ChecksID
AND CSD.ActionType IN (6,9)
LEFT JOIN CheckSession CS
ON CSD.CheckSessionID = CS.CheckSessionID
WHERE
O.OrdersID = #OrdersID
AND C.CheckDate IS NOT NULL
This is what it is returning. If the check is Voided, it will simply change the Description to 'Void.'
| FileNumber | Date | Description | CheckNumber | Receipts | Disbursements |
| 1 | 8/8/2014 | Check | 1001 | 0 | $7500 |
| 1 | 8/8/2014 | Check | 1002 | 0 | $5000 |
| 1 | 8/8/2014 | Void | 1003 | 0 | $1000 |
This is how I NEED it to return:
| FileNumber | Date | Description | CheckNumber | Receipts | Disbursements |
| 1 | 8/8/2014 | Check | 1001 | 0 | $7500 |
| 1 | 8/8/2014 | Check | 1002 | 0 | $5000 |
| 1 | 8/8/2014 | Check | 1003 | 0 | $1000 |
| 1 | 8/8/2014 | Void | 1003 | $1000 | $0 |
Notice how if the check was voided, it retains the initial Check entry, but ALSO creates a Void entry and places the dollar amount into the Receipts column. It will also persist the same CheckNumber.
Essentially, is it possible to say something along the lines of: If a Check has the status of Void, create a new row entry? I'm thinking that a temporary table might work, but I've not delved too much into them. I've been banging my head trying to figure this out all day long, so any and all help you guys could offer would be a tremendous help.
Sure, instead of:
FROM Checks C
LEFT JOIN dbo.Orders O ...
try:
SELECT DISTINCT
O.GFNo AS 'FileNumber'
,c.Date
...
FROM ( select Date = CheckDate, typ = 'Check', * from Checks
UNION ALL
select Date = VoidDate, typ = 'Void', * from Checks WHERE VoidDate is not null ) c
LEFT JOIN dbo.Orders O ...
and use typ in your case statements for Description, Receipts, etc.

Multiple sql query or Cursor?

I need help on something that seems to be complex to me.
I made a query to create a tbl1 which is the Cartesian product of the tables Item and Warehouse. It give’s me back all items in all warehouses:
SELECT i.ItemID, w.WarehouseID
FROM Item i, Warehouse w
I made a second query (tbl2) where I check the date of the last document previous or equal to a variable date (#datevar) and whose quantity rule is 1 (PhysicalQtyRule = 1), this by Item and Warehouse, obtained from StockHistory table
SELECT MAX(CreateDate) AS [DATE1], ItemID, Quantity, WarehouseID
FROM StockHistory
WHERE PhysicalQtyRule = 1 AND CreateDate <= #datevar
GROUP BY ItemID, Quantity, WarehouseID
Now, I need more three steps:
Build a third table containing per item and warehouse the sum of quantity, but the quantity rule is 2 (PhysicalQtyRule = 2) and date between tbl2.date (if exists) and the date of the variable #datevar, obtained from the table StockHistory. Something like that:
SELECT ItemID, WarehouseID, SUM(Quantity)
FROM StockHistory
WHERE PhysicalQtyRule = 2 AND CreateDate > tbl2.DATE1 --If exists
AND CreateDate <= #datevar
GROUP BY ItemID, WarehouseID
Build a fourth table containing per item and warehouse the sum of quantity, but the quantity rule is 3 (PhysicalQtyRule = 3) and date between tbl2.date (if any) and the date of the variable #datevar, obtained from the table StockHistory. Something like that:
SELECT ItemID, WarehouseID, SUM(Quantity)
FROM StockHistory
WHERE PhysicalQtyRule = 3 AND CreateDate > tbl2.DATE1 --If exists
AND CreateDate <= #datevar
GROUP BY ItemID, WarehouseID
Create a final table based on the first one, with an sum quantity column, something like that:
SELECT i.ItemID, w.WarehouseID, tbl2.Quantity + tbl3.Quantity – tbl4.Quantity AS [Qty]
FROM Item i, Warehouse w
I don't know if need cursors (something new for me) or multiple querys, but it's important the best performance because my StockHistory table have millions of records.
Can anyone help-me please? Thank you!
Some sample data, only for one Item and one warehouse:
+--------+-------------+------------+-----------------+----------+
| ItemID | WarehouseID | CreateDate | PhysicalQtyRule | Quantity | Balance | comments
+--------+-------------+------------+-----------------+----------+
| 1234 | 11 | 2013-03-25 | 2 | 35 | 35 | Rule 2 = In
| 1234 | 11 | 2013-03-28 | 3 | 30 | 5 | Rule 3 = Out
| 1234 | 11 | 2013-04-01 | 1 | 3 | 3 | Rule 1 = Reset
| 1234 | 11 | 2013-07-12 | 2 | 40 | 43 | Rule 2 = In
| 1234 | 11 | 2013-09-05 | 3 | 20 | 23 | Rule 3 = Out
| 1234 | 11 | 2013-12-31 | 1 | 25 | 25 | Rule 1 = Reset
| 1234 | 11 | 2014-01-09 | 3 | 11 | 14 | Rule 3 = Out
| 1234 | 11 | 2014-01-16 | 3 | 6 | 8 | Rule 3 = Out
I want to know the balance on any variable date.
Without your data, I can't test this but I believe this should be your solution.
SELECT i.ItemID
,w.WarehouseID
,[Qty] = tbl2.Quantity + tbl3.Quantity – tbl4.Quantity
FROM Item i
CROSS JOIN Warehouse w
OUTER APPLY (
SELECT [DATE1] = MAX(sh.CreateDate)
,sh.ItemID
,sh.Quantity
,sh.WarehouseID
FROM StockHistory sh
WHERE sh.PhysicalQtyRule = 1 AND sh.CreateDate <= #datevar
AND i.ItemID = sh.ItemID
AND w.WarehouseID = sh.WarehouseID
GROUP BY sh.ItemID, sh.Quantity, sh.WarehouseID ) tbl2
OUTER APPLY (
SELECT sh.ItemID
,sh.WarehouseID
,[Quantity] = SUM(sh.Quantity)
FROM StockHistory sh
WHERE sh.PhysicalQtyRule = 2 AND sh.CreateDate > tbl2.DATE1 --If exists
AND sh.CreateDate <= #datevar AND i.ItemID = sh.ItemID
AND w.WarehouseID = sh.WarehouseID
GROUP BY sh.ItemID, sh.WarehouseID ) tbl3
OUTER APPLY (
SELECT sh.ItemID
,sh.WarehouseID
,[Quantity] = SUM(sh.Quantity)
FROM StockHistory sh
WHERE sh.PhysicalQtyRule = 3 AND sh.CreateDate > tbl2.DATE1 --If exists
AND sh.CreateDate <= #datevar AND i.ItemID = sh.ItemID
AND w.WarehouseID = sh.WarehouseID
GROUP BY sh.ItemID, sh.WarehouseID ) tbl4