SQL Passing Table Date for Date - db2

Wondering how I can pass the RECEIVEDDATETIME to the statement below. What am I missing in my SQL statement?
I added an inner join to my CALENDAR table and TESTDATA table to pass the T.RECEIVEDDATETIME as the date from the original example thanks to Mark Barinstein.
This statement gets the C.WORKDATE from my tblCalendar, but I need to have the T.RECEIVEDDATETIME to pass to get my desired "DUEDATE".
I created a "tblCalendar" because I read that it was easier to reference a Calendar for true workdays...to exclude weekends and holidays and to account for leap years. Unsure if this is the best practice, but seemed straight forward to not code for exceptions. So I created the tblCalendar that includes ALL DATEs from 2017 until 2050 and holidays. The data below only represents partially January 2019 as I haven't found a way to attach a table here:
tblCalendar (partial)
DATE NUMDAYOFWK DAYOFWK HOLIDAY
01/01/2019 3 Tuesday YES
01/02/2019 4 Wednesday
01/03/2019 5 Thursday
01/04/2019 6 Friday
01/05/2019 7 Saturday
01/06/2019 1 Sunday
01/07/2019 2 Monday
01/08/2019 3 Tuesday
01/09/2019 4 Wednesday
01/10/2019 5 Thursday
01/11/2019 6 Friday
01/12/2019 7 Saturday
01/13/2019 1 Sunday
01/14/2019 2 Monday
01/15/2019 3 Tuesday
01/16/2019 4 Wednesday
01/17/2019 5 Thursday
01/18/2019 6 Friday
01/19/2019 7 Saturday
01/20/2019 1 Sunday
01/21/2019 2 Monday YES
The tblTestData table holds the core data where I reference all fields needed for my reports.
tblTestData Columns (partial) - DeliveryDays would reference the 2nd parameter BusDayAdd that was noted in the previous SQL.
ID RECEIVEDDATE DeliveryDays Address
T-20190116-255 01/16/2019 2 1234 Address
T-20190117-255 01/17/2019 2 3657 Address
T-20190118-222 01/18/2019 2 9999 Address
T-20190119-255 01/19/2019 2
T-20190120-255 01/20/2019
T-20190121-255 01/21/2019
T-20190303-1 03/03/2019
The Desired end results would look like the following taking into account the RECEIVEDDATETIME in my tblTestData and reference the tblCalendar table to exclude weekends and holidays to give the correct due date.
ID RECEIVEDDATE DeliveryDays DueDate Address
T-20190116-255 1/16/2019 2 1/18/2019 1234 Address
T-20190117-255 1/17/2019 2 1/22/2019 3657 Address
T-20190118-222 1/18/2019 2 1/23/2019 9999 Address
T-20190119-255 1/19/2019 2 1/23/2019 10000 Address
T-20190120-255 1/20/2019 2 1/23/2019 10001 Address
T-20190121-255 1/21/2019 2 1/23/2019 10002 Address
T-20190121-256 1/22/2019 2 1/24/2019 10003 Address
T-20190303-1 3/3/2019 3 3/6/2019 10004 Address
T-20190121-257 3/15/2019 7 3/26/2019 10005 Address
I have tried various statements by rewriting the code to wrap the SQL string to pass the table "RECEIVEDDATETIME", but each time the "DUEDATE" comes back {NULL}.
SELECT T.ID, VARCHAR_format(T.RECEIVEDDATETIME, 'MM/DD/YYYY') RECDATE,
(select VARCHAR_FORMAT(WORKDATE,'MM/DD/YYYY') DUEDATE
from
(Select
WORKDATE, T.RECEIVEDDATETIME,
sum(case when C.HOLIDAY='YES' or C.NUMDAYOFWK in (7,1) then 0 else 1 end) over (order by C.WORKDATE) BUSDAYADD
from tblCALENDAR C
--ADDED INNER JOIN TO GET T.RECEIVEDDATETIME TO FEED AUTOMATICALLY FROM TESTDATA TABLE
INNER JOIN TESTDATA T
ON
VARCHAR_FORMAT(C.WORKDATE, 'MM/DD/YYYY') = VARCHAR_FORMAT(T.RECEIVEDDATETIME,'MM/DD/YYYY')
where C.WORKDATE > VARCHAR_FORMAT(T.RECEIVEDDATETIME,'MM/DD/YYYY')) -- 1-st PARAMETER TO CAPTURE RECEIVEDDATETIME
WHERE BUSDAYADD = ? -- 2-nd parameter to add the number of days needed to be added to RECEIVEDDATETIME
order by WORKDATE --3rd Parameter
fetch first 1 row only)
FROM TESTDATA T
WHERE ID = 'T-20190303-1'
When I run the SQL, I get {NULL} for my results for DUEDATE:
ID RECDATE DUEDATE
T-20190303-1 03/03/2019 {NULL}
The results should be:
ID RECDATE DUEDATE
T-20190303-1 03/03/2019 03/05/2019
Any help is appreciated.

