How can i create trigger using GROUP BY - sql-server

I want to create a trigger after insert and update which calculate the sum of values in multiple rows and set the sum in [Target_Cummlative]. I try the following code but that causes this error:
Msg 156, Level 15, State 1, Procedure target_cummlative, Line 13
Incorrect syntax near the keyword 'FROM
My code:
CREATE TRIGGER [dbo].[target_cummlative]
ON [dbo].[Appointments]
AFTER insert, UPDATE
AS
BEGIN
IF TRIGGER_NESTLEVEL() > 1
RETURN
UPDATE T1
SET t1.[Target_Cummlative] = SUM(ISNULL(CAST([TARGET] AS FLOAT), 0)
FROM [Appointments] T1
INNER JOIN inserted i ON T1.[UniqueId] = i.[UniqueId]
GROUP BY CONVERT(VARCHAR(10), t1.[StartDate], 111), t1.[ResourceId]
END
Here is what i have currently, as you can see Target cummlative is null
Id StartDate Location ResourceId TARGET Target_Cummlative
1381 2019-07-22 07:00:00 41051 1 20 NULL
1382 2019-07-22 08:00:00 41051 1 20 NULL
1383 2019-07-22 09:15:00 41051 1 15 NULL
1384 2019-07-22 10:00:00 41051 1 20 NULL
1385 2019-07-22 11:00:00 41051 1 20 NULL
1386 2019-07-22 12:30:00 41051 1 8 NULL
I want set the sum the values in TARGET column and update Target cummlative as
Id StartDate Location ResourceId TARGET Target_Cummlative
1381 2019-07-22 07:00:00 41051 1 20 103
1382 2019-07-22 08:00:00 41051 1 20 103
1383 2019-07-22 09:15:00 41051 1 15 103
1384 2019-07-22 10:00:00 41051 1 20 103
1385 2019-07-22 11:00:00 41051 1 20 103
1386 2019-07-22 12:30:00 41051 1 8 103

If possible - I'd advice against storing cumulative data in the same table, if possible - better create a separate one to store aggregated information.
In any case - your issue is missing parenthesis in SET clause.
CREATE TRIGGER [dbo].[target_cumulative]
ON [dbo].[Appointments]
AFTER insert, UPDATE
AS
BEGIN
IF TRIGGER_NESTLEVEL() > 1
RETURN
UPDATE T1
SET T1.[Target_Cumulative] = SUM(ISNULL(CAST([TARGET] AS FLOAT), 0))
FROM dbo.Appointments AS T1
INNER JOIN inserted AS ION I.UniqueId = T1.UniqueId
GROUP BY CONVERT(DATE, T1.StartDate), T1.ResourceId;
END;
Oh - and there's typo in cummlative, it should be cumulative

Related

Subtract depletion And Limit for SUM

First I'm using AdventureWork2019 as a reference
I have a query where I'm joining 5 Tables
USE [AdventureWorks2019]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
Alter PROCEDURE dbo.TestLocation
#UseDate DateTime
AS
BEGIN
SET NOCOUNT ON;
SELECT prodID
,SUM(PurchQty) AS TotalPurchase
,SUM(SalesQty) AS TotalSell
,StartDate
from (
SELECT DISTINCT WO.ProductID AS prodID
, StartDate
,WO.OrderQty AS PurchQty
,SOD.OrderQty AS SalesQty
FROM Sales.SalesOrderDetail SOD
LEFT JOIN Production.WorkOrderRouting WOR ON WOR.ProductID = SOD.ProductID
--LEFT JOIN Production.Location PL ON PL.LocationID = WOR.LocationID
--The above Join is the one for the locationID and it's working Fine
LEFT JOIN Production.WorkOrder WO ON WO.ProductID = SOD.ProductID
FULL OUTER JOIN Purchasing.PurchaseOrderDetail POD ON POD.ProductID = SOD.ProductID
WHERE StartDate = #UseDate
-- AND PL.LocationID >= 10
) Test3
Group by prodID,StartDate
order by prodID ASC, StartDate
END
GO
EXEC TestLocation '2011-07-02 00:00:00.000'
Output(sample):
prodID TotalPurc TotalSell StartDate
717 8 36 2011-07-02 00:00:00.000
730 9 47 2011-07-02 00:00:00.000
744 2 3 2011-07-02 00:00:00.000
747 12 21 2011-07-02 00:00:00.000
749 5 15 2011-07-02 00:00:00.000
761 16 138 2011-07-02 00:00:00.000
775 26 91 2011-07-02 00:00:00.000
777 12 78 2011-07-02 00:00:00.000
802 6 21 2011-07-02 00:00:00.000
804 40 60 2011-07-02 00:00:00.000
806 16 138 2011-07-02 00:00:00.000
807 24 23 2011-07-02 00:00:00.000
810 21 28 2011-07-02 00:00:00.000
811 6 21 2011-07-02 00:00:00.000
813 8 37 2011-07-02 00:00:00.000
817 21 28 2011-07-02 00:00:00.000
And another Table For LocationID (as a warehouse)
SELECT LocationID,CostRate,Availability
FROM Production.Location
WHERE LocationID >= 10
order by CostRate ASC
LocationID CostRate Availability
50 12.25 120.00
60 12.25 120.00
30 14.50 120.00
40 15.75 120.00
45 18.00 80.00
10 22.50 96.00
20 25.00 108.00
What I want to do is to take each LoactionId and ProdID and take TotalPurc to the location and decrement the quantity in the Availability column, each TotalSell will increment the Availability column. The max Availability quantity is 130.
If all locations have no Available quantity that is the Available is 0 for all locations then it will stop.
the above will work with the date specified as you can check the query and run it if you have
AdventureWork2019
simple output to check how I want the data to be:
prodID TotalPurc TotalSell StartDate
717 8 36 2011-07-02 00:00:00.000
730 9 47 2011-07-02 00:00:00.000
744 2 3 2011-07-02 00:00:00.000
747 12 21 2011-07-02 00:00:00.000
749 5 15 2011-07-02 00:00:00.000
LocationID CostRate Availability
50 12.25 120.00
60 12.25 120.00
30 14.50 120.00
40 15.75 120.00
45 18.00 80.00
10 22.50 96.00
20 25.00 108.00
Output :
prodID TotalPurc TotalSell StartDate LocationID Availability Remaining
717 8 36 2011-07-02 00:00:00.000 50 130 18
717 8 36 2011-07-02 00:00:00.000 60 130 8
717 8 36 2011-07-02 00:00:00.000 30 128 0
--what happened above is that I took the (120-8) = 112 then 112+36 = 148 we only can use 130 then the remaining is 18 then we took the next `LocationID` with the least Cost (120+18 = 138 we can use 130 so we took the 8) and used it in the next `LocationID`
730 9 47 2011-07-02 00:00:00.000 30 130 36
730 9 47 2011-07-02 00:00:00.000 40 130 26
730 9 47 2011-07-02 00:00:00.000 45 106 0
744 2 3 2011-07-02 00:00:00.000 45 107 0
747 12 21 2011-07-02 00:00:00.000 45 116 0
749 5 15 2011-07-02 00:00:00.000 45 126 0
--the above is the same as the first 3 rows we subtract and add to the availability
The other condition is that if all locations reached 0 or 130 then stop
How can I do that in SQL Server? I tried using CTE but didn't work well with me and tried the cursor which I think is the best for this kind of thing but didn't achieve anything.
Thank you in advance
Edit :
ALTER FUNCTION GetStockMovment
(
-- Add the parameters for the function here
#ForDate Datetime
)
RETURNS #Sums TABLE (
RemoveQTY Numeric(24, 7),
ADDQTY Numeric(24, 7)
)
AS
BEGIN
Declare #WoSum Numeric(24, 7),
#SODSUM Numeric(24, 7),
#WORSum Numeric(24, 7),
#PODSum Numeric(24, 7)
select #SODSUM = SUM(SOD.OrderQty) from Sales.SalesOrderDetail SOD
INNER JOIN Sales.SalesOrderHeader SOH ON SOD.SalesOrderID = SOH.SalesOrderID
where SOH.OrderDate = #ForDate
select #WoSum = sum(orderQty) from Production.WorkOrder
where StartDate = #ForDate
select #PODSum = sum(POD.OrderQty) from Purchasing.PurchaseOrderDetail POD
INNER JOIN Purchasing.PurchaseOrderHeader POH ON POD.PurchaseOrderID = POH.PurchaseOrderID
where POH.OrderDate = #ForDate
select #WoSum = sum(WO.OrderQty) from Production.WorkOrder WO
where WO.DueDate = #ForDate
INSERT INTO #Sums (RemoveQTY,ADDQTY)
SELECT isnull(#SODSUM,0) + isnull(#WORSum,0) , isnull(#PODSum,0) + isnull(#WoSum,0)
RETURN;
END;
GO
select * from dbo.GetStockMovment ('2014-05-26 00:00:00.000')
Output:
RemoveQTY ADDQTY
189.0000000 5334.0000000
You should use LAG or LEAD function.
https://learn.microsoft.com/en-us/sql/t-sql/functions/lead-transact-sql?view=sql-server-ver15
https://learn.microsoft.com/en-us/sql/t-sql/functions/lag-transact-sql?view=sql-server-ver15

Insert a row for every hour in a date range

I have a table with a StartDate column and an EndDate column. I need to insert into a new table a row for each hour in the date range of the above tables column.
The table I have looks like this
StartDate EndDate
2017-10-25 19:00:00.000 2017-11-30 23:59:59.997
2017-10-26 13:00:00.000 2017-12-1 23:59:59.997
new table I need should look like this
Date Hour
2017-10-25 19
2017-10-25 20
2017-10-25 21
2017-10-25 22
2017-10-25 23
2017-10-26 0
2017-10-26 1
2017-10-26 2
:::::::::: :
:::::::::: :
2017-11-30 22
2017-11-30 23
I am so lost, please help!
Can be done with an ad-hoc tally table in concert with a CROSS APPLY.
Example
Select Date = cast(D as date)
,Hour = datepart(HOUR,D)
From YourTable A
Cross Apply (
Select Top (DateDiff(HOUR,A.StartDate,A.EndDate)+1) D=DateAdd(HOUR,-1+Row_Number() Over (Order By (Select Null)),A.StartDate)
From master..spt_values n1,master..spt_values n2
) B
Returns
Date Hour
2017-10-25 19
2017-10-25 20
2017-10-25 21
2017-10-25 22
2017-10-25 23
2017-10-26 0
2017-10-26 1
2017-10-26 2
2017-10-26 3
...

How to bulk update the date column

I have a table with 10000 records.
Sample
Id Transaction_Id Contract_Id Contractor_Id ServiceDetail_Id ServiceMonth UnitsDelivered CreateDate
----------------------------------------------------------------------------------------------------------------------------
1 1 352 466 590 2016-03-01 203 2016-04-25 17:01:55.000
2 1 352 466 566 2016-03-01 200 2016-04-25 17:02:38.807
3 1 352 466 138 2016-04-13 20 2016-04-13 00:00:00.000
5 1 352 466 138 2016-04-14 21 2016-04-13 00:00:00.000
6 10011 40 460 68 2016-03-17 10 2016-04-25 17:20:13.413
7 10011 40 460 511 2016-03-17 15 2016-04-25 17:20:13.413
8 10011 40 460 1611 2016-03-17 20 2016-04-25 17:20:13.413
9 20011 352 466 2563 2016-02-05 10 2016-04-25 17:20:25.307
11 100 40 460 68 2016-03-17 10 2016-04-25 17:29:23.653
In this table I have servicemonth with different dates.
I want to update the servicemonth column to the existing months last date.
suppose if I have 2016-03-17 in the table, it should be updated to 2016-3-31
suppose if I have 2016-05-12 in the table, it should be updated to 2016-5-31
Can anyone suggest a single query to update this?
EOMONTH: Returns the last day of the month that contains the specified date, with an optional offset.
UPDATE ... SET servicemonth = EOMONTH(servicemonth)
Update servicemonth
set servicemonth = DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+1,0))
--replace getdate() with the date column for which you want the end day of the month

