View Missing Numbers in a Sequence by date - sql-server-2005

I have a table name "Numerator" has 3 columns: No_Fact, Numerat and Date. Like this:
No_Fact Numerat Date
1207020015 000713 2012-07-02 00:00:00.000
1207020016 000720 2012-07-02 00:00:00.000
1207020017 000722 2012-07-02 00:00:00.000
1207020019 000723 2012-07-02 00:00:00.000
1207020022 000724 2012-07-02 00:00:00.000
1207020021 000733 2012-07-02 00:00:00.000
1207020020 000734 2012-07-02 00:00:00.000
1207020018 000735 2012-07-02 00:00:00.000
1208120001 000766 2012-08-12 00:00:00.000
1208120002 000769 2012-08-12 00:00:00.000
1208230001 000777 2012-08-23 00:00:00.000
1208230002 000780 2012-08-23 00:00:00.000
I want to view some missing numbers between numbers inside Numerat column, so I have tried using the code that is:
SELECT A.Numerat+1 AS 'MISSING FROM', MIN(B.Numerat)-1 AS 'TO'
FROM Numerator A, Numerator B
WHERE A.Numerat < B.Numerat
GROUP BY A.Numerat
HAVING A.Numerat+1 < MIN(B.Numerat)
ORDER BY 1
And result set:
MISSING FROM TO
714 719
721 721
725 732
736 765
767 768
770 776
778 779
This is a code that nice.
But the sequence numbers is too many, to be incompatible with the need to be concise. So I only want to show part rows today (2012-08-23) only, likes this:
MISSING FROM TO
778 779
I am very grateful to anyone who can decode it, Thank You.
There's an interesting thing when the next line in the insert in the table,
I show began on August 23 2012:
No_Fact Numerat Date
1208230001 000777 2012-08-23 00:00:00.000
1208230002 000780 2012-08-23 00:00:00.000
1208250001 000782 2012-08-25 00:00:00.000
1208250002 000783 2012-08-25 00:00:00.000
1208250003 000784 2012-08-25 00:00:00.000
1208250004 000785 2012-08-25 00:00:00.000
1208250005 000786 2012-08-25 00:00:00.000
1208250006 000788 2012-08-25 00:00:00.000
1208250007 000789 2012-08-25 00:00:00.000
1208250008 000790 2012-08-25 00:00:00.000
1208250009 000793 2012-08-25 00:00:00.000
1208250010 000794 2012-08-25 00:00:00.000
As my original purpose, that I want to see the missing numbers in the Numerat column and to summarily I only just wanted to show a line I enter this day alone, then with advice from bluefeet I use the following code:
SELECT A.Numerat+1 AS 'MISSING FROM', MIN(B.Numerat)-1 AS 'TO'
FROM Numerator A
INNER JOIN Numerator B
ON A.Numerat < B.Numerat
AND a.Tanggal = DATEADD(DAY, DATEDIFF(DAY, 0, GetDate()), 0)
GROUP BY A.Numerat
HAVING A.Numerat+1 < MIN(B.Numerat)
ORDER BY 1
and achieving results:
MISSING FROM TO
787 787
791 792
If we look carefully there is a missing number that does not appear, that is:
MISSING FROM TO
781 781
But we do not know exactly, including the date do it? Is 23 or 25. In this case it is not important to know exactly falls on what the date this missing number, obviously there is one number missing, it should be noted, and due on the 24th of all public holidays, let us enter it on the 25th. Can anyone help?