You provided inconsistent data in both tables: your calendar ends too early for all records in the tblTestData table except the 1-st one.
Let me provide absolutely the same query, which I already sent earlier to your another question.
with tblCalendar (DATE, HOLIDAY) as (values
(date(to_date('01/16/2019', 'MM/DD/YYYY')), '')
, (date(to_date('01/17/2019', 'MM/DD/YYYY')), '')
, (date(to_date('01/18/2019', 'MM/DD/YYYY')), '')
, (date(to_date('01/19/2019', 'MM/DD/YYYY')), '')
, (date(to_date('01/20/2019', 'MM/DD/YYYY')), '')
, (date(to_date('01/21/2019', 'MM/DD/YYYY')), 'YES')
, (date(to_date('01/22/2019', 'MM/DD/YYYY')), '')
, (date(to_date('01/23/2019', 'MM/DD/YYYY')), '')
, (date(to_date('01/24/2019', 'MM/DD/YYYY')), '')
, (date(to_date('01/25/2019', 'MM/DD/YYYY')), '')
, (date(to_date('01/26/2019', 'MM/DD/YYYY')), '')
, (date(to_date('01/27/2019', 'MM/DD/YYYY')), '')
, (date(to_date('01/28/2019', 'MM/DD/YYYY')), '')
, (date(to_date('01/29/2019', 'MM/DD/YYYY')), '')
, (date(to_date('01/30/2019', 'MM/DD/YYYY')), '')
, (date(to_date('01/31/2019', 'MM/DD/YYYY')), '')
, (date(to_date('02/01/2019', 'MM/DD/YYYY')), '')
, (date(to_date('02/02/2019', 'MM/DD/YYYY')), '')
, (date(to_date('02/03/2019', 'MM/DD/YYYY')), '')
, (date(to_date('02/04/2019', 'MM/DD/YYYY')), '')
, (date(to_date('02/05/2019', 'MM/DD/YYYY')), '')
)
, tblTestData (ID, RECEIVEDDATE, DeliveryDays) as (values
('T-20190116-255', date(to_date('01/16/2019', 'MM/DD/YYYY')), 2)
, ('T-20190117-255', date(to_date('01/17/2019', 'MM/DD/YYYY')), 2)
, ('T-20190118-222', date(to_date('01/18/2019', 'MM/DD/YYYY')), 2)
, ('T-20190119-255', date(to_date('01/19/2019', 'MM/DD/YYYY')), 2)
, ('T-20190120-255', date(to_date('01/20/2019', 'MM/DD/YYYY')), 2)
, ('T-20190121-255', date(to_date('01/21/2019', 'MM/DD/YYYY')), 2)
, ('T-20190121-256', date(to_date('01/22/2019', 'MM/DD/YYYY')), 2)
, ('T-20190303-1' , date(to_date('01/23/2019', 'MM/DD/YYYY')), 3)
, ('T-20190121-257', date(to_date('01/24/2019', 'MM/DD/YYYY')), 7)
)
select m.*, t.date as DUEDATE
--, dayofweek(date) as DAYOFWK, dayname(date) as DAY
from tblTestData m
, table
(
select date
from table
(
select
date
, sum(case when HOLIDAY='YES' or dayofweek(date) in (7,1) then 0 else 1 end) over (order by date) as dn_
from tblCalendar t
where t.date > m.RECEIVEDDATE
)
where dn_ = m.DeliveryDays
fetch first 1 row only
) t;
The result is:
ID RECEIVEDDATE DAYS DUEDATE
-------------- ------------ ---- ----------
T-20190116-255 2019-01-16 2 2019-01-18
T-20190117-255 2019-01-17 2 2019-01-22
T-20190118-222 2019-01-18 2 2019-01-23
T-20190119-255 2019-01-19 2 2019-01-23
T-20190120-255 2019-01-20 2 2019-01-23
T-20190121-255 2019-01-21 2 2019-01-23
T-20190121-256 2019-01-22 2 2019-01-24
T-20190303-1 2019-01-23 3 2019-01-28
T-20190121-257 2019-01-24 7 2019-02-04