mssql join criteria with the same table

Just need a little help. so I have this calendar table here sqlfiddle.com/#!3/5d8a9 There are 2 flags (M, W) indicating Month or Week. I need to do a join with TBL2 (below) that has a weekly date field. so I will join TBL2 on the weekstartdate with Cal table on start date (with W flag) but I need to return the startdate with the 'M' flag if the weekstartdate falls between start and end date. Does it make sense?
TBL2:
prod_id weeknum wkstartdate postingDate qty
----------------------------------------------------------------
1043890 5 2015-01-25 2016-01-18 3
1043890 6 2015-02-01 2016-01-18 6
1043890 7 2015-02-08 2016-01-18 2
1043890 8 2015-02-15 2016-01-18 0
...
1043890 50 2015-12-06 2016-01-18 1
1043890 51 2015-12-13 2016-01-18 2
1043890 52 2015-12-20 2016-01-18 7
Desired Result:
==================
prod_id weeknum wkstartdate postingDate qty Period
----------------------------------------------------------------------------
1043890 5 2015-01-25 2016-01-18 3 2015-01-25
1043890 6 2015-02-01 2016-01-18 6 2015-01-25
1043890 7 2015-02-08 2016-01-18 2 2015-01-25
1043890 8 2015-02-15 2016-01-18 0 2015-01-25
1043890 10 2015-03-01 2016-01-18 0 2015-03-01
...
1043890 50 2015-12-06 2016-01-18 1 2015-11-22
1043890 51 2015-12-13 2016-01-18 2 2015-11-22
1043890 52 2015-12-20 2016-01-18 7 2015-11-22
If it makes sense, I'm trying to check if weekstartdate falls between a range of dates (startdate-enddate) with a flag of M, then bring the startdate and either update that record in the temp table or create a temp table with the new column added to each record.
Thanks.
what I understood is you trying to join table2 on weekstarttable with cal table on startdate and you want to modify flag if week start date falls between start date and end date.
if got it correct, try the following query.
select t2.prod_id,t2.weeknum,c.startdate
,case when t2.wkstartdate between c.startdate and c.enddate
then 'M' else null end modifiedflag,
c.flag as originalflag
from cal c,table2 t2
where c.flag ='W' and t2.wkstartdate = c.startdate
and t2.wkstartdate between c.startdate and c.enddate;

