I have data like this in a table:
https://i.stack.imgur.com/JYZjz.png
I want to group first two events (1, 10) as F1, than second two events (1,10) as F2
Running this SQL query:
SELECT
Id, Date, Event,
CASE
WHEN Event = 1 AND LEAD(Event) OVER (ORDER BY ID) = 10
THEN 'F1'
WHEN Event = 10 AND LAG(Event) OVER (ORDER BY ID) = 1
THEN 'F1'
ELSE NULL
END AS Flow
FROM
lxfr.SystemEventsDiary
WHERE
Event IN (1, 10)
I get this result as output:
ID
Date
Event
Flow
4
2022-02-07 00:00:00.000
1
F1
44
2022-02-08 00:00:00.000
10
F1
47
2022-02-09 00:00:00.000
1
F1
72
2022-02-10 00:00:00.000
10
F1
75
2022-02-10 00:00:00.000
1
F1
97
2022-02-11 00:00:00.000
10
F1
100
2022-02-11 00:00:00.000
1
NULL
113
2022-02-28 00:00:00.000
1
F1
217
2022-03-04 00:00:00.000
10
F1
235
2022-03-22 00:00:00.000
10
NULL
241
2022-03-22 00:00:00.000
1
F1
270
2022-03-24 00:00:00.000
10
F1
273
2022-03-24 00:00:00.000
1
F1
308
2022-03-25 00:00:00.000
10
F1
But I need a result like this:
ID
Date
Event
Flow
4
2022-02-07 00:00:00.000
1
F1
44
2022-02-08 00:00:00.000
10
F1
47
2022-02-09 00:00:00.000
1
F2
72
2022-02-10 00:00:00.000
10
F2
75
2022-02-10 00:00:00.000
1
F3
97
2022-02-11 00:00:00.000
10
F3
100
2022-02-11 00:00:00.000
1
NULL
113
2022-02-28 00:00:00.000
1
F4
217
2022-03-04 00:00:00.000
10
F4
235
2022-03-22 00:00:00.000
10
NULL
241
2022-03-22 00:00:00.000
1
F5
270
2022-03-24 00:00:00.000
10
F5
273
2022-03-24 00:00:00.000
1
F6
308
2022-03-25 00:00:00.000
10
F6
I tried with a CTE, but I'm getting errors...
Thanks
This seems to work. I've copied your desired output as my input sample data just to show that my new column matches your Desired one, and also a couple of other renames for keywords:
declare #t table (ID int, DateOf datetime, EventNo int, DesiredFlow varchar(7))
insert into #t(ID,DateOf,EventNo,DesiredFlow) values
(4 ,'2022-02-07T00:00:00.000',1 ,'F1'),
(44 ,'2022-02-08T00:00:00.000',10 ,'F1'),
(47 ,'2022-02-09T00:00:00.000',1 ,'F2'),
(72 ,'2022-02-10T00:00:00.000',10 ,'F2'),
(75 ,'2022-02-10T00:00:00.000',1 ,'F3'),
(97 ,'2022-02-11T00:00:00.000',10 ,'F3'),
(100 ,'2022-02-11T00:00:00.000',1 ,NULL),
(113 ,'2022-02-28T00:00:00.000',1 ,'F4'),
(217 ,'2022-03-04T00:00:00.000',10 ,'F4'),
(235 ,'2022-03-22T00:00:00.000',10 ,NULL),
(241 ,'2022-03-22T00:00:00.000',1 ,'F5'),
(270 ,'2022-03-24T00:00:00.000',10 ,'F5'),
(273 ,'2022-03-24T00:00:00.000',1 ,'F6'),
(308 ,'2022-03-25T00:00:00.000',10 ,'F6')
;With Neighbours as (
select
*,
LEAD(EventNo,1,NULL) OVER (ORDER BY ID) as Successor,
LAG(EventNo,1,NULL) OVER (ORDER BY ID) as Predecessor
from
#t t
), NoStragglers as (
select
*
from
Neighbours
where
(EventNo = 1 and Successor = 10) or
(EventNo = 10 and Predecessor = 1)
), Ordered as (
select
*, ROW_NUMBER() OVER (PARTITION BY EventNo ORDER BY ID) as rn
from NoStragglers
)
select
t.*,'F' + (CONVERT(varchar(10),o.rn)) as ActualFlow
from
#t t
left join
Ordered o
on
t.Id = o.Id
Hopefully you can see how each CTE builds from the previous one (either by inspection or changing the final select to pull one one of the earlier CTEs instead).
Result:
ID DateOf EventNo DesiredFlow ActualFlow
----------- ----------------------- ----------- ----------- -----------
4 2022-02-07 00:00:00.000 1 F1 F1
44 2022-02-08 00:00:00.000 10 F1 F1
47 2022-02-09 00:00:00.000 1 F2 F2
72 2022-02-10 00:00:00.000 10 F2 F2
75 2022-02-10 00:00:00.000 1 F3 F3
97 2022-02-11 00:00:00.000 10 F3 F3
100 2022-02-11 00:00:00.000 1 NULL NULL
113 2022-02-28 00:00:00.000 1 F4 F4
217 2022-03-04 00:00:00.000 10 F4 F4
235 2022-03-22 00:00:00.000 10 NULL NULL
241 2022-03-22 00:00:00.000 1 F5 F5
270 2022-03-24 00:00:00.000 10 F5 F5
273 2022-03-24 00:00:00.000 1 F6 F6
308 2022-03-25 00:00:00.000 10 F6 F6
Which seems to match.
Check this,
create table #temp(id int,dates datetime,Events int)
insert into #temp(id,dates,events) values
(4 ,'2022-02-07 00:00:00.000', 1 )
,(44 ,'2022-02-08 00:00:00.000', 10 )
,(47 ,'2022-02-09 00:00:00.000', 1 )
,(72 ,'2022-02-10 00:00:00.000', 10 )
,(75 ,'2022-02-10 00:00:00.000', 1 )
,(97 ,'2022-02-11 00:00:00.000', 10 )
,(100 ,'2022-02-11 00:00:00.000', 1 )
,(113 ,'2022-02-28 00:00:00.000', 1 )
,(217 ,'2022-03-04 00:00:00.000', 10 )
,(235 ,'2022-03-22 00:00:00.000', 10 )
,(241 ,'2022-03-22 00:00:00.000', 1 )
,(270 ,'2022-03-24 00:00:00.000', 10 )
,(273 ,'2022-03-24 00:00:00.000', 1 )
,(308 ,'2022-03-25 00:00:00.000', 10 )
WITH CTE
AS (SELECT Id,
Dates,
Events,
CASE
WHEN Events = 1
AND LEAD(Events) OVER(
ORDER BY ID) = 10
THEN id
WHEN Events = 10
AND LAG(Events) OVER(
ORDER BY ID) = 1
THEN LAG(id) OVER(
ORDER BY ID)
ELSE NULL
END AS Flow
FROM #temp),
CtE1
AS (SELECT *,
DENSE_RANK() OVER(
ORDER BY flow) rn
FROM CTe
WHERE flow IS NOT NULL
UNION ALL
SELECT *,
NULL
FROM CTE
WHERE flow IS NULL)
SELECT id,
dates,
events,
CASE
WHEN rn IS NOT NULL
THEN concat('F', rn)
ELSE NULL
END Flow
FROM cte1
ORDER BY id;
drop table #temp
You can use cascading subqueries to assign a ROW_NUMBER() based on Flow values that are NOT NULL:
SELECT d.Id,
d.Date,
d.Event,
CASE WHEN b.rn IS NULL THEN NULL
ELSE CONCAT('F', b.rn)
END AS Flow
FROM (SELECT
Id,
Date,
Event,
Flow,
ROW_NUMBER() OVER (PARTITION BY Event ORDER BY Id ASC) as rn
FROM (SELECT
Id,
Date,
Event,
CASE WHEN Event = 1 AND LEAD(Event) OVER (ORDER BY Id) = 10 THEN 1
WHEN Event = 10 AND LAG(Event) OVER (ORDER BY Id) = 1 THEN 1
ELSE 0
END as Flow
FROM SystemEventsDiary WHERE Event IN (1, 10)
) a WHERE Flow = 1) b
RIGHT JOIN SystemEventsDiary d ON b.id = d.Id
ORDER BY d.Id ASC
Result:
Id
Date
Event
Flow
4
2022-02-07 00:00:00.000
1
F1
44
2022-02-08 00:00:00.000
10
F1
47
2022-02-09 00:00:00.000
1
F2
72
2022-02-10 00:00:00.000
10
F2
75
2022-02-10 00:00:00.000
1
F3
97
2022-02-11 00:00:00.000
10
F3
100
2022-02-11 00:00:00.000
1
NULL
113
2022-02-28 00:00:00.000
1
F4
217
2022-03-04 00:00:00.000
10
F4
235
2022-03-22 00:00:00.000
10
NULL
241
2022-03-22 00:00:00.000
1
F5
270
2022-03-24 00:00:00.000
10
F5
273
2022-03-24 00:00:00.000
1
F6
308
2022-03-25 00:00:00.000
10
F6
db<>fiddle here.
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
I've sifted through the various sql-server tagged threads using AVERAGE and Cumulative as search terms. Various desperate answers, but I can't cobble them together for my needs. The use case is to find the initial average value (cumulative value/cumulative days on) for a time period when cumulative days on is greater than 60 and less than 90.
Below is a table where ID identifies the object, VALUE is the amount reported on a monthly basis and DAYSON is the number of days in that month where the object ran to produce the value. YEARMONTH is date value on which on can sort.
ID VALUE DASYON YEARMONTH
1 166 27 201502
1 1 2 201505
1 569 19 201507
1 312 19 201508
2 364 27 201502
2 328 31 201503
2 242 29 201504
2 273 31 201505
2 174 30 201506
2 188 25 201507
2 203 25 201508
3 474 28 201502
3 521 31 201503
3 465 30 201504
3 473 31 201505
3 434 30 201506
3 404 31 201507
I would like to create a summary table that averages the cumulative value divided by the cumulative days uniquely for each ID where cumulative days is greater than 60 and less than 90. Below is a table that with the cumulative values. (I generated this in Excel)
ID VALUE cumValue DASYON cumDaysOn YEARMONTH
1 166 166 27 27 201502
1 1 167 2 29 201505
1 569 736 19 48 201507
1 312 1048 19 67 201508
2 364 364 27 27 201502
2 328 692 31 58 201503
2 242 934 29 87 201504
2 273 1207 31 118 201505
2 174 1381 30 148 201506
2 188 1569 25 173 201507
2 203 1772 25 198 201508
3 474 474 28 28 201502
3 521 505 31 59 201503
3 465 535 30 89 201504
3 473 566 31 120 201505
3 434 596 30 150 201506
3 404 627 31 181 201507
I try this based on other threads:
SELECT
ID,
Value,
SUM(Value) OVER (ORDER BY ID, YearMonth) [cumValue],
DaysOn,
SUM (DaysOn) OVER (Order by ID, YearMonth) as cumDaysOn,
YearMonth
FROM table
WHERE DAYSON > 0 and Liquid > 0 and YearMonth > 201501
GROUP BY ID, YearMonth, Value, DaysOn
ORDER BY ID, yearmonth
I can't get it to iterate over the ID; it just keeps summing down the column. If I could create a table or view like the one above, then I could always use a select statement and divide cumvalue by cumdayson.
Below is a table to show where I would get the initial average value (InititalAverageValue) based on the criteria:
ID VALUE cumValue DASYON cumDaysOn YEARMONTH InitalAvgValue
1 166 166 27 27 201502
1 1 167 2 29 201505
1 569 736 19 48 201507
1 312 1048 19 67 201508 55
2 364 364 27 27 201502
2 328 692 31 58 201503
2 242 934 29 87 201504 32
2 273 1207 31 118 201505
2 174 1381 30 148 201506
2 188 1569 25 173 201507
2 203 1772 25 198 201508
3 474 474 28 28 201502
3 521 505 31 59 201503
3 465 535 30 89 201504 18
3 473 566 31 120 201505
3 434 596 30 150 201506
3 404 627 31 181 201507
Ultimately what I desire is table as such:
ID InitalAvgValue
1 55
2 32
3 18
Thanks in advance for any help.
The crux is that you need a running total. There are several approaches to calculating running totals, but they have various tradeoffs between simplicity and performance. The "best" approach depends on the expected size of your data set and whether you are using SQL Server 2012 or an earlier version. The following article describes some different options along with the pros and cons:
http://sqlperformance.com/2012/07/t-sql-queries/running-totals
Here's a quick example using correlated subqueries, which may be reasonable for small data sets, but likely would not scale well to larger data:
SELECT
ID,
ROUND(AVG(CAST(CumulativeValue AS FLOAT) / CAST(CumulativeDaysOn AS FLOAT)), 1) AS Average
FROM
(
SELECT
ID,
Value,
DaysOn,
(SELECT SUM(Value) FROM ExampleTable t2 WHERE t1.ID = t2.ID and t2.YearMonth <= t1.YearMonth) AS CumulativeValue,
(SELECT SUM(DaysOn) FROM ExampleTable t2 WHERE t1.ID = t2.ID and t2.YearMonth <= t1.YearMonth) AS CumulativeDaysOn
FROM
ExampleTable t1
) AS ExampleWithTotals
WHERE
CumulativeDaysOn > 60 AND CumulativeDaysOn < 90
GROUP BY
ID
ORDER BY
ID
;
Output:
ID Average
1 15.6
2 10.7
3 16.4
I have a table set up, it is a customers' repayment schedule for a product.
My table consists of the following fields:
SaleID | PaymentDueDate | AmountDue
What I need for each Sale is:
SaleID | FirstPaymentDate | FirstPaymentValue | RegularPaymentValue | FinalPaymentvalue
Each repayment schedule has the following characteristics:
First payment amount is defined by the customer.
Regular and final payments are dictated by the balance remaining.
e.g
Sale = £200
Term = 14 Weeks (14 Payments)
First Payment = £50 (x1)
Regular Payment = £12 (x12)
Final Payment = £6 (x1)
Please can you advise how I can retrieve what I need.
ADDITION:
Code So Far:
This is what I have achieved so far, but this only gets me the First Payment and First Payment Date for each Sale:
SELECT
FP.SaleID, FP.FirstPaymentDate, RS.AmountDue AS FirstPayment
FROM
(
SELECT SaleID, MIN(PaymentDueDate)AS FirstPaymentDate
FROM RepaymentSchedule
GROUP BY SaleID
) AS FP
LEFT OUTER JOIN
RepaymentSchedule AS RS ON FP.SaleID = RS.SaleID AND FP.FirstPaymentDate = RS.PaymentDueDate ORDER BY RS.SaleID
ADDITION:
Data Sample:
£280 over 14 Weeks
SaleID PaymentDueDate AmountDue
41 2012-08-29 00:00:00.000 120.00
41 2012-09-05 00:00:00.000 12.30
41 2012-09-12 00:00:00.000 12.30
41 2012-09-19 00:00:00.000 12.30
41 2012-09-26 00:00:00.000 12.30
41 2012-10-03 00:00:00.000 12.30
41 2012-10-10 00:00:00.000 12.30
41 2012-10-17 00:00:00.000 12.30
41 2012-10-24 00:00:00.000 12.30
41 2012-10-31 00:00:00.000 12.30
41 2012-11-07 00:00:00.000 12.30
41 2012-11-14 00:00:00.000 12.30
41 2012-11-21 00:00:00.000 12.30
41 2012-11-28 00:00:00.000 12.40
I guess the FinalPaymentValue is not too crucial.
Really I just need to know how to work out what the regular payment is.
This should work:
SELECT pd.saleid,
pd.firstpaymentdate AS FirstPaymentDate,
(SELECT amountdue
FROM repaymentschedule
WHERE saleid = pd.saleid
AND paymentduedate = pd.firstpaymentdate) AS FirstPaymentValue,
(SELECT TOP 1 amountdue
FROM repaymentschedule
WHERE saleid = pd.saleid
AND paymentduedate <> pd.firstpaymentdate
AND paymentduedate <> pd.lastpaymentdate) AS RegularPaymentValue,
(SELECT amountdue
FROM repaymentschedule
WHERE saleid = pd.saleid
AND paymentduedate = pd.lastpaymentdate) AS FinalPaymentValue
FROM (SELECT saleid,
Min(paymentduedate) AS FirstPaymentDate,
Max(paymentduedate) AS LastPaymentDate
FROM repaymentschedule
GROUP BY saleid) pd
See it in action
Sample Result:
| SALEID | FIRSTPAYMENTDATE | FIRSTPAYMENTVALUE | REGULARPAYMENTVALUE | FINALPAYMENTVALUE |
--------------------------------------------------------------------------------------------------------
| 41 | August, 29 2012 00:00:00+0000 | 120 | 12.3 | 12.4 |
This question already has an answer here:
Closed 12 years ago.
Possible Duplicate:
How to join two tables
table 1
Date StartingAum
07/01/2010 120
08/01/2010 220
09/01/2010 320
table 2
Date DepContr withdra
01/01/2010 60 15
02/01/2010 70 25
03/01/2010 80 15
04/01/2010 30 89
05/01/2010 40 15
06/01/2010 25 85
07/01/2010 16 17
08/01/2010 19 21
09/01/2010 68 79
the output should be
Date StartingAum DepContr withdra
01/01/2010 0 60 15
02/01/2010 0 70 25
03/01/2010 0 80 15
04/01/2010 0 30 89
05/01/2010 0 40 15
06/01/2010 0 25 85
07/01/2010 120 16 17
08/01/2010 220 19 21
09/01/2010 320 68 79
i need the output exactly similar to that
DECLARE #Table1 table ([date] datetime, StartingAum int)
DECLARE #Table2 table ([date] datetime, DepContr int, withdra int)
INSERT #Table1 VALUES ('07/01/2010', 120)
INSERT #Table1 VALUES ('08/01/2010', 220)
INSERT #Table1 VALUES ('09/01/2010', 320)
INSERT #Table2 VALUES ('01/01/2010', 60 , 15)
INSERT #Table2 VALUES ('02/01/2010', 70 , 25)
INSERT #Table2 VALUES ('03/01/2010', 80 , 15)
INSERT #Table2 VALUES ('04/01/2010', 30 , 89)
INSERT #Table2 VALUES ('05/01/2010', 40 , 15)
INSERT #Table2 VALUES ('06/01/2010', 25 , 85)
INSERT #Table2 VALUES ('07/01/2010', 16 , 17)
INSERT #Table2 VALUES ('08/01/2010', 19 , 21)
INSERT #Table2 VALUES ('09/01/2010', 68 , 79)
SELECT
t2.[Date]
,ISNULL(t1.StartingAum, 0) AS StartingAum
,t2.DepContr
,t2.withdra
FROM #Table2 t2
LEFT JOIN #Table1 t1 ON t2.[Date] = t1.[Date]
ORDER BY t2.[Date]
OUTPUT:
Date StartingAum DepContr withdra
----------------------- ----------- ----------- -----------
2010-01-01 00:00:00.000 0 60 15
2010-02-01 00:00:00.000 0 70 25
2010-03-01 00:00:00.000 0 80 15
2010-04-01 00:00:00.000 0 30 89
2010-05-01 00:00:00.000 0 40 15
2010-06-01 00:00:00.000 0 25 85
2010-07-01 00:00:00.000 120 16 17
2010-08-01 00:00:00.000 220 19 21
2010-09-01 00:00:00.000 320 68 79
I think you should have a look at this page: http://www.w3schools.com/sql/sql_union.asp
That might be helpful?