The SELECT of the DUEDATE is
INNER JOIN TESTDATA T
ON VARCHAR_FORMAT(C.WORKDATE, 'MM/DD/YYYY') = VARCHAR_FORMAT(T.RECEIVEDDATETIME,'MM/DD/YYYY')
where C.WORKDATE > VARCHAR_FORMAT(T.RECEIVEDDATETIME,'MM/DD/YYYY'))
This means you join on the Equality and then restrict to Workdate > Receiveddatetime
that might be the problem...

Related

How to fix more than one columns returned error in PostgreSQL

I was trying to write a code to fetch records from a table and group by some columns but the subquery returns "more than one" error.
When I write the codes independently, I get a great awesome result but combining them is a problem.
select
year as Season,
cal_scheme as Scheme,
(case when cal_scheme='Mt.Elgon' then '1000'
when cal_scheme='West Nile' then '2000'
when cal_scheme='Rwenzori' then '1500' else '' end) as Target,
min(today::date) as startdatetime,
max(today::date)-min(today::date) as No_of_days,
(select count(id) as id from
kcl_internal_edit where new_farmer='' or new_farmer is null
group by year, cal_scheme)as growers
from kcl_internal_edit
group by year, cal_scheme
The expected result is to be as follows:
Season Scheme Target Startdatetime No_of_days growers
2019 Mt.Elgon 1000 28-10-2019 5 5
2019 West Nile 2000 29-05-2019 10 1
2018 Mt.Elgon 1500 29-08-2018 207 3
Your query should look like this:
select
year as Season,
cal_scheme as Scheme,
(case when cal_scheme='Mt.Elgon' then '1000'
when cal_scheme='West Nile' then '2000'
when cal_scheme='Rwenzori' then '1500' else '' end) as Target,
min(today::date) as startdatetime,
max(today::date)-min(today::date) as No_of_days,
count(id) FILTER (WHERE new_farmer='' or new_farmer is null) as growers
from kcl_internal_edit
group by year, cal_scheme;
There is no need for a subselect!

update a column with the latest hiredate of an employee