MS SQL: Group by date [duplicate]

This question already has answers here:
Sql Date Grouping with avaliable dates in database
(3 answers)
Closed 4 years ago.
ID DateTime EmailCount
93 6/1/2014 00:00:00 4
94 6/2/2014 00:00:00 4
95 6/3/2014 00:00:00 2
96 6/4/2014 00:00:00 2
97 6/5/2014 00:00:00 2
98 6/6/2014 00:00:00 2
99 6/7/2014 00:00:00 2
73 6/8/2014 00:00:00 2
74 6/9/2014 00:00:00 2
75 6/10/2014 00:00:00 4
76 6/11/2014 00:00:00 4
77 6/12/2014 00:00:00 2
78 6/13/2014 00:00:00 2
79 6/14/2014 00:00:00 2
80 6/16/2014 00:00:00 2
81 6/17/2014 00:00:00 4
82 6/18/2014 00:00:00 4
83 6/19/2014 00:00:00 4
84 6/20/2014 00:00:00 4
100 6/21/2014 00:00:00 4
101 6/22/2014 00:00:00 4
102 6/23/2014 00:00:00 4
103 6/24/2014 00:00:00 4
89 6/27/2014 00:00:00 4
90 6/28/2014 00:00:00 4
91 6/29/2014 00:00:00 4
92 6/30/2014 00:00:00 4
104 7/1/2014 00:00:00 4
105 7/2/2014 00:00:00 4
106 7/3/2014 00:00:00 4
121 7/6/2014 00:00:00 2
122 7/7/2014 00:00:00 2
123 7/8/2014 00:00:00 2
Generated Output
Startdate EndDate EmailCount
6/3/2014 00:00:00 6/14/2014 00:00:00 2
6/16/2014 00:00:00 6/16/2014 00:00:00 2
7/6/2014 00:00:00 7/8/2014 00:00:00 2
6/1/2014 00:00:00 6/11/2014 00:00:00 4
6/17/2014 00:00:00 6/24/2014 00:00:00 4
6/27/2014 00:00:00 7/3/2014 00:00:00 4
Here, the generated output is not perfect because I want StartDate to EndDate in groups like: (6/3/2014 to 6/9/2014 and EmailCount = 2) and (6/10/2014 to 6/11/2014 and EmailCount =4) and (6/12/2014 to 6/14/2014 and EmailCount =2). Also, date not in database should not be added to group.
A somewhat complex query to explain, but here goes an attempt;
If the time is always midnight, you could use a common table expression to assign a row number to each row, and group by the difference between the date and row number. As long as the sequence is not broken (ie the dates are consecutive and with the same emailid) they will end up in the same group and an outer query can easily extract the start and end date for each group;
WITH cte AS (
SELECT dateandtime, emailid,
ROW_NUMBER() OVER (PARTITION BY emailid ORDER BY dateandtime) rn
FROM mytable
)
SELECT MIN(dateandtime) start_time,
MAX(dateandtime) end_time,
MAX(emailid) emailid
FROM cte GROUP BY DATEADD(d, -rn, dateandtime) ORDER BY start_time
An SQLfiddle to test with.
If the datetimes are not always midnight, the grouping will fail. If that's the case, you could add a common table expression that converts the datetime to a date as a separate step before running this query.
You're looking for runs of consecutive dates in blocks with the same EmailID. This assumes you have no gaps in the dates. I'm not sure it's the most elegant approach but you can find a lot of stuff on this topic.
with BlockStart as (
select t.StartDate, t.EmailID
from T as t left outer join T as t2
on t2.StartDate = t1.StartDate - 1 and t2.EmailID = t1.EmailID
where t2.StartDate is null
union all
select max(StartDate) + 1, null
from T
) as BlockStart
select
StartDate,
(select min(StartDate) - 1 from BlockStart as bs2 where bs2 > bs.StartDate) as EndDate,
EmailID
from BlockStart as bs
where
EmailID is not null
-- /* or */ exists (select 1 from BlockStart as bs3 where bs3.StartDate > bs.StartDate)

Resources