I am trying to create the data between two different dates.
Data in table looks like as shown below:
StartDate | EndDate | StudId | Active
-----------+---------------+-------------+-----------
01-01-2009 | 02-15-2009 | 12345 | Y
02-16-2009 | 03-15-2009 | 12345 | Y
03-16-2009 | 04-10-2009 | 12345 | N
04-11-2009 | 05-31-2009 | 12345 | Y
01-01-2009 | 02-15-2009 | 23642 | Y
02-16-2009 | 03-15-2009 | 23642 | Y
03-16-2009 | 04-10-2009 | 23642 | N
04-11-2009 | 05-31-2009 | 23642 | Y
and the data in table goes on with different Startdate, EndDate and StudID.
I am trying to get the result as shown below:
Startdate | StudID | Active
------------+----------+--------
01-01-2009 | 12345 | Y
01-02-2009 | 12345 | Y
01-03-2009 | 12345 | Y
01-04-2009 | 12345 | Y
. . .
. . .
02-15-2009 | 12345 | Y
02-16-2009 | 12345 | Y
As shown above I am trying to load the active data for student based on dates between Startdate and enddate.
We don't have any daily data using startdate and enddate we need to create daily data. If there is a gap between EndDate and next Startdate then the Active field should be '0' for those dates
Can someone suggest how to do this?
This requires calendar table and join
WITH calendar
AS (SELECT Min(StartDate) AS dates,
Max(EndDate) ed_date
FROM Yourtable
UNION ALL
SELECT Dateadd(dd, 1, dates),
ed_date
FROM calendar
WHERE dates < ed_date)
SELECT a.dates as Startdate,b.StudID,b.Active
FROM calendar a
JOIN Yourtable b
ON a.dates BETWEEN b.StartDate AND b.EndDate
ORDER BY dates
OPTION (maxrecursion 0)
Note: I have used Recursive CTE to generate dates. It is better to create physical calendar table and use it in queries like this
Live Demo
This should work.
declare #tmp date;
select #tmp = max(EndDate) from tmpTable;
print #tmp
;with cte as
(
select min(StartDate) over() as dd from tmpTable
union all select dateadd(day,1,dd) from cte where dd < #tmp
)
select distinct dd as StartDate, isnull(Studid, 12345), isnull(Active,0) as Active from tmpTable as t
right join cte as c on c.dd between t.startDate and t.enddate
where t.Studid = 12345 or t.studid is null
option (maxrecursion 0)
Related
I currently have the following table:
+-----+-----------------------------+------------------------------+
| ID | StartDate | EndDate |
+-----+-----------------------------+------------------------------|
| 1 | 2017-07-24 08:00:00.000 | 2017-07-29 08:00:00.000 |
| 2 | 2017-07-25 08:00:00.000 | 2017-07-28 08:00:00.000 |
| 3 | 2017-07-25 08:00:00.000 | 2017-07-26 08:00:00.000 |
+-----+-----------------------------+------------------------------+
I would like to know the count of the ID's that were not Closed on each date.
So for example, I wan't to know the count of open ID's on 2017-07-26 00:00:00.000. This would be all 3 in this case.
Another example: I wan't to know the count of open ID's on 2017-07-29 00:00:00.000. Which would be result to 1. Only ID=1 is Not yet closed at that date.
I have tried using another solution here on StackOverflow, but I can't quite figure why it is giving me false results.
declare #dt date, #dtEnd date
set #dt = getdate()-7
set #dtEnd = dateadd(day, 100, #dt);
WITH CTEt1 (SupportCallID, StartDate, EndDate, Onhold)
as
(SELECT SupportCallID
,OpenDate
,MAX(CASE WHEN StatusID IN('19381771-8E81-40C5-8E36-62A7DB0A2A99', '95C7A5FB-2389-4D14-9DAE-A08BFCC3B09A', 'D5429790-3B43-4462-9E1E-2466EA29AC74') then CONVERT(DATE, LastChangeDate) end) EndDate
,OnHold
FROM [ClienteleITSM_Prod_Application].[dbo].[SupportCall]
group by SupportCallID, OpenDate, OnHold
)
SELECT dates.myDate,
(SELECT COUNT(*)
FROM CTEt1
WHERE myDate BETWEEN StartDate and EndDate
)
FROM
(select dateadd(day, number, #dt) mydate
from
(select distinct number from master.dbo.spt_values
where name is null
) n
where dateadd(day, number, #dt) < #dtEnd) dates
If you use a cte to create a table of dates that span the range of dates in your source table, you can easily left join from that to your source table and count up the rows returned:
declare #t table(ID int,StartDate datetime,EndDate datetime);
insert into #t values (1,'2017-07-24 08:00:00.000','2017-07-29 08:00:00.000'),(2,'2017-07-25 08:00:00.000','2017-07-28 08:00:00.000'),(3,'2017-07-25 08:00:00.000','2017-07-26 08:00:00.000');
declare #StartDate datetime = (select min(StartDate) from #t);
declare #EndDate datetime = (select max(EndDate) from #t);
-- Table with 10 rows in to be joined together to create a large tally table (10 * 10 * 10 * etc)
with t(t) as (select t from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(t))
-- Add the row_number of the tally table to your start date to generate all dates within your data range
,d(d) as (select top(datediff(d,#StartDate,#EndDate)+1) dateadd(d,row_number() over (order by (select null))-1,#StartDate) from t t1,t t2,t t3)
select d.d
,count(t.ID) as OpenIDs
from d
left join #t as t
on(d.d between cast(t.StartDate as date) and t.EndDate)
group by d.d
order by d.d;
Output:
+-------------------------+---------+
| d | OpenIDs |
+-------------------------+---------+
| 2017-07-24 08:00:00.000 | 1 |
| 2017-07-25 08:00:00.000 | 3 |
| 2017-07-26 08:00:00.000 | 3 |
| 2017-07-27 08:00:00.000 | 2 |
| 2017-07-28 08:00:00.000 | 2 |
| 2017-07-29 08:00:00.000 | 1 |
+-------------------------+---------+
I have three tables. I want to get data from all those tables and put it in a virtual table. i am using SQL Server 2012.
Sorry if my format or tags are wrong because I m getting error Stack overflow requires external javascrip from another source domain, which is blocked of failed to load.
Booking Table
BookingId | date
======================
2 | 7/1/2017 (MM/dd/yyyy)
3 | 7/1/2017
BookingCost Table
Id | bookinId | Cost
==========================
1 | 2 | 2000
2 | 3 | 4000
Expense Table
Id | ExpenseCost | Date
======================
1 | 1400 | 7/2/2017 (MM/dd/yyyy)
2 | 1422 | 7/1/2017
3 | 4000 | 6/3/2017
I want to get Monthly result like following Table.
Date | Expense | Bookings
===================================
jan/2017 | 0 | 0
feb/2017 | 0 | 0
. | . | .
. | . | .
. | . | .
jun/2017 | 4000 | 0
jul/2017 | 2822 | 6000
. | . | .
. | . | .
. | . | .
How is something like this (assuming your dates are DATE types and not VARCHAR - otherwise you could convert them).
SELECT COALESCE(EXPENSE.MONTH, BOOKINGS.MONTH) [Date], EXPENSE.Cost Expense, BOOKINGS.Cost Bookings
FROM (
SELECT DATEADD(DD,1-DAY([date]),[date]) MONTH, SUM(Cost) Cost
FROM Booking
INNER JOIN BookingCost
ON Booking.BookingID = BookingCost.BookingID
GROUP BY DATEADD(DD,1-DAY([date]),[date])
) BOOKINGS
FULL JOIN (
SELECT DATEADD(DD,1-DAY([date]),[date]) MONTH, SUM(ExpenseCost) Cost
FROM Expense
GROUP BY DATEADD(DD,1-DAY([date]),[date])
) EXPENSE
ON EXPENSE.MONTH = BOOKINGS.MONTH
ORDER BY 1
To also get the 0 counts, you could left join the totals to a tally table which has all the months for the year.
The sql is using FORMAT to transform the Date
For example:
;WITH MONTHS AS
(
select
[Year], [Month],
format(datefromparts([Year],[Month],1),'MMM/yyyy') as [MonthYear]
from (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) m([Month])
cross join (values (2017)) y([Year])
)
select
m.[MonthYear] as [Date],
coalesce(e.TotalExpense,0) as Expense,
coalesce(bc.TotalCost,0) as Bookings
from MONTHS m
left join (
select
datepart(year,[Date]) as [Year],
datepart(month,[Date]) as [Month],
sum(ExpenseCost) as TotalExpense
from Expense
where datepart(year,[Date]) in (select distinct [Year] from MONTHS)
group by datepart(year,[Date]), datepart(month,[Date])
)e on (e.[Year] = m.[Year] and e.[Month] = m.[Month])
left join (
select
datepart(year,b.[date]) as [Year],
datepart(month,b.[date]) as [Month],
sum(c.Cost) as TotalCost
from Booking b
join BookingCost c on c.BookingId = b.BookingId
where datepart(year,b.[date]) in (select distinct [Year] from MONTHS)
group by datepart(year,b.[date]), datepart(month,b.[date])
) bc
on (bc.[Year] = m.[Year] and bc.[Month] = m.[Month])
order by m.[Year], m.[Month];
Test data I used
declare #Booking table (BookingId int, [date] date);
insert into #Booking (BookingId,[date]) values (2,'2017-07-01'),(3,'2017-07-01');
declare #BookingCost table (Id int, BookingId int, Cost int);
insert into #BookingCost (Id, BookingId, Cost) values (1,2,2000),(2,3,4000);
declare #Expense table (Id int, ExpenseCost int, [Date] date);
insert into #Expense (Id, ExpenseCost, [Date]) values
(1,1400,'2017-07-02'),(2,1422,'2017-07-01'),(3,4000,'2017-06-03');
I Want to create loop between two days and USERID
For Example
If I Choose between 01-01-2017 and 05-01-2017
procedure already insert into my table days between for each user
every single user has a same days
I've tried the following
DECLARE #USERID int,#S_Date date,#E_Date date
SELECT #S_Date = #S_Date, #USERID = #USERID
While #S_Date <= #E_Date
begin
select #S_Date, #USERID
while #USERID < 3
begin
INSERT INTO myTable values(#S_Date, #USERID)
select #S_Date as S, #USERID as U
set #USERID = #USERID + 1
end
set #S_Date = DateAdd(Day, 1, #S_Date)
end
declare #fromdate date = '20170101'
declare #thrudate date = '20170105'
declare #from_UserId int = 1;
declare #thru_UserId int = 3;
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, dates as (
select top (datediff(day, #fromdate, #thrudate)+1)
[Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,#fromdate))
from n as deka cross join n as hecto cross join n as kilo
cross join n as tenK cross join n as hundredK
order by [Date]
)
insert into myTable ([Date],UserId)
select [Date], u.UserId
from dates
cross join (
select UserId
from Users u
where u.UserId >= #from_UserId
and u.UserId <= #thru_UserId
) u;
The above uses stacked ctes to generate a numbers table, and uses that numbers table to generate dates between the date range provided.
It might not look as simple as using a while loop, but it will perform much better.
rextester demo: http://rextester.com/HHY62656
returns:
+------------+--------+
| date | userid |
+------------+--------+
| 2017-01-01 | 1 |
| 2017-01-02 | 1 |
| 2017-01-03 | 1 |
| 2017-01-04 | 1 |
| 2017-01-05 | 1 |
| 2017-01-01 | 2 |
| 2017-01-02 | 2 |
| 2017-01-03 | 2 |
| 2017-01-04 | 2 |
| 2017-01-05 | 2 |
| 2017-01-01 | 3 |
| 2017-01-02 | 3 |
| 2017-01-03 | 3 |
| 2017-01-04 | 3 |
| 2017-01-05 | 3 |
+------------+--------+
Number and Calendar table reference:
Generate a set or sequence without loops - 2 - Aaron Bertrand
The "Numbers" or "Tally" Table: What it is and how it replaces a loop - Jeff Moden
Creating a Date Table/Dimension in sql Server 2008 - David Stein
Calendar Tables - Why You Need One - David Stein
Creating a date dimension or calendar table in sql Server - Aaron Bertrand
I have a table with a history of assigning Eployee Type to a Work item, like follows:
| WorkItemID | EmployeeTypeID | ValidFrom | ValidTo |
| 1 | 1 | 2017-03-01 12:19:20.000 | 2017-03-05 14:11:20.000 |
| 1 | 1 | 2017-03-10 17:00:20.000 | NULL |
| 1 | 2 | 2017-05-12 12:19:20.000 | 2017-05-29 14:11:20.000 |
| 1 | 2 | 2017-07-01 12:19:20.000 | NULL |
| 2 | 1 | 2017-01-01 15:19:20.000 | 2017-03-01 11:29:20.000 |
| 2 | 1 | 2017-04-03 16:19:20.000 | NULL |
NULL means that there's no End date for the last assignment and it is still valid.
I also have a table with a history of assigning Eployee Type to an Employee:
| EmployeeID | EmployeeTypeID | ValidFrom | ValidTo |
| 1 | 1 | 2017-01-01 12:19:20.000 | 2017-03-05 14:11:20.000 |
| 1 | 2 | 2017-03-05 14:11:20.000 | NULL |
| 2 | 1 | 2016-05-05 15:19:20.000 | 2017-03-01 11:29:20.000 |
| 2 | 2 | 2017-03-01 11:29:20.000 | NULL |
For a given EmployeeID and WorkItemID, I need to select a minimum date within these date ranges where their EmployeeTypeID matched (if there is any).
For example, for EmployeeID = 1 And WorkItemID = 1 the minimum date when their Employeetypes matched is 2017-03-01 (disregard the time part).
How do I write an SQL query to join these two tables correctly and select the desired date?
The following way appeared to be correct for me:
Firstly, I select Min Date from table 1 that match with table 2 by date ranges and they should overlap as well:
DECLARE #MinDate1 datetime
DECLARE #MinDate2 datetime
SELECT #MinDate1 =
(SELECT MIN(t1.ValidFrom)
FROM Table1 t1
JOIN Table2 t2 ON t1.EmployeeTypeID = t2.EmployeeTypeID
WHERE t1.WorkItemID = 1 AND t2.EmployeeID = 1
AND (t1.ValidFrom <= t2.ValidTo OR t2.ValidTo IS NULL)
AND (t1.ValidTo >= t2.ValidFrom OR t1.ValidTo IS NULL))
Then I select Min Date from table 2 that match with table 1 by date ranges and they should overlap as well:
SELECT #MinDate2 =
(SELECT MIN(t2.ValidFrom)
FROM Table1 t1
JOIN Table2 t2 ON t1.EmployeeTypeID = t2.EmployeeTypeID
WHERE t1.WorkItemID = 1 AND t2.EmployeeID = 1
AND (t1.ValidFrom <= t2.ValidTo OR t2.ValidTo IS NULL)
AND (t1.ValidTo >= t2.ValidFrom OR t1.ValidTo IS NULL))
And finaly, I select the max date of two which would be the min date when the two ranges actually overlap and have the same EmployeeTypeID
SELECT CASE WHEN #MinDate1 > #MinDate2 THEN #MinDate1 ELSE #MinDate2 END AS MinOverlapDate
The output would be:
| MinOverlapDate |
| 2017-03-01 12:19:20.000 |
So it should be something like this:
SELECT MIN(Date)
FROM table1 t1
JOIN table2 t2 ON t1.EmployeeTypeID = t2.EmployeeTypeID
WHERE t1.EmployeeID = givenValue AND t2.WorkitemID = givenValue
But again if you dont know from which table the result goes you cant write a query for that.
What you should do is do at least 3 tables or maybe more
Would contain Employee informations
Items jobs dates whatever is connected to WORK
Some connection between them (Emp 1 has Work 2) (Emp 2 has Work 4) and so on
You CANNOT have same values in two tables without knowing from which one you want to get tha data!
OR .. You can do it into one table.
Columns: WorkItem | EmployeeID | EmployeeType | Date | Date
Actually, my variant still does not work correctly. The #MinDate1 and #MinDate2 should be compared by each EmployeeTypeID one by one. There it was compared independently.
Here is correct variant of solving this problem:
SELECT MIN(CASE WHEN t1.ValidFrom > t2.ValidFrom THEN t1.ValidFrom ELSE t2.ValidFrom END) AS MinOverlapDate
FROM Table1 t1
JOIN Table2 t2 ON t1.EmployeeTypeID = t2.EmployeeTypeID
WHERE t1.WorkItemID = 1 AND t2.EmployeeID = 1
AND (t1.ValidFrom <= t2.ValidTo OR t2.ValidTo IS NULL)
AND (t1.ValidTo >= t2.ValidFrom OR t1.ValidTo IS NULL)
Don't use >=, <=, = or between when comparing datetime fields. Since all of the mention operator would check against time as well. You would want to use datediff to check against the smallest interval according to your needs
select
Min_Overlap_Per_Section = (select MAX(ValidFrom)
FROM (VALUES (t1.ValidFrom), (t2.ValidFrom)) as ValidFrom(ValidFrom))
, Section_From = (select MAX(ValidFrom)
FROM (VALUES (t1.ValidFrom), (t2.ValidFrom)) as ValidFrom(ValidFrom))
, Section_To = (select MIN(ValidTo)
FROM (VALUES (t1.ValidTo), (t2.ValidTo)) as ValidTo(ValidTo))
from Table1
JOIN Table2 t2 ON t1.EmployeeTypeID = t2.EmployeeTypeID
where (
datediff(day, t1.ValidFrom, t2.ValidTo) >= 0
or t2.ValidTo IS NULL
)
and (
datediff(day, t2.ValidFrom, t1.ValidTo) >= 0
or t1.ValidTo IS NULL
)
I need to write a statement joining two tables based on dates.
Table 1 contains time recording entries.
+----+-----------+--------+---------------+
| ID | Date | UserID | DESC |
+----+-----------+--------+---------------+
| 1 | 1.10.2010 | 5 | did some work |
| 2 | 1.10.2011 | 5 | did more work |
| 3 | 1.10.2012 | 4 | me too |
| 4 | 1.11.2012 | 4 | me too |
+----+-----------+--------+---------------+
Table 2 contains the position of each user in the company. The ValidFrom date is the date at which the user has been or will be promoted.
+----+-----------+--------+------------+
| ID | ValidFrom | UserID | Pos |
+----+-----------+--------+------------+
| 1 | 1.10.2009 | 5 | PM |
| 2 | 1.5.2010 | 5 | Senior PM |
| 3 | 1.10.2010 | 4 | Consultant |
+----+-----------+--------+------------+
I need a query which outputs table one with one added column which is the position of the user at the time the entry has been made. (the Date column)
All date fileds are of type date.
I hope someone can help. I tried a lot but don't get it working.
Try this using a subselect in the where clause:
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE TimeRecord
(
ID INT,
[Date] Date,
UserID INT,
Description VARCHAR(50)
)
INSERT INTO TimeRecord
VALUES (1,'2010-01-10',5,'did some work'),
(2, '2011-01-10',5,'did more work'),
(3, '2012-01-10', 4, 'me too'),
(4, '2012-11-01',4,'me too')
CREATE TABLE UserPosition
(
ID Int,
ValidFrom Date,
UserId INT,
Pos VARCHAR(50)
)
INSERT INTO UserPosition
VALUES (1, '2009-01-10', 5, 'PM'),
(2, '2010-05-01', 5, 'Senior PM'),
(3, '2010-01-10', 4, 'Consultant ')
Query 1:
SELECT TR.ID,
TR.[Date],
TR.UserId,
TR.Description,
UP.Pos
FROM TimeRecord TR
INNER JOIN UserPosition UP
ON UP.UserId = TR.UserId
WHERE UP.ValidFrom = (SELECT MAX(ValidFrom)
FROM UserPosition UP2
WHERE UP2.UserId = UP.UserID AND
UP2.ValidFrom <= TR.[Date])
Results:
| ID | Date | UserId | Description | Pos |
|----|------------|--------|---------------|-------------|
| 1 | 2010-01-10 | 5 | did some work | PM |
| 2 | 2011-01-10 | 5 | did more work | Senior PM |
| 3 | 2012-01-10 | 4 | me too | Consultant |
| 4 | 2012-11-01 | 4 | me too | Consultant |
You can do it using OUTER APPLY:
SELECT ID, [Date], UserID, [DESC], x.Pos
FROM table1 AS t1
OUTER APPLY (
SELECT TOP 1 Pos
FROM table2 AS t2
WHERE t2.UserID = t1.UserID AND t2.ValidFrom <= t1.[Date]
ORDER BY t2.ValidFrom DESC) AS x(Pos)
For every row of table1 OUTER APPLY operation fetches all table2 rows of the same user that have a ValidFrom date that is older or the same as [Date]. These rows are sorted in descending order and the most recent of these is finally returned.
Note: If no match is found by the OUTER APPLY sub-query then a NULL value is returned, meaning that no valid position exists in table2 for the corresponding record in table1.
Demo here
This works by using a rank function and subquery. I tested it with some sample data.
select sub.ID,sub.Date,sub.UserID,sub.Description,sub.Position
from(
select rank() over(partition by t1.userID order by t2.validfrom desc)
as 'rank', t1.ID as'ID',t1.Date as'Date',t1.UserID as'UserID',t1.Descr
as'Description',t2.pos as'Position', t2.validfrom as 'validfrom'
from temployee t1 inner join jobs t2 on -- replace join tables with your own table names
t1.UserID=t2.UserID
) as sub
where rank=1
This query would work
select t1.*,t2.pos from Table1 t1 left outer join Table2 t2 on
t1.Date=t2.Date and t1.UserID=t2.UserID