I have an employee status history table.
I need to create one more column that should copy the min(EffectiveStartDate) on each row till the employee is rehired. I need to get the length of service of the employee where the date will be passed by UI.
How can i achieve in SQL server 2014
This answer has a few assumptions.
Assumptions
The data set is only for one Employee at a time. If it is not,
and there is another column, such as EmployeeID, then you will
want to specify that in a partition by clause inside the over
clause where my comments denote that.
That the EmployeeStatusCatalog values have the below meanings:
A: Active
L: Leave (of Absence)
I: Inactive
That a "Hire" or "Rehire" transaction is considered to happen
either at initial A status, or after an I status has ended.
Sample Data Setup
Did not include the EmployeeStatusId column, as my assumption is that it is not relevant to creating the expected outcome.
declare #employee table
(
EffectiveStartDate date not null
, EffectiveEndDate date not null
, EmployeeStatusCatalog char(1) not null
)
insert into #employee
values ('2008-02-29', '2016-05-31', 'A')
, ('2016-06-01', '2016-06-30', 'A')
, ('2016-07-01', '2016-07-30', 'L')
, ('2016-07-31', '2016-09-02', 'A')
, ('2016-09-03', '2016-10-09', 'I')
, ('2016-10-10', '2016-11-01', 'A')
, ('2016-11-02', '2016-12-02', 'L')
, ('2016-12-03', '2016-12-05', 'I')
, ('2016-12-06', '2016-12-06', 'A')
, ('2016-12-07', '2017-01-01', 'L')
, ('2017-01-02', '9999-12-31', 'A')
Answer
As you may or may not know, this is a classic gaps and islands scenario. Where each segment between Hire/Rehire dates is an island (no gaps in this example).
I used a CTE to move the I status forward one row (via LAG function), and then get the running count of the number of I rows to give each island a "ID" number.
After that, used a min function, while partitioning by the island number, to determine the minimum EffectiveStartDate for each island.
; with inactive_dts as
(
--move the I status forward one row
select e.EffectiveStartDate
, e.EffectiveEndDate
, e.EmployeeStatusCatalog
, lag(e.EmployeeStatusCatalog, 1, 'A') over (/*partion by here*/ order by e.EffectiveStartDate asc) as prev_status
from #employee as e
where 1=1
)
, active_island_nbr as
(
--get the running count of the number of I rows
select a.EffectiveStartDate
, a.EffectiveEndDate
, a.EmployeeStatusCatalog
, a.prev_status
, sum(case a.prev_status when 'I' then 1 else 0 end) over (/*partition by here*/ order by a.EffectiveStartDate asc) as ActiveIslandNbr
from inactive_dts as a
)
select min(a.EffectiveStartDate) over (partition by a.ActiveIslandNbr) as HireRehireDate
, a.EffectiveStartDate
, a.EffectiveEndDate
, a.EmployeeStatusCatalog
from active_island_nbr as a
Results
HireRehireDate EffectiveStartDate EffectiveEndDate EmployeeStatusCatalog
2008-02-29 2008-02-29 2016-05-31 A
2008-02-29 2016-06-01 2016-06-30 A
2008-02-29 2016-07-01 2016-07-30 L
2008-02-29 2016-07-31 2016-09-02 A
2008-02-29 2016-09-03 2016-10-09 I
2016-10-10 2016-10-10 2016-11-01 A
2016-10-10 2016-11-02 2016-12-02 L
2016-10-10 2016-12-03 2016-12-05 I
2016-12-06 2016-12-06 2016-12-06 A
2016-12-06 2016-12-07 2017-01-01 L
2016-12-06 2017-01-02 9999-12-31 A

T-SQL - Data Islands and Gaps - How do I summarise transactional data by month?