Here are a few different ways to do this query, all have incorporated the date into it:
SELECT A.Numerat+1 AS 'MISSING FROM', MIN(B.Numerat)-1 AS 'TO'
FROM test A
INNER JOIN test B
ON A.Numerat < B.Numerat
AND a.dt = DATEADD(DAY, DATEDIFF(DAY, 0, GetDate()), 0)
GROUP BY A.Numerat
HAVING A.Numerat+1 < MIN(B.Numerat)
ORDER BY 1
See SQL Fiddle with Demo
Or
;with cte as
(
select numerat, dt,
row_number() over(partition by dt order by numerat) rn
from test
where dt = DATEADD(DAY, DATEDIFF(DAY, 0, GetDate()), 0)
)
select c.Numerat+1 MissingFrom, t.Numerat-1 [To]
from cte c
inner join test t
on c.Numerat < t.Numerat
where c.rn = 1
See SQL Fiddle with Demo
Or you can use a MAX() aggregate with no date:
SELECT max([Missing From]), max([to])
FROM
(
SELECT A.Numerat+1 AS 'MISSING FROM', MIN(B.Numerat)-1 AS 'TO'
FROM test A
INNER JOIN test B
ON A.Numerat < B.Numerat
GROUP BY A.Numerat
HAVING A.Numerat+1 < MIN(B.Numerat)
) x
See SQL Fiddle with Demo

Related

Rolling Sum for Last 12 Months in SQL

I'm trying to get the rolling sum for the past 12 months (Oct 2019-Sept 2020, etc.)> So far, I figured out to get the current year total (which I also want), however, I'm stuck on a legit 12 month rolling sum.
SELECT DATEADD(MONTH, DATEDIFF(Month, 0, ENTRY_DATE), 0) AS Payout_Month, SUM(PRINCIPAL_AMT) Payout_amt,
SUM(SUM(PRINCIPAL_AMT)) OVER (PARTITION BY YEAR(ENTRY_DATE) ORDER BY MIN(ENTRY_DATE)) as YearRollingSum,
SUM(SUM(PRINCIPAL_AMT)) OVER (PARTITION BY Year(ENTRY_DATE)
ORDER BY MIN(ENTRY_DATE)
ROWS BETWEEN 12 PRECEDING AND 1 PRECEDING
) AS TwelveMonthRollingSum
FROM ACCOUNTHISTORY
WHERE LEFT(TOKEN_STRING, 4) LIKE '%Py%'
AND FOCUS_TELLER_ID = 6056
AND PRINCIPAL_AMT > 0 AND PRINCIPAL_AMT < 25
AND ENTRY_DATE >= '01/01/2019'
GROUP BY DATEADD(MONTH, DATEDIFF(Month, 0, ENTRY_DATE), 0), YEAR(ENTRY_DATE)
Order BY DATEADD(MONTH, DATEDIFF(Month, 0, ENTRY_DATE), 0)
here's what my current output looks like
Payout_Month Payout_amt YearRollingSum TwelveMonthRollingSum
2019-01-01 00:00:00.000 5696.50 5696.50 NULL
2019-02-01 00:00:00.000 11205.60 16902.10 5696.50
2019-03-01 00:00:00.000 23341.50 40243.60 16902.10
2019-04-01 00:00:00.000 25592.80 65836.40 40243.60
2019-05-01 00:00:00.000 28148.30 93984.70 65836.40
2019-06-01 00:00:00.000 27190.90 121175.60 93984.70
2019-07-01 00:00:00.000 25079.80 146255.40 121175.60
2019-08-01 00:00:00.000 30206.90 176462.30 146255.40
2019-09-01 00:00:00.000 28000.80 204463.10 176462.30
2019-10-01 00:00:00.000 29076.60 233539.70 204463.10
2019-11-01 00:00:00.000 29001.30 262541.00 233539.70
2019-12-01 00:00:00.000 28366.00 290907.00 262541.00
2020-01-01 00:00:00.000 32062.40 32062.40 NULL
2020-02-01 00:00:00.000 28526.70 60589.10 32062.40
2020-03-01 00:00:00.000 29056.50 89645.60 60589.10
2020-04-01 00:00:00.000 28016.00 117661.60 89645.60
2020-05-01 00:00:00.000 25173.30 142834.90 117661.60
2020-06-01 00:00:00.000 27646.10 170481.00 142834.90
2020-07-01 00:00:00.000 36083.70 206564.70 170481.00
2020-08-01 00:00:00.000 34872.20 241436.90 206564.70
2020-09-01 00:00:00.000 35727.10 277164.00 241436.90
2020-10-01 00:00:00.000 34030.80 311194.80 277164.00
AS you can see, it resets at the beginning of the year for the last column. Any ideas?
Basically, you want to remove the partition by clause from the rolling 12 month sum. I would also suggest a few optimizations to the query:
select
x.payout_month,
sum(ah.principal_amt) payout_amt,
sum(sum(ah.principal_amt)) over (
partition by year(x.payout_month)
order by x.payout_month
) as yearrollingsum,
sum(sum(ah.principal_amt)) over (
order by x.payout_month
rows between 12 preceding and 1 preceding
) as twelvemonthrollingsum
from accounthistory ah
cross apply (values (datefromparts(year(ah.entry_date), month(entry_date), 1))) x(ah.payout_month)
where
left(ah.token_string, 4) like '%py%'
and ah.focus_teller_id = 6056
and ah.principal_amt > 0 and principal_amt < 25
and ah.entry_date >= '20190101'
group by x.payout_month
order by x.payout_month
The main change is that the payout_month is computed only once, in a lateral join, using datefromparts(). You can then use it all over the query, and consistently in the order by clauses of the window functions.
Note that your strategy will fail to produce a proper results if you ever have a month without any sale (the rows clause of the window function will spread over the preceding month, which is not what you want). If that's something that may happen, then an alternative is a subquery, or another lateral join.

