Stuck with Trigger on insert and Update - sql

Thank you for looking on this question. I am using MSSQL Server Express.
I am trying to write a trigger which will update client's Eligibility based on their age and their income. I wrote the part with ages, but I am completely stuck with Eligibility part. I have statement which can select Clients who are not eligible based on their income, periodicity(month/year), household size
Select ClientID,c.HshldSize,c.MonthlyYearly,c.AnnualHshldIncome,i.SeniorMo,
StatusID,i.HshldSize
from Clients c
join IncomeEligibility i on c.HshldSize = i.HshldSize
where c.HshldSize= i.HshldSize and c.AnnualHshldIncome >= i.SeniorMo
and StatusID in (1,2)
and c.CategCode = 'SR' and MonthlyYearly ='month'
This select show all clients who not eligible
Example of respond
ClientID HshldSize MonthlyYearly AnnualHshldIncome SeniorMo StatusID HshldSize
28 1 month 1095 977 2 1
51 1 month 1253 977 1 1
63 1 month 1300 977 1 1
73 1 month 1200 977 1 1
96 1 month 1300 977 1 1
101 1 month 1255 977 1 1
160 2 month 1800 1513 1 2
IncomeEligibility Looks like this
HshldSize AKGuidline WomanChildYr WomanChildMo SeniorYr SeniorMo PFDYr PFDMo
1 9020 16687 1391 11726 977 878 73
2 13970 25845 2154 18161 1513 1756 146
3 18920 35002 2917 24596 2050 2634 219
4 23870 44160 3680 31031 2586 3512 292
5 28820 53317 4443 37466 3122 4390 365
6 33770 62475 5206 43901 3658 5268 439
7 38720 71632 5969 50336 4195 6146 512
8 43670 80790 6733 56771 4731 7024 585
9 48620 89947 7496 63206 5267 7902 658
10 53570 99105 8259 69641 5803 8780 731
11 58520 108262 9022 76076 6340 9658 804
12 63470 117420 9785 82511 6876 10536 878
13 68420 126577 10548 88946 7412 11414 951
14 73370 135735 11311 95381 7948 12292 1024
15 78320 144892 12074 101816 8485 13170 1097
16 83270 154050 12838 108251 9021 14048 1170
17 88220 163207 13601 114686 9557 14926 1243
18 93170 172365 14364 121121 10093 15804 1317
19 98120 181522 15127 127556 10630 16682 1390
20 103070 190680 15890 133991 11166 17560 1463
Trigger should set StatusID =5 on the clientrow if they are not eligible .
So far I have this trigger
create trigger tr_EligebilityCheck
on dbo.Clients
FOR INSERT,UPDATE
as
/*Check if Senior not eligible by age*/
If (select CategCode from inserted )='SR'
declare
#DOB date
SET #DOB = (select dob from inserted)
if DATEDIFF(YEAR,#DOB,GETDATE())<60
BEGIN
Update Clients
set StatusID = 5
From Clients c, inserted i
where c.CategCode = 'SR' and i.ClientID = C.ClientID
END
/*Check if Children eligebel by age*/
If (select CategCode from inserted )='CH'
declare
#DOBCH date
SET #DOBCH = (select dob from inserted)
if DATEDIFF(YEAR,#DOBCH,GETDATE()) >=6
BEGIN
Update Clients
set StatusID = 5
From Clients c, inserted i
where c.CategCode ='CH' and i.ClientID = C.ClientID
END
but have no idea how to add checking by Income, Please help if you have an idea how to do this. Also looks like my Trigger doesnot work throwing error when I am rtying insert new reccourd
Msg 512, Level 16, State 1, Procedure tr_EligebilityCheck, Line 6
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Thank you!

You have the select statement prepared if that's the only way of identifying who's not eligible. So you just need to update the ones that fall into that criteria. Do it with a subquery (I did not SQLFiddle). You can put the update in whatever position it needs to be in the trigger.
UPDATE Clients
SET
theColName = 'value'
WHERE
ClientID IN (Select ClientID
from Clients c
join IncomeEligibility i
on c.HshldSize = i.HshldSize
where c.HshldSize= i.HshldSize
and c.AnnualHshldIncome >= i.SeniorMo
and StatusID in (1,2)
and c.CategCode = 'SR'
and MonthlyYearly ='month')
The error is about this line If (select CategCode from inserted )='SR' the select CategCode query is returning multiple values so an equals sign is the wrong operator. Although I would put 'SR' in a variable but try this approach.
if #var IN (select CategCode from inserted)
The statement looks for the value IN a resultset, not to see if it's equals to multiple values. Look at your query results and see if it's returning multiple CategCodes, that's what the error is saying.

Related

Query to solve a rolling issue

I am seeking guidance on how to solve the following issue?
My data look like:
id
Total
Allowed
Left
1
815
150
655
2
815
15
650
3
815
135
515
4
815
1380
-864
5
815
109
-974
6
815
10
-984
"Left" column is derived from difference of Total and Allowed and then it rolls to next row with Left - Allowed to set value of Left in next rows.
I want to create a new column of Final where if Left value is greater than Allowed, then set the value of Final as Allowed; however, where Left value is less than Allowed, set the value Final for all such rows to 0 except for first row where Left value was less than 0 and set it to the value of last positive Left value. Output should be like this:
id
Total
Allowed
Left
Final
1
815
150
655
150
2
815
15
650
15
3
815
135
515
135
4
815
1380
-864
515
5
815
109
-974
0
6
815
10
-984
0
The way I tried to do is:
Update table
set Final =
Case when Left >= Allowed then Left End
However, i do not understand how to do the remaining part. I am new to this site and let me know if more details are required to help provide solution to me.
You can use an updatable CTE
WITH t AS (
SELECT *,
f = CASE WHEN [Left] > Allowed THEN Allowed
WHEN [Left] > 0 THEN [Left]
WHEN LAG([Left]) OVER (ORDER BY id) > 0 THEN LAG([Left]) OVER (ORDER BY id)
ELSE 0 END
FROM YourTable
)
UPDATE t
SET Final = f;
db<>fiddle

Implement the Scalar Function inside a CTE

i have a View(The View contains a CTE inside it as shown below) where in I am calling one Scalar Function and in the same scalar function the view is called. This whole process making the performance slower. Can i implement the function's functionality in the same View.
Please help
WITH tree AS
(
SELECT c1.structureid,c1.assessmentid, c1.sequence,c1.Required,c1.Objective, c1.parentid, c1.Text, [level] = 1, path = cast( c1.structureid as varchar(100))
FROM [ast].[Structure] c1
WHERE c1.parentid IS NULL
UNION ALL
SELECT c2.structureid, c2.assessmentid, c2.sequence,c2.Required,c2.Objective, c2.parentid, c2.Text, [level] = tree.[level] + 1,
Path = Cast(tree.path+'/'+right('000000000' + cast(c2.structureid as varchar(10)),10) as varchar(100))
FROM [ast].[Structure] c2 INNER JOIN tree ON tree.structureid = c2.parentid
)
SELECT tree.level,tree.sequence,
tree.path, parentid, tree.assessmentid, tree.Required,tree.Objective, (SELECT [dbo].Tree_full_index(tree.structureid))+' '+ tree.Text AS description ,C.* ,
wasScored = (case when C.choiceid is null then 0 else 1 end ),
wasDerived = (case when C.choiceid is null and C.Score is not null then 1 else 0 end )
FROM tree inner join [ast].[Value] as C on tree.structureid = C.structureid
Scalar Function
ALTER FUNCTION [dbo].[Tree_full_index]
(
#tree_node_id int
)
RETURNS varchar(20)
AS
BEGIN
declare #result varchar(20)
set #result =''
declare #node_seq_index varchar(5)
DECLARE #parentID int
select #node_seq_index=isnull(sequence,''),#parentID=isnull(parentid,0) from vwAssesment where structureid=#tree_node_id
set #result=#node_seq_index
WHILE #parentID > 0
BEGIN
SELECT #tree_node_id = #parentID
select #node_seq_index=isnull(sequence,''),#parentID=parentid from vwAssesment where structureid=#tree_node_id
set #result=#node_seq_index+'.0'+#result
END
RETURN #result
END
Structure Table
StructureId AssessmentId ParentId Required Sequence Text Objective
633 132 NULL 1 1 Customer Satisfaction understand our top Customers and our supplier ranking with them.
634 132 633 1 1 Top Customers NULL
635 132 634 1 1 Display top Customers on Lead Board NULL
636 132 634 1 2 Display Customer Supplier Ranking for Facility - NA NULL
637 132 634 1 3 Display Work Plan that provides path to Preferred Supplier status NULL
638 132 633 1 2 Real Time Response Process NULL
639 132 638 0 1 Real-time response system in place when abnormalities occur with documented Counter Measures NULL
640 132 NULL 1 2 Continuous Improvement ensure driving foundation for Continuous Improvement
641 132 640 1 1 Gemba NULL
642 132 641 1 1 Routine and scheduled NULL
643 132 641 1 2 Incorporated into appropriate different levels of organization NULL
644 132 640 1 2 TPM NULL
645 132 644 1 1 Perform initial Cleaning & Inspection (Level 1) NULL
646 132 645 1 1 Learn how to identify equipment problems NULL
The result Should have the column with actual index of the Node as shown
level sequence parentid assessmentid Required Objective description ValueId InstanceId StructureId ChoiceId Score wasScored wasDerived
1 1 NULL 132 1 understand our top Customers and our supplier ranking with them. Ensure In-Station Quality and continuous improvement. 1 Customer Satisfaction 666 207 633 NULL 2 0 1
2 1 633 132 1 NULL 1.01 Top Customers 667 207 634 NULL 4 0 1
3 1 634 132 1 NULL 1.01.01 Display top Customers on Lead Board 668 207 635 40 4 1 0
3 2 634 132 1 NULL 1.01.02 Display Customer Supplier Ranking for Facility - NA 669 207 636 40 4 1 0
3 3 634 132 1 NULL 1.01.03 Display Work Plan that provides path to Preferred Supplier status 670 207 637 40 4 1 0
2 2 633 132 1 NULL 1.02 Real Time Response Process 671 207 638 NULL NULL 0 0
3 1 638 132 0 NULL 1.02.01 Real-time response system in place when abnormalities occur with documented Counter Measures 672 207 639 NULL NULL 0 0
1 2 NULL 132 1 ensure driving foundation for Continuous Improvement culture to be successful and achieve meaningful results 2 Continuous Improvement 673 207 640 NULL 3.5 0 1
2 1 640 132 1 NULL 2.01 Gemba 674 207 641 20 2 1 0
3 1 641 132 1 NULL 2.01.01 Routine and scheduled 675 207 642 NULL NULL 0 0
3 2 641 132 1 NULL 2.01.02 Incorporated into appropriate different levels of organization (Facility Manager, Staff, site Director) 676 207 643 NULL NULL 0 0
2 2 640 132 1 NULL 2.02 TPM 677 207 644 NULL 5 0 1
3 1 644 132 1 NULL 2.02.01 Perform initial Cleaning & Inspection (Level 1) 678 207 645 50 5 1 0
4 1 645 132 1 NULL 2.02.01.01 Learn how to identify equipment problems 679 207 646 NULL NULL 0 0
You can implement the function in the view or in a CTE before the tree cte, as below:-
;with prev as (
select *,1 [depth],cast(isnull([sequence],'') as varchar(max)) [Tree_full_index] From vwAssesment where parentid is null
union all
select v.*,prev.depth+1 [depth],+[Tree_full_index]+'.0'+isnull(v.[sequence],'') from prev
inner join vwAssesment v on v.parentid=prev.structureid
),Tree as (.......
I tested the prev cte, it will return the Tree_full_index as part of it with all columns of vwAssesment , so change your query to use prev instead of vwAssesment, or update vwAssesment or create a new view to have the Tree_full_index and use it
For the belowsample data:-
Declare #vwAssesment Table(
structureid int,
parentid int,
[sequence] varchar(5)
)
insert into #vwAssesment values(1,null,'1')
insert into #vwAssesment values(2,null,'2')
insert into #vwAssesment values(3,null,'3')
insert into #vwAssesment values(4,1, '1')
insert into #vwAssesment values(5,1, '2')
insert into #vwAssesment values(6,1, '3')
insert into #vwAssesment values(7,2, '2')
insert into #vwAssesment values(8,2, '3')
insert into #vwAssesment values(9,8, '2')
insert into #vwAssesment values(10,8,'3')
The result is:-
structureid parentid sequence depth Tree_full_index
1 NULL 1 1 1
2 NULL 2 1 2
3 NULL 3 1 3
4 1 1 2 1.01
5 1 2 2 1.02
6 1 3 2 1.03
7 2 2 2 2.02
8 2 3 2 2.03
9 8 2 3 2.03.02
10 8 3 3 2.03.03

How to write the query to make report by month in sql

I have the receiving and sending data for whole year. so i want to built the monthly report base on that data with the rule is Fisrt in first out. It means is the first receiving will be sent out first ...
DECLARE #ReceivingTbl AS TABLE(Id INT,ProId int, RecQty INT,ReceivingDate DateTime)
INSERT INTO #ReceivingTbl
VALUES (1,1001,210,'2019-03-12'),
(2,1001,315,'2019-06-15'),
(3,2001,500,'2019-04-01'),
(4,2001,10,'2019-06-15'),
(5,1001,105,'2019-07-10')
DECLARE #SendTbl AS TABLE(Id INT,ProId int, SentQty INT,SendMonth int)
INSERT INTO #SendTbl
VALUES (1,1001,50,3),
(2,1001,100,4),
(3,1001,80,5),
(4,1001,80,6),
(5,2001,200,6)
SELECT * FROM #ReceivingTbl ORDER BY ProId,ReceivingDate
SELECT * FROM #SendTbl ORDER BY ProId,SendMonth
Id ProId RecQty ReceivingDate
1 1001 210 2019-03-12
2 1001 315 2019-06-15
5 1001 105 2019-07-10
3 2001 500 2019-04-01
4 2001 10 2019-06-15
Id ProId SentQty SendMonth
1 1001 50 3
2 1001 100 4
3 1001 80 5
4 1001 80 6
5 2001 200 6
--- And the below is what i want:
Id ProId RecQty ReceivingDate ... Mar Apr May Jun
1 1001 210 2019-03-12 ... 50 100 60 0
2 1001 315 2019-06-15 ... 0 0 20 80
5 1001 105 2019-07-10 ... 0 0 0 0
3 2001 500 2019-04-01 ... 0 0 0 200
4 2001 10 2019-06-15 ... 0 0 0 0
Thanks!
Your question is not clear to me.
If you want to purely use the FIFO approach, therefore ignore any data the table contains, you necessarely need to order by ID, which in your example you are providing, and looks like it is in order of insert.
The first line inserted should be also the first line appearing in the select (FIFO), in order to do so you have to use:
ORDER BY Id ASC
Which will place the lower value of the ID first (1, 2, 3, ...)
To me though, this doesn't make much sense, so pay attention to the meaning o the data you actually have and leverage dates like ReceivingDate, and order by that, maybe even filtering by month of the date, below an example for January data:
WHERE MONTH(ReceivingDate) = 1

Microsoft access Query rolling total every 5 lines not using dates

I am new to Microsoft access.
I need a query that will allow me to sum a rolling total for every 5 lines of data. So on the sixth day I need a line to drop off the total and the new line to be added.
Fields:
ID, Daily_SUM
The results should be
ID Daily sum Weekly Sum
1 12
2 41
3 46
4 125
5 120 344
6 42 374
7 41 374
8 57 385
9 207 467
10 215 562
11 187 707
12 -43 623
13 45 611
14 56 460
15 40 285
16 8 106
17 95 244
18 580 779
19 360 1083
20 337 1380
You can do this with a correlated subquery. The challenge is actually getting NULL values on the first few rows:
select t.id, t.daily,
(select iif(count(*) = 7, sum(t3.daily), NULL)
from (select top 7 t2.daily
from table t2
where t2.id <= t.id
order by t2.id desc
) t3
) as weekly
from table t;
EDIT:
If we assume that the ids are assigned sequentially with no gaps, then you can use an explicit join:
select t.id, t.daily,
iif(count(*) = 7, sum(t2.daily), NULL) as weekly
from table t inner join
table t2
on t2.id between t.id - 6 and t.id
group by t.id, t.daily;

SQL query self join

I am working on a query for a report in Oracle 10g.
I need to generate a short list of each course along with the number of times they were offered in the past year (including ones that weren't actually offered).
I created one query
SELECT coursenumber, count(datestart) AS Offered
FROM class
WHERE datestart BETWEEN (sysdate-365) AND sysdate
GROUP BY coursenumber;
Which produces
COURSENUMBER OFFERED
---- ----------
ST03 2
PD01 1
AY03 2
TB01 4
This query is all correct. However ideally I want it to list those along with COURSENUMBER HY and CS in the left column as well with 0 or null as the OFFERED value. I have a feeling this involves a join of sorts, but so far what I have tried doesn't produce the classes with nothing offered.
The table normally looks like
REFERENCE_NO DATESTART TIME TIME EID ROOMID COURSENUMBER
------------ --------- ---- ---- ---------- ---------- ----
256 03-MAR-11 0930 1100 2 2 PD01
257 03-MAY-11 0930 1100 12 7 PD01
258 18-MAY-11 1230 0100 12 7 PD01
259 24-OCT-11 1930 2015 6 2 CS01
260 17-JUN-11 1130 1300 6 4 CS01
261 25-MAY-11 1900 2000 13 6 HY01
262 25-MAY-11 1900 2000 13 6 HY01
263 04-APR-11 0930 1100 13 5 ST03
264 13-SEP-11 1930 2100 6 4 ST03
265 05-NOV-11 1930 2100 6 5 ST03
266 04-FEB-11 1430 1600 6 5 ST03
267 02-JAN-11 0630 0700 13 1 TB01
268 01-FEB-11 0630 0700 13 1 TB01
269 01-MAR-11 0630 0700 13 1 TB01
270 01-APR-11 0630 0700 13 1 TB01
271 01-MAY-11 0630 0700 13 1 TB01
272 14-MAR-11 0830 0915 4 3 AY03
273 19-APR-11 0930 1015 4 3 AY03
274 17-JUN-11 0830 0915 14 3 AY03
275 14-AUG-09 0930 1015 14 3 AY03
276 03-MAY-09 0830 0915 14 3 AY03
SELECT
coursenumber,
COUNT(CASE WHEN datestart BETWEEN (sysdate-365) AND sysdate THEN 1 END) AS Offered
FROM class
GROUP BY coursenumber;
So, as you can see, this particular problem doesn't need a join.
I think something like this should work for you, by just doing it as a subquery.
SELECT distinct c.coursenumber,
(SELECT COUNT(*)
FROM class
WHERE class.coursenumber = c.coursenumber
AND datestart BETWEEN (sysdate-365) AND sysdate
) AS Offered
FROM class c
I like jschoen's answer better for this particular case (when you want one and only one row and column out of the subquery for each row of the main query), but just to demonstrate another way to do it:
select t1.coursenumber, nvl(t2.cnt,0)
from class t1 left outer join (
select coursenumber, count(*) cnt
from class
where datestart between (sysdate-365) AND sysdate
group by coursenumber
) t2 on t1.coursenumber = t2.coursenumber