I'm trying to query some transactional data to establish the CurrentProductionHours value for each Report at the end of each month.
Providing there has been a transaction for each report in each month, that's pretty straight-forward... I can use something along the lines of the code below to partition transactions by month and then pick out the rows where TransactionByMonth = 1 (effectively, the last transaction for each report each month).
SELECT
ReportId,
TransactionId,
CurrentProductionHours,
ROW_NUMBER() OVER (PARTITION BY [ReportId], [CalendarYear], [MonthOfYear]
ORDER BY TransactionTimestamp desc
) AS TransactionByMonth
FROM
tblSource
The problem that I have is that there will not necessarily be a transaction for every report every month... When that's the case, I need to carry forward the last known CurrentProductionHours value to the month which has no transaction as this indicates that there has been no change. Potentially, this value may need to be carried forward multiple times.
Source Data:
ReportId TransactionTimestamp CurrentProductionHours
1 2014-01-05 13:37:00 14.50
1 2014-01-20 09:15:00 15.00
1 2014-01-21 10:20:00 10.00
2 2014-01-22 09:43:00 22.00
1 2014-02-02 08:50:00 12.00
Target Results:
ReportId Month Year ProductionHours
1 1 2014 10.00
2 1 2014 22.00
1 2 2014 12.00
2 2 2014 22.00
I should also mention that I have a date table available, which can be referenced if required.
** UPDATE 05/03/2014 **
I now have query which is genertating results as shown in the example below but I'm left with islands of data (where a transaction existed in that month) and gaps in between... My question is still similar but in some ways a little more generic - What is the best way to fill gaps between data islands if you have the dataset below as a starting point?
ReportId Month Year ProductionHours
1 1 2014 10.00
1 2 2014 12.00
1 3 2014 NULL
2 1 2014 22.00
2 2 2014 NULL
2 3 2014 NULL
Any advice about how to tackle this would be greatly appreciated!
Try this:
;with a as
(
select dateadd(m, datediff(m, 0, min(TransactionTimestamp))+1,0) minTransactionTimestamp,
max(TransactionTimestamp) maxTransactionTimestamp from tblSource
), b as
(
select minTransactionTimestamp TT, maxTransactionTimestamp
from a
union all
select dateadd(m, 1, TT), maxTransactionTimestamp
from b
where tt < maxTransactionTimestamp
), c as
(
select distinct t.ReportId, b.TT from tblSource t
cross apply b
)
select c.ReportId,
month(dateadd(m, -1, c.TT)) Month,
year(dateadd(m, -1, c.TT)) Year,
x.CurrentProductionHours
from c
cross apply
(select top 1 CurrentProductionHours from tblSource
where TransactionTimestamp < c.TT
and ReportId = c.ReportId
order by TransactionTimestamp desc) x
A similar approach but using a cartesian to obtain all the combinations of report ids/months.
in the first step.
A second step adds to that cartesian the maximum timestamp from the source table where the month is less or equal to the month in the current row.
Finally it joins the source table to the temp table by report id/timestamp to obtain the latest source table row for every report id/month.
;
WITH allcombinations -- Cartesian (reportid X yearmonth)
AS ( SELECT reportid ,
yearmonth
FROM ( SELECT DISTINCT
reportid
FROM tblSource
) a
JOIN ( SELECT DISTINCT
DATEPART(yy, transactionTimestamp)
* 100 + DATEPART(MM,
transactionTimestamp) yearmonth
FROM tblSource
) b ON 1 = 1
),
maxdates --add correlated max timestamp where the month is less or equal to the month in current record
AS ( SELECT a.* ,
( SELECT MAX(transactionTimestamp)
FROM tblSource t
WHERE t.reportid = a.reportid
AND DATEPART(yy, t.transactionTimestamp)
* 100 + DATEPART(MM,
t.transactionTimestamp) <= a.yearmonth
) maxtstamp
FROM allcombinations a
)
-- join previous data to the source table by reportid and timestamp
SELECT distinct m.reportid ,
m.yearmonth ,
t.CurrentProductionHours
FROM maxdates m
JOIN tblSource t ON t.transactionTimestamp = m.maxtstamp and t.reportid=m.reportid
ORDER BY m.reportid ,
m.yearmonth

Get First and Last Day of Any Year