Why the Lookup function in SSRS is failing

I have two tables in my database as follows:
SELECT TOP 12 [Date], [Amount]
FROM [Db].[dbo].[final]
UNION
SELECT MAX([F1]) 'Date', SUM([F2]) 'Amount' FROM (
SELECT TOP 5 [F1], [F2]
FROM [Db].[dbo].[origtable]
WHERE [F1] IS NOT NULL
ORDER BY [F1] DESC
) c
SELECT TOP 12 [date_period2] AS [Date], [trnchargeamt] AS [Amount]
FROM [Db].[dbo].[othertable]
ORDER BY [date_period2] DESC
Which displays the following (first query is the top and second query is the bottom):
Date Amount
2013-07-31 00:00:00.000 9658254
2013-08-31 00:00:00.000 6659659
2013-09-30 00:00:00.000 14256326
2013-10-31 00:00:00.000 8912215
2013-11-30 00:00:00.000 9326659
2013-12-31 00:00:00.000 10211985
2014-01-31 00:00:00.000 8652365
2014-02-28 00:00:00.000 16256326
2014-03-31 00:00:00.000 24454342
2014-04-30 00:00:00.000 16345908
2014-05-30 00:00:00.000 6976515.77
2014-05-30 00:00:00.000 23578640
Date Amount
201406 42492.78
201405 1846703.374
201405 44390961.65
201404 45413273.91
201403 46943807.39
201402 33744223.24
201401 41630266.94
201312 40672523.92
201311 42465408.47
201310 47878730.59
201309 39444986.25
201308 40554376.28
In my SSRS I have it like the following way to display the charts:
I am using the following lookup function to display the first table with the second table:
=Lookup(Fields!Date.Value,Fields!Date.Value, Fields!Amount.Value, "WMGDailyPaymentsRed")
I get the following warning and nothing is displayed for the second chart:
[rsRuntimeErrorInExpression] The Y expression for the chart ‘Chart8’ contains an error:
Exception of type 'Microsoft.ReportingServices.ReportProcessing.ReportProcessingException_ComparisonError' was thrown.`
As you can see from the results of your select statement the first SQL.. has an actual Date and the second sql has a date period with just year and month.. hence comparing both would not work. You would need to modify your first query to format the date into YYYYMM format and then it would work in the lookup.

Computation of period Start date

I have a table that hold the start date and the end date of a financial period.
CHARGE_PERIOD_ID START_DATE END_DATE
13 2013-03-31 00:00:00.000 2013-04-27 00:00:00.000
14 2013-04-28 00:00:00.000 2013-05-25 00:00:00.000
15 2013-05-26 00:00:00.000 2013-06-29 00:00:00.000
16 2013-06-30 00:00:00.000 2013-07-27 00:00:00.000
17 2013-07-28 00:00:00.000 2013-08-24 00:00:00.000
18 2013-08-25 00:00:00.000 2013-09-28 00:00:00.000
19 2013-09-29 00:00:00.000 2013-10-26 00:00:00.000
20 2013-10-27 00:00:00.000 2013-11-23 00:00:00.000
21 2013-11-24 00:00:00.000 2013-12-28 00:00:00.000
22 2013-12-29 00:00:00.000 2014-01-25 00:00:00.000
23 2014-01-26 00:00:00.000 2014-02-22 00:00:00.000
24 2014-02-23 00:00:00.000 2014-03-29 00:00:00.000
The user of a report wants the current financial year split into 12 periods and want to give to feed in 2 parameters into the report , a year and a period number which will go into my sql . So something like #year=2014 #period=1 will be recieved . I have to write some sql to go to this table and set a period start date of 31/03/2014 and a period end date of 27/04/2014.
So in pseudo code:
Look up period 1 for 2014 and return period start date of 31/03/2014 and period end date of 27/04/2014.
#PERIOD_START_DATE = select the the first period that starts in March for the given year . all financial period starts in March.
#PERIOD_END_DATE = select the corresponding END_DATE from the table .
The question is how to begin to code this or my design approach? Should I create a function that calcualtes this or should I do a CTE and add a column which will hold the period number in the way they want etc .
Thinking about it more I think I need a mapping table . So the real question is can I do this without a mapping table ?
DECLARE #Year INT
DECLARE #Period INT
SET #Year= 2013
SET #Period = 1
;WITH CTE AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY
CASE WHEN MONTH([START_DATE])<3 THEN YEAR([START_DATE]) -1 ELSE YEAR([START_DATE]) END
ORDER BY
CASE WHEN MONTH([START_DATE])<3 THEN YEAR([START_DATE]) - 1 ELSE YEAR([START_DATE]) END
,CASE WHEN MONTH([START_DATE])<3 THEN MONTH([START_DATE]) + 12 ELSE MONTH([START_DATE]) END
) AS RN
FROM Periods
)
SELECT * FROM CTE
WHERE RN = #Period
AND CASE WHEN MONTH([START_DATE])<3 THEN YEAR([START_DATE]) -1 ELSE YEAR([START_DATE]) END = #Year
SQLFiddle DEMO

SQL Server query join several tables

I have a query that I don't think should be that hard to make, however, I've spent a lot of time on it now and still can't get it the way I want, so I hope someone here can help me.
Basically, I need to create a report that will give a value for each month, for each area. However, not all areas deliver data each month; in that case the view should return NULL for that month and area. So, the view need to look something like this:
Month Area Value
2012-08-01 Area1 2
2012-08-01 Area2 3
2012-09-01 Area1 3
2012-09-01 Area2 NULL
My data table looks something like this
Date Area Value
2012-08-01 Area1 2
2012-08-01 Area2 3
2012-09-01 Area1 3 -- Notice that Area2 is not present for September here
I have a table with all the available areas
Furthermore, I have created a table-valued function that returns all dates from a given date until now.
For example this statement
SELECT * FROM Periods_Months('2012-01-01')
would return 8 records like:
DateValue Year Month YearMonth
2012-01-01 00:00:00.000 2012 1 20121
2012-02-01 00:00:00.000 2012 2 20122
2012-03-01 00:00:00.000 2012 3 20123
2012-04-01 00:00:00.000 2012 4 20124
2012-05-01 00:00:00.000 2012 5 20125
2012-06-01 00:00:00.000 2012 6 20126
2012-07-01 00:00:00.000 2012 7 20127
2012-08-01 00:00:00.000 2012 8 20128
Based on the suggestions, my query now looks like this:
WITH months AS (
SELECT DateValue, YearMonth FROM Periods_Months('2011-01-01')
)
select m.DateValue
,CAST(DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,m.DateValue)+1,0)) AS Date) AS DateReported -- Get last day in month
,ResponseTime AS Value
,g.ExternalId
from GISDB.dbo.GisObjects g
CROSS JOIN months m
LEFT OUTER JOIN
( -- SELECT data from data table, grouped by area and month
SELECT dbo.YearMonth(CloseDate) AS YearMonth
,MAX(CloseDate) AS LastDate
,GisObjectId
,SUM(DATEDIFF(HH,RegDate,CloseDate)) AS ResponseTime -- calculate response time between start and end data (the value we need)
FROM DataTable
WHERE CloseDate IS NOT NULL
AND GisObjectId IS NOT NULL
GROUP BY GisObjectId, dbo.YearMonth(CloseDate) -- group by area and month
) c
ON g.ObjectId = c.GisObjectId AND c.YearMonth = m.YearMonth
WHERE g.CompanyId = 3 AND g.ObjectTypeId = 1 -- reduce the GIS objects that we compare to
ORDER BY m.DateValue, g.ObjectId
But the result is this (Value is always NULL):
DateValue DateReported Value ExternalId
2011-01-01 00:00:00.000 31-01-2011 NULL 9994
2011-01-01 00:00:00.000 31-01-2011 NULL 9993
2011-01-01 00:00:00.000 31-01-2011 NULL 9992
2011-01-01 00:00:00.000 31-01-2011 NULL 9991
2011-01-01 00:00:00.000 31-01-2011 NULL 2339
2011-01-01 00:00:00.000 31-01-2011 NULL 2338
2011-01-01 00:00:00.000 31-01-2011 NULL 2337
2011-01-01 00:00:00.000 31-01-2011 NULL 2336
2011-01-01 00:00:00.000 31-01-2011 NULL 2335
2011-01-01 00:00:00.000 31-01-2011 NULL 2334
2011-01-01 00:00:00.000 31-01-2011 NULL 2327
2011-01-01 00:00:00.000 31-01-2011 NULL 2326
2011-01-01 00:00:00.000 31-01-2011 NULL 2325
2011-01-01 00:00:00.000 31-01-2011 NULL 2324
2011-01-01 00:00:00.000 31-01-2011 NULL 2323
2011-01-01 00:00:00.000 31-01-2011 NULL 2322
etc.
I suppose you have a table with all your areas, which I call area_table.
WITH month_table AS (
SELECT dateValue FROM Periods_Months('2012-01-01')
)
select * from area_table
CROSS JOIN month_table
LEFT OUTER JOIN myValueTable
ON area_table.name = myValueTable.area
AND myValueTable.date = left(convert(varchar(30),month_table.dateValue,120),10)
ORDER BY myValueTable.Month, myValueTable.area
Suppose Areas is your table for all available areas, t - is your data table:
SELECT pm.dateValue,Ar.Area, t.value
FROM Periods_Months('2012-01-01') pm, Areas ar
left join t on (pm.dateValue=t.Date) and (ar.Area=t.Area)
order by pm.DateValue,ar.Area

SQL Server Query to group sequential dates

I have a table named Absence Details and I want to group sequential dates. Here, is the data
EID AbsenceType AbsenceStartDate AbsenceEndDate
769 Holiday 2012-06-25 00:00:00.000 2012-06-25 23:59:59.000
769 Holiday 2012-06-26 00:00:00.000 2012-06-26 23:59:59.000
769 Holiday 2012-09-03 00:00:00.000 2012-09-03 23:59:59.000
769 Holiday 2012-09-04 00:00:00.000 2012-09-04 23:59:59.000
769 Holiday 2012-09-05 00:00:00.000 2012-09-05 23:59:59.000
769 Holiday 2012-09-06 00:00:00.000 2012-09-06 23:59:59.000
769 Holiday 2012-09-07 00:00:00.000 2012-09-07 23:59:59.000
The result i am trying to get is
EID AbsenceType AbsenceStartDate AbsenceEndDate
769 Holiday 2012-06-25 00:00:00.000 2012-06-26 23:59:59.000
769 Holiday 2012-09-03 00:00:00.000 2012-09-07 23:59:59.000
Any help is much appreciated.
I have simplified your scenario to isolate the main problem. Let's supose this table with gaps:
with ns as (
select 1 as n union
select 2 as n union
select 3 as n union
select 8 as n union --gap
select 9 as n )
select *
into #ns
from ns;
Now, the result you are expecting for is:
ini fi
--- --
1 3
8 9
To get this results I massage the data in this way: first I create two views with start and end periods and second, I join both views to get final result. Notice that I join table with it self to locate starts and ends periods:
with
inis as -- identifying start periods
(
select n1.n
from #ns n1
left outer join #ns n2
on n1.n = n2.n + 1
where n2.n is null
),
fis as -- identifying ends periods
(
select n1.n
from #ns n1
left outer join #ns n2
on n1.n = n2.n - 1
where n2.n is null
)
select inis.n as ini, min( fis.n ) as fi -- joining starts and ends
from inis
inner join fis
on inis.n <= fis.n
group by inis.n
;
You can transfer this technique to your data and data types. If you have any issue translating query be free to ask.
Check query and results.
Here is the solution that worked for me.
SELECT EID, AbsenceType, MIN(AbsenceStartDate) AS AbsenceStartDate, MAX(AbsenceEndDate) AS AbsenceEndDate
FROM (SELECT EID, AbsenceType, AbsenceStartDate, AbsenceEndDate,
DATEADD(dd, - ROW_NUMBER() OVER (PARTITION BY EID, AbsenceType ORDER BY EID,AbsenceStartDate), AbsenceStartDate)
FROM AbsenceDetails
GROUP BY EID,AbsenceType,AbsenceStartDate,AbsenceEndDate
) a(EID, AbsenceType, AbsenceStartDate, AbsenceEndDate, Grp)
GROUP BY EID, AbsenceType, Grp;
I would do it as follows:
Identify the list of sequence of absence dates.
SELECT
ad1.EID, ad1.StartDate, ad2.EndDate
FROM
AbsenceDetails ad1
JOIN AbsenceDetails ad2
ON ad1.EID = ad2.EID
WHERE
DATEDIFF(ss, ad1.EndDate, ad2.StartDate) = 1
The results would be as follows:
769 2012-06-25 00:00:00.000 2012-06-26 23:59:59.000
769 2012-09-03 00:00:00.000 2012-09-04 23:59:59.000
769 2012-09-04 00:00:00.000 2012-09-05 23:59:59.000
769 2012-09-05 00:00:00.000 2012-09-06 23:59:59.000
769 2012-09-06 00:00:00.000 2012-09-07 23:59:59.000
Iterate through the list and identify the start and end duration of each stretch. This is better done at the app layer.
If I understood your question correctly you want to find continuous time intervals in your records.
The main problem will be identifying what actually constitutes continuous time interval:
If you are looking at absence at work than any sequence of
date1.09:00 to date1.18:00
date2.09:00 to date2.18:00
where date2 is a next business day after date1 can be considered continuous.
In your case it is relatively easy, but you will not be able to do it in a single query. At least I can't think of a way to do it right now.
P.S. "Islands and Gaps" algorithm suggested by "podiluska" will help you to write it in single query/stored procedure.