Can you pull from before a casted time - sql

sql newbie here. I'm trying to make a report for a tracking software where assets go into the database with a "Status" and a time stamp when they make that change.
What I'm trying to do is write a report that tells me when something hits the status 'complete' but its origin status was a month prior. How would I go about getting this to work properly with declaring a start and end date?
Declare #StartDate datetime
Declare #EndDate datetime
-- Set the start and end dates here yyyy-mm-dd --
set #StartDate = '2021-08-01'
set #EndDate = '2021-08-31'
Select *
from assetcurrent.dbo ac
inner join (select assetid, min(hdate) as [hdate] from history where hstatus = "created" group by assetid, hdate) ah
Where a.status in ('complete')
and cast(ac.hdate as date) <= cast(#EndDate as date)
and cast(ac.hdate as date) >= cast(#StartDate as date)
and ah.hdate < dateadd(month, -1, #StartDate)
order by [Report number]
Am I properly using dateadd here in the where clauses? I just get 0 results when I know there should be at least a few.
Thanks!

--and ah.hdate < dateadd(month, -1, #StartDate)
Was what i was trying and giving me failed results.
and MONTH(ac.hdate) != month(ah.hdate)
Is what i should have used instead. much simpler.

Correct me if I'm wrong.
Please clarify for month prior. Ex, something ordered on Sept 30 and Completed on Oct 1 is only 1 day, yet in the prior month. Is your question really what projects too longer than, ex: 30 days, to be completed? Much more specific condition. Also, are you looking for completed tasks such as Completed within September 2021 that took more than 30 days from the order/project start date?
select
AC.*,
PQ.DateCreated,
PQ.DateCompleted
from
( select
h.assetid,
MIN( h2.hdate) as DateCreated,
min(h.hdate) as DateCompleted,
from
history h
JOIN history h2
ON h.assetid = h2.assetid
AND h2.status = 'created'
AND h2.hdate < dateadd( month, -1, h.hdate )
where
h.hstatus = 'complete'
AND h.hdate >= '2021-09-01'
AND h.hdate < '2021-10-01'
group by
h.assetid ) PQ
JOIN AssetCurrent AC
ON PQ.assetid = AC.assetid
order by
ac.[Report Number]
So, in the query above, I am starting to look for all orders COMPLETED within the given time period (changed to Sept 1 to Sept 30). From that, I joined secondary to same history table again with alias h2 on the same asset ID, but this time based on when it was originally created. Now, I have a record of complete and created that I am looking at. From that, I can now apply the h2.date being LESS than a month prior to its creation. So in an example of an order was complete on Sept 27, it would only return the same asset if it was ordered prior to Oct 27, thus took more than a month to complete.
Once I had that part resolved, I could then join to your AssetCurrent table based on an implied key of assetid to get those details. While at it, I grabbed both context of Created and Completed Dates in the return set.

Related

How to setup a cumulative count grouped by month with underlying conditions

I've come across somewhat of an interesting scenario where I'm needing to aggregate enrollment counts and group them by the individual month and all subsequent months leading up to the completion date. The starting counter will be placed into the month when the enrollment began, and now I'm needing to set up a cumulative sum to carry out the single count.
Here's a couple of test records I'm working with
I've set up the following query to compile the date_month CTE to compile the full 12 months derived from my Start/End Range variables. I've then joined it to my test table in order to establish the Counter placements.
DECLARE #EnrollmentDateStart DATETIME = '2020-01-01'
DECLARE #EnrollmentDateEnd DATETIME = '2020-12-01'
;WITH CTE_Months(year_month) AS
(
SELECT DATEADD(MONTH, n, DATEADD(MONTH, DATEDIFF(MONTH, 0, #EnrollmentDateStart), 0))
FROM ( SELECT TOP (DATEDIFF(MONTH, #EnrollmentDateStart, #EnrollmentDateEnd) + 1)
n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
FROM sys.all_objects ORDER BY [object_id] ) AS n
)
SELECT
[Year] = YEAR(cm.year_month),
[Month] = DATENAME(MONTH, cm.year_month),
SUM(IIF(tt.[Enrollment Start Date] >= #EnrollmentDateStart,1,0)) AS EnrollmentCount
FROM CTE_Months cm
LEFT OUTER JOIN #TMP_Testing_Table tt
ON tt.[Enrollment Start Date] >= cm.year_month
AND tt.[Enrollment Start Date] < DATEADD(MONTH, 1, cm.year_month)
GROUP BY tt.Department, cm.year_month
At this stage, I'm pulling back the following results, so I now have the Enrollment Counts placed into the correct starting months derived from the Enrollment Start Date.
Now I'm trying to figure out what would be the best course of action to place the subsequent count for the additional months leading up to the Completion date?
For example - The first User (UserId: 1) was Enrolled in March, 2020, and Completed in August, 2020, so essentially I'm looking to produce the following result to reflect the number of months ranging between March <> July (Last month prior to Completion)
January: 0
February: 0
March: 1
April: 1
May: 1
June: 1
July: 1
August: 0
September: 0
October: 0
November: 0
December: 0
Thinking a cumulative total should be able to address the subsequent for the month by month range, however, I would then need to zero out the total for all subsequent months on and after the recorded Completion date for this record in question.
Seeing if I can get your thoughts/suggestions on how to address this scenario? Apologies if the information/explanation is confusing, but please let me know, and I'll do my best to elaborate.
....................
SELECT
[Year] = YEAR(cm.year_month),
[Month] = DATENAME(MONTH, cm.year_month),
count(tt.userid) AS EnrollmentCount
FROM CTE_Months cm
LEFT OUTER JOIN #TMP_Testing_Table tt on cm.year_month > eomonth([Enrollment Start Date], -1)
and cm.year_month <= tt.[Enrollment End Date]
GROUP BY cm.year_month

SQL - Use DATEADD in GROUP BY (?)

Im working on a query that is meant to retrive sales by hour, which it does. However, in the used database table all the timestamps are UTC +1, also for sales made in a country with UTC +2.
So what I'm trying to achive is a result that can be used by local business units (a parameter is set depending on who is looking at a report will determine the which country/store to display). So when it's sales in a UTC +2 country the datestamp needs to be modified with +1.
I'm thinking this can be done in the group by, perhaps by using a DATEADD together with a condition that checks the county name. For example, when the country is 'Greece' (column exists in the database), use a DATEADD to add 1 hour to the timestamp.
Is this a possible solution and if so, how is it done?
This is the GROUP BY im using at the moment:
SELECT
DATEPART(hour, sales.OrderDate) AS Hour,
SUM(CASE WHEN FORMAT(sales.OrderDate, 'yyyy-MM-dd') = Cast(GETDATE() AS date) THEN sales.SALES * (1 + sales.vv / 100) END) AS SALES,
COUNT(DISTINCT (CASE WHEN FORMAT(sales.OrderDate, 'yyyy-MM-dd') = Cast(GETDATE() AS date) THEN sales.OID END) ) AS CUSTOMERS,
MAX(CASE WHEN FORMAT(sv.Date_Time, 'yyyy-MM-dd') = Cast(GETDATE() AS date) THEN sv.CC END) AS VISITORS
FROM
[DW].[Tot_Sales] AS sales
LEFT JOIN [DW].[SV_24M] AS sv ON dateadd(hour, datediff(hour, 0, sales.[OrderDate]), 0) = dateadd(HOUR, 0, sv.Date_Time)
AND sales.SID = sv.SID
WHERE
sales.SID = #Store
AND FORMAT(sales.OrderDate, 'yyyy-MM-dd') > DATEADD(year, - 1, Cast(GETDATE() AS date))
GROUP BY
sales.SID,
DATEPART(hour, sales.OrderDate)
ORDER BY
DATEPART(hour, sales.OrderDate)
It is in the columns SALES and CUSTOMERS this needs to be applied, and they are from the table named sales. This is how the result looks:
Resultset
The issue is that the sales and customers occuring at hour 9 actually occured hour 10 in UCT +2. Visitors data arrives in local time (UCT +2 in this case), and therefore it's a mismatch between Visitors and Customers.
Sample data set:
Dataset
If you know the countries then you could make use of AT TIME ZONE in the query to set the appropriate local time.
Refer to this

Query to check number of records created in a month.

My table creates a new record with timestamp daily when an integration is successful. I am trying to create a query that would check (preferably automated) the number of days in a month vs number of records in the table within a time frame.
For example, January has 31 days, so i would like to know how many days in january my process was not successful. If the number of records is less than 31, than i know the job failed 31 - x times.
I tried the following but was not getting very far:
SELECT COUNT (DISTINCT CompleteDate)
FROM table
WHERE CompleteDate BETWEEN '01/01/2015' AND '01/31/2015'
Every 7 days the system executes the job twice, so i get two records on the same day, but i am trying to determine the number of days that nothing happened (failures), so i assume some truncation of the date field is needed?!
One way to do this is to use a calendar/date table as the main source of dates in the range and left join with that and count the number of null values.
In absence of a proper date table you can generate a range of dates using a number sequence like the one found in the master..spt_values table:
select count(*) failed
from (
select dateadd(day, number, '2015-01-01') date
from master..spt_values where type='P' and number < 365
) a
left join your_table b on a.date = b.CompleteDate
where b.CompleteDate is null
and a.date BETWEEN '01/01/2015' AND '01/31/2015'
Sample SQL Fiddle (with count grouped by month)
Assuming you have an Integers table*. This query will pull all dates where no record is found in the target table:
declare #StartDate datetime = '01/01/2013',
#EndDate datetime = '12/31/2013'
;with d as (
select *, date = dateadd(d, i - 1 , #StartDate)
from dbo.Integers
where i <= datediff(d, #StartDate, #EndDate) + 1
)
select d.date
from d
where not exists (
select 1 from <target> t
where DATEADD(dd, DATEDIFF(dd, 0, t.<timestamp>), 0) = DATEADD(dd, DATEDIFF(dd, 0, d.date), 0)
)
Between is not safe here
SELECT 31 - count(distinct(convert(date, CompleteDate)))
FROM table
WHERE CompleteDate >= '01/01/2015' AND CompleteDate < '02/01/2015'
You can use the following query:
SELECT DATEDIFF(day, t.d, dateadd(month, 1, t.d)) - COUNT(DISTINCT CompleteDate)
FROM mytable
CROSS APPLY (SELECT CAST(YEAR(CompleteDate) AS VARCHAR(4)) +
RIGHT('0' + CAST(MONTH(CompleteDate) AS VARCHAR(2)), 2) +
'01') t(d)
GROUP BY t.d
SQL Fiddle Demo
Explanation:
The value CROSS APPLY-ied, i.e. t.d, is the ANSI string of the first day of the month of CompleteDate, e.g. '20150101' for 12/01/2015, or 18/01/2015.
DATEDIFF uses the above mentioned value, i.e. t.d, in order to calculate the number of days of the month that CompleteDate belongs to.
GROUP BY essentially groups by (Year, Month), hence COUNT(DISTINCT CompleteDate) returns the number of distinct records per month.
The values returned by the query are the differences of [2] - 1, i.e. the number of failures per month, for each (Year, Month) of your initial data.
If you want to query a specific Year, Month then just simply add a WHERE clause to the above:
WHERE YEAR(CompleteDate) = 2015 AND MONTH(CompleteDate) = 1

Calculating Open incidents per month

We have Incidents in our system with Start Time and Finish Time and project name (and other info) .
We would like to have report: How many Incidents has 'open' status per month per project.
Open status mean: Not finished.
If incident is created in December 2009 and closed in March 2010, then it should be included in December 2009, January and February of 2010.
Needed structure should be like this:
Project Year Month Count
------- ------ ------- -------
Test 2009 December 2
Test 2010 January 10
Test 2010 February 12
....
In SQL Server:
SELECT
Project,
Year = YEAR(TimeWhenStillOpen),
Month = DATENAME(month, MONTH(TimeWhenStillOpen)),
Count = COUNT(*)
FROM (
SELECT
i.Project,
i.Incident,
TimeWhenStillOpen = DATEADD(month, v.number, i.StartTime)
FROM (
SELECT
Project,
Incident,
StartTime,
FinishTime = ISNULL(FinishTime, GETDATE()),
MonthDiff = DATEDIFF(month, StartTime, ISNULL(FinishTime, GETDATE()))
FROM Incidents
) i
INNER JOIN master..spt_values v ON v.type = 'P'
AND v.number BETWEEN 0 AND MonthDiff - 1
) s
GROUP BY Project, YEAR(TimeWhenStillOpen), MONTH(TimeWhenStillOpen)
ORDER BY Project, YEAR(TimeWhenStillOpen), MONTH(TimeWhenStillOpen)
Briefly, how it works:
The most inner subselect, that works directly on the Incidents table, simply kind of 'normalises' the table (replaces NULL finish times with the current time) and adds a month difference column, MonthDiff. If there can be no NULLs in your case, just remove the ISNULL expression accordingly.
The outer subselect uses MonthDiff to break up the time range into a series of timestamps corresponding to the months where the incident was still open, i.e. the FinishTime month is not included. A system table called master..spt_values is also employed there as a ready-made numbers table.
Lastly, the main select is only left with the task of grouping the data.
A useful technique here is to create either a table of "all" dates (clearly that would be infinite so I mean a sufficiently large range for your purposes) OR create two tables: one of all the months (12 rows) and another of "all" years.
Let's assume you go for the 1st of these:
create table all_dates (d date)
and populate as appropriate. I'm going to define your incident table as follows
create table incident
(
incident_id int not null,
project_id int not null,
start_date date not null,
end_date date null
)
I'm not sure what RDBMS you are using and date functions vary a lot between them so the next bit may need adjusting for your needs.
select
project_id,
datepart(yy, all_dates.d) as "year",
datepart(mm, all_dates.d) as "month",
count(*) as "count"
from
incident,
all_dates
where
incident.start_date <= all_dates.d and
(incident.end_date >= all_dates.d or incident.end_date is null)
group by
project_id,
datepart(yy, all_dates.d) year,
datepart(mm, all_dates.d) month
That is not going to quite work as we want as the counts will be for every day that the incident was open in each month. To fix this we either need to use a subquery or a temporary table and that really depends on the RDBMS...
Another problem with it is that, for open incidents it will show them against all future months in your all_dates table. adding a all_dates.d <= today solves that. Again, different RDBMSs have different methods of giving back now/today/systemtime...
Another approach is to have an all_months rather than all_dates table that just has the date of first of the month in it:
create table all_months (first_of_month date)
select
project_id,
datepart(yy, all_months.first_of_month) as "year",
datepart(mm, all_months.first_of_month) as "month",
count(*) as "count"
from
incident,
all_months
where
incident.start_date <= dateadd(day, -1, dateadd(month, 1, first_of_month)
(incident.end_date >= first_of_month or incident.end_date is null)
group by
project_id,
datepart(yy, all_months.first_of_month),
datepart(mm, all_months.first_of_month)

Get month from date time now month, Get year from date time now year and write specific day

Hello everyone i want search data from invoices and client by today date I'm using DateDiff() GETDATE() functions for example two tables
1 Client
- ID int
- Name Varcher
2 Invoice
- ID int
- ClientID int
- date Datetime
- Total money
query
Select * from client c
inner join invoice i on c.id = i.ClientID
WHERE DateDiff(dd, i.date, getdate()) = 0
when i run query the getdate() it is function to get date from date time now
the date content current month and current year and current day
when i run query today the date is 08-23-2010, when i run query tomorrow the date is 08-24-2010.
Q - the query do something equal date i want get month from date time now month, get year
from date time now year and write specific day only.
I do not understand your question clearly, but as far as I understand you want to have a time difference other than 0 days. You would then have to use other dateparts (first argument to DateDiff()) See the MS documentation of DateDiff for details, or the DateAdd function.
Furthermore, maybe you want to use
i.date between DateAdd(dd, -10, getDate) and getDate()
(which would show everything between ten days ago and today).
declare #day integer
select #day = 10
Select * from client c
inner join invoice i on c.id = i.ClientID
WHERE
DateDiff(dd, i.date, dateadd(dd,#day-datepart(dd,getdate()),getdate())) = 0