I'm currently trying to get the first and last day of any year. I have data from 1950 and I want to get the first day of the year in the dataset to the last day of the year in the dataset (note that the last day of the year might not be December 31rst and same with the first day of the year).
Initially I thought I could use a CTE and call DATEPART with the day of the year selection, but this wouldn't partition appropriately. I also tried a CTE self-join, but since the last day or first day of the year might be different, this also yields inaccurate results.
For instance, using the below actually generates some MINs in the MAX and vice versa, though in theory it should only grab the MAX date for the year and the MIN date for the year:
;WITH CT AS(
SELECT Points
, Date
, DATEPART(DY,Date) DA
FROM Table
WHERE DATEPART(DY,Date) BETWEEN 363 AND 366
OR DATEPART(DY,Date) BETWEEN 1 AND 3
)
SELECT MIN(c.Date) MinYear
, MAX(c.Date) MaxYear
FROM CT c
GROUP BY YEAR(c.Date)
You want something like this for the first day of the year:
dateadd(year, datediff(year,0, c.Date), 0)
and this for the last day of the year:
--first day of next year -1
dateadd(day, -1, dateadd(year, datediff(year,0, c.Date) + 1, 0)
try this
for getting first day ,last day of the year && firstofthe next_year
SELECT
DATEADD(yy, DATEDIFF(yy,0,getdate()), 0) AS Start_Of_Year,
dateadd(yy, datediff(yy,-1, getdate()), -1) AS Last_Day_Of_Year,
DATEADD(yy, DATEDIFF(yy,0,getdate()) + 1, 0) AS FirstOf_the_NextYear
so putting this in your query
;WITH CT AS(
SELECT Points
, Date
, DATEPART(DY,Date) DA
FROM Table
WHERE DATEPART(DY,Date) BETWEEN
DATEPART(day,DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)) AND
DATEPART(day,dateadd(yy, datediff(yy,-1, getdate()), -1))
)
SELECT MIN(c.Date) MinYear
, MAX(c.Date) MaxYear
FROM CT c
GROUP BY YEAR(c.Date)
I should refrain from developing in the evenings because I solved it, and it's actually quite simple:
SELECT MIN(Date)
, MAX(Date)
FROM Table
GROUP BY YEAR(Date)
I can put these values into a CTE and then JOIN on the dates and get what I need:
;WITH CT AS(
SELECT MIN(Date) Mi
, MAX(Date) Ma
FROM Table
GROUP BY YEAR(Date)
)
SELECT c.Mi
, m.Points
, c.Ma
, f.Points
FROM CT c
INNER JOIN Table m ON c.Mi = m.Date
INNER JOIN Table f ON c.Ma = f.Date

T-SQL get months from ranges of date

I have a Table with id and start date and end date. i want insert into another table, end of each month between the start data and end date and the ID, e.g.
ID Start Date End Date
1 2012-01-01 2012-03-31
2 2012-10-01 2012-12-31
Results
ID MONTH END
1 2012-01-31
1 2012-02-29
1 2012-03-31
2 2012-10-31
2 2012-11-30
2 2012-12-31
This answer makes some assumptions - no end-dates greater than start-dates, but you should see how it works. It creates a recursive union CTE and uses that to figure out the end dates
CREATE TABLE #Dates
(
ID INT IDENTITY PRIMARY KEY,
START_DATE DATETIME2(0) NOT NULL,
END_DATE DATETIME2(0) NOT NULL
)
INSERT INTO #Dates VALUES ('2012-01-01', '2012-03-31'), ('2012-10-01','2012-12-31')
WITH MONTHS ([ID],[Month],[Date], [End])
AS
(
SELECT ID, DATEPART(m,START_DATE) AS [Month], START_DATE AS [Date], DATEADD(s,-1,DATEADD(m,DATEDIFF(m,0,START_DATE)+1,0)) as [End]
FROM #Dates
UNION ALL
SELECT D.ID, DATEPART(m,DATEADD(m,1,[Date])),DATEADD(m,1,[Date]), DATEADD(s,-1,DATEADD(m,DATEDIFF(m,0,DATEADD(m,1,[Date]))+1,0)) as [End]
FROM #Dates D
INNER JOIN MONTHS M
ON D.ID = M.ID
WHERE DATEADD(m,1,[Date]) < [END_DATE]
)
SELECT *
FROM MONTHS ORDER BY ID, Date
DROP TABLE #Dates