MS Access Transform / Pivot to SQL Server with grouping - sql-server

I know similar questions have been asked before, however the grouping is throwing me off and hopefully I can get some help. I currently have a working MS Access model that does custom calculations to an Oracle connection, however my data is now pushing the 2GB mark with the custom calculations and trying SQL Server Express as an alternative and need a bit of help.
The database structure is from a 3rd party application so have to live with what I have - its UGLY.
ID | ATRSTUDY | ENDTIME | NAME | COUNT
---+----------+--------------------+-------------+-------
1 | A | Jan 1, 18 00:15 | NorthBound | 10
2 | A | Jan 1, 18 00:15 | SouthBound | 20
3 | A | Jan 1, 18 00:15 | Both Dir | 30
4 | B | Jan 1, 18 00:15 | EastBound | 30
5 | B | Jan 1, 18 00:15 | WestBound | 40
5 | B | Jan 1, 18 00:15 | Both Dir | 70
My existing MS-Access SQL is:
TRANSFORM Sum(CountData_Local.Count) AS SumOfCount
SELECT CountData_Local.ATRSTUDY, DateValue([CountData_Local]![ENDTIME]) AS CNTDATE, CountData_Local.ENDTIME
FROM DataVariables, CountData_Local
GROUP BY CountData_Local.ATRSTUDY, DateValue([CountData_Local]![ENDTIME]), CountData_Local.ENDTIME
PIVOT IIf([NAME]="EastBound" Or [NAME]="NorthBound" Or [NAME]="First Direction","C1",IIf([NAME]="WestBound" Or [NAME]="SouthBound" Or [NAME]="Second Direction","C2",IIf([NAME]="Both Dir","TC")));
The end result I am try to achieve is a pivot table that combines the 3 rows into one row as follows:
ATRSTUDY | CNDDATE | ENDTIME | C1 | C2 | TC
---------+-----------+---------+----+----+---
A | Jan 1, 18 | 00:15 |10 | 20 | 30
B | Jan 1, 18 | 00:15 |30 | 40 | 70
Thanks in advance!

Here is my SQL to date. Still having a bit of trouble with showing date and time and adding a filter for a date range which I currently have filtered out.
Perfect situation, date would be a between A and B.
Select atrstudy, endtime, sum(c1), sum(c2), sum(tc) from
(
with t as
(
select
sdata.oid,
sdata.atrstudy,
case upper(datatyp.name)
when 'EASTBOUND' then 'C1'
when 'NORTHBOUND' then 'C1'
when 'FIRST DIRECTION' then 'C1'
when 'WESTBOUND' then 'C2'
when 'SOUTHBOUND' then 'C2'
when 'SECOND DIRECTION' then 'C2'
when 'BOTH DIRECTIONS' then 'TC'
else datatyp.name
end as namedir,
/* trunc(datadtlbs.starttime) as starttime, */
endtime as endtime,
datadtlbs.count as counttot,
sdata.gcrecord
from roy.atrstudydata sdata
left outer join roy.atrstudydatadtl datadtl
on sdata.oid = datadtl.parent
join roy.atrstudydattyp datatyp
on sdata.direction = datatyp.oid
left outer join roy.atrstudydatadtlbs datadtlbs
on datadtl.oid = datadtlbs.oid
/* where trunc(datadtlbs.endtime) > '31-DEC-15' can remove this where clause*/)
select atrstudy, endtime, C1, C2, TC from t
pivot
(sum(COUNTtot) for NAMEdir in ('C1' as C1, 'C2' as C2, 'TC' as TC)))
group by atrstudy, endtime
order by atrstudy, endtime

MS SQL Server has PIVOT to do such things.
And CASE can be used to group those names as codes.
Example snippet:
--
-- Using a table variable for easy testing in the example
--
declare #Count_data table (id int identity(1,1) primary key, ATRSTUDY varchar(1), ENDTIME datetime, NAME varchar(30), [COUNT] int);
insert into #Count_data (ATRSTUDY, ENDTIME, NAME, [COUNT]) values
('A','2018-01-01T18:00:15','NorthBound',10),
('A','2018-01-01T18:00:15','SouthBound',20),
('A','2018-01-01T18:00:15','Both Directions',30),
('B','2018-01-01T18:00:15','EastBound',30),
('B','2018-01-01T18:00:15','WestBound',40),
('B','2018-01-01T18:00:15','Both Directions',70);
select ATRSTUDY, ENDDATE, ENDTIME, [C1], [C2], [TC]
from (
select
d.ATRSTUDY,
cast(d.ENDTIME as date) as ENDDATE,
left(cast(d.ENDTIME as time),5) as ENDTIME,
(case -- if the Name column has a Case-Insensitive collation then lower or upper case won't matter.
when d.Name in ('eastbound', 'northbound', 'first direction') then 'C1'
when d.Name in ('westbound', 'southbound', 'second direction') then 'C2'
when d.Name like 'both dir%' then 'TC'
else d.Name
end) as ColName,
d.[count]
From #Count_data d
) as q
pivot (
sum([count])
for ColName in ([C1], [C2], [TC])
) as pvt
order by ATRSTUDY, ENDDATE, ENDTIME;
Output:
ATRSTUDY ENDDATE ENDTIME C1 C2 TC
-------- ---------- ------- -- -- --
A 2018-01-01 18:00 10 20 30
B 2018-01-01 18:00 30 40 70

Related

SQL Server: Find records with closest Date to CurrentDate based on conditions

I'm using SQL Server 2012 and I'm attempting to create a VIEW that would return records based on these conditions:
Query needs to retrieve most applicable record based on date
For dates that are within an inner Date Range, the closest record to CurrentDate will be returned
For dates that are outside an inner Date Range, the closest record to CurrentDate will be returned
Sample tables in the database:
Person table:
pId | Name
----------------------
01 | Person 1
02 | Person 2
----------------------
PersonDate table:
dId | pId | StartDate | EndDate
---------------------------------------------------
A1 | 01 | 2014-01-08 | 2018-01-08
A2 | 01 | 2016-11-23 | 2016-12-01
A3 | 01 | 2016-12-03 | 2016-12-08
A4 | 02 | 2016-10-10 | 2016-12-31
A5 | 02 | 2016-12-01 | 2016-12-05
If I run this query and the CurrentDate is 2016-11-28:
select p.name, d.startdate, d.enddate
from Person p, PersonDate d
where p.pId = d.pId
and d.StartDate = (select max(sl.StartDate)
from PersonDate sl
where d.pId = s1.pId)
The records that are returned are:
name | startdate | enddate
-------------------------------------------
Person 1 | 2016-12-03 | 2016-12-08 --> PersonDate Table row A3
Person 2 | 2016-12-01 | 2016-12-05 --> PersonDate Table row A5
-------------------------------------------
Both returned records are incorrect based on the conditions I'm trying to get back. I understand why I'm getting the returned records, and it is due to using the Max() function within my subquery, but I don't know how to write the query/subquery.
The correct records that I want to be return are (CurrentDate being 2016-11-28):
name | startdate | enddate
-------------------------------------------
Person 1 | 2016-11-23 | 2016-12-01
Person 2 | 2016-10-10 | 2016-12-31
-------------------------------------------
PersonDate table row A2, since this inner Date Range falls closest to CurrentDate (condition #2)
PersonDate table row A4, since the inner Date Range (A5) hasn't come yet (condition #3)
When CurrentDate is 2016-12-02:
name | startdate | enddate
---------------------------------------------
Person 1 | 2014-01-08 | 2018-01-08
Person 2 | 2016-12-01 | 2016-12-05
---------------------------------------------
PersonDate table row A1, since CurrentDate is outside both row A2 and A3 inner Date Ranges
PersonDate table row A5, since CurrentDate is inside a Date Range
How can I write a VIEW that would return records based on the conditions above?
create table #temp(did varchar(10),pid int,startdate datetime,enddate datetime)
insert into #temp values('A1',01,'2014-01-08','2018-01-08')
insert into #temp values('A2',01, '2016-11-23' , '2016-12-01' )
insert into #temp values('A3',01, '2016-12-03' , '2016-12-08' )
insert into #temp values('A4',02, '2016-10-10' , '2016-12-31' )
insert into #temp values('A5',02, '2016-12-01' , '2016-12-05' )
select b.pid,b.startdate,b.enddate
from
(
select ROW_NUMBER()over(partition by pid order by id desc) as SID , a.*
from
(
select
ROW_NUMBER()over(partition by pid order by startdate,enddate desc) as ID
, * from #temp
--to identify whether it is inner or outer
--1 means outer
--2 means inner
)a
where '2016-12-02' between startdate and enddate
--to find date lies in outer or inner range and select the required
)b
where b.SID=1

SQL Server display first, second, and third values with same value

I am trying to create a query in SQL Server but having some difficulties. I will try to supply some sample data so it is easier. There are multiple tables I am trying to pull from:
Units:
UnitID
------
101
102
103
104
etc..
Jobs:
JobID
------
1
2
etc.
Job Units:
UnitID | JobID | DispatchDate
-----------------------------
102 | 12 | Dec 12 2015
104 | 14 | Dec 12 2015
102 | 18 | Dec 12 2015
108 | 18 | Dec 12 2015
102 | 11 | Dec 12 2015
104 | 10 | Dec 12 2015
What I would like for a desired outcome would reflect this data set:
UnitID | Job 1 | Job 2 | Job 3
------------------------------
102 | 12 | 18 | 11
103 | | |
104 | 14 | 10 |
105 | | |
106 | | |
107 | | |
108 | 18 | |
So basically, I want to display the job the Unit has been out to up to three jobs, but I still need to show the other units that haven't gone out, or only have gone out once or twice.
I currently am exporting this data set out to three separate listviews and using three separate stored procedures, but this isn't getting the job done and is a mess, so I won't even bother posting my code, but if needed I can.
Any help is greatly appreciated. Thanks!
Edit: As per request of /u/Pasty, here is the code I am working on:
select UnitID, case when (select COUNT(JobID)
from JobUnits
where UnitID=U.UnitID
and DispatchDate='Dec 12 2015') >= 1
then (select top 1 JobID
from JobUnits
where UnitID=U.UnitID
and DispatchDate='Dec 12 2015'
order by JobID asc )
else null
end as 'Job 1', case when (select COUNT(JobID)
from JobUnits
where UnitID=U.UnitID
and DispatchDate='Dec 12 2015') >= 2
then (select top 1 JobID
from JobUnits
where UnitID=U.UnitID
and DispatchDate='Dec 12 2015'
order by JobID desc )
else null
end as 'Job 2', case when (select COUNT(JobID)
from JobUnits
where UnitID=U.UnitID
and DispatchDate='Dec 12 2015') >= 3
then (select top 1 JobID
from JobUnits
where UnitID=U.UnitID
and DispatchDate='Dec 12 2015'
order by JobID asc )
else null
end as 'Job 3'
from Units U
order by UnitID asc
You need to use LEFT JOIN in order to achieve the desired result. If you have table A and B and want to display matches in B and all items from A, LEFT JOIN allow's you to do that. A good visual explanation of SQL JOINS can be found here - A Visual Explanation of SQL Joins.
How can you create the desired output in your case (I am assuming the tables are named Job, Unit ans Unit_has_Job):
Unit | UnitId
-----------
101
102
103
104
105
107
Job | JobId
--------------
1
2
3
4
5
Unit_has_JobId | UnitId | JobId
----------------------------------
101 | 1
101 | 3
101 | 4
102 | 4
105 | 3
select u.UnitId, ISNULL(j.JobId, 0) as [JobId] from unit as u
left join unit_has_job as uhj on u.UnitId = uhj.UnitId
left join job as j on uhj.JobId = j.JobId
The result is:
UnitId | JobId
101 | 1
101 | 3
101 | 4
102 | 4
103 | 0
104 | 0
105 | 3
107 | 0
I have created an example in SQL-Fiddle.
With the ISNULL function you mark the empty slots. The different JobId for every UnitId is equivalent to the columns in your desired output.
Using LINQ2SQL and the GroupBy method you can easily group the jobs per unit from the result and iterate over them:
var jobsPerUnit = result.GroupBy (r => r.UnitId);
foreach (var jobs in jobsPerUnit)
{
Console.WriteLine("Unit: " + jobs.Key);
foreach (var job in jobs)
{
if (job.JobId > 0)
{
Console.WriteLine("Job: " + job.JobId);
}
}
Console.WriteLine("=================");
}
The output:
Unit: 101
Job: 1
Job: 3
Job: 4
=================
Unit: 102
Job: 4
=================
Unit: 103
=================
Unit: 104
=================
Unit: 105
Job: 3
=================
Unit: 107
=================
Columns in SQL are the result of a selection/projection, which can't be created so easily dynamically. One possible solution is to use dynamic SQL in order to create a temp table, fill it and then select from this temp table, but the overhead is probably not worth it. You still need a cursor for this solution.
If you want to do everything on the SQL-side, then one possible solution would be to iterate with a cursor over the result and group by UnitId.
I was just playing around and this is what I came up with:
SQLFiddle
IF OBJECT_ID(N'dbo.UNITS', 'U') IS NOT NULL
DROP TABLE dbo.UNITS
GO
CREATE TABLE UNITS(ID INT IDENTITY(1, 1), UnitID INT, JobID INT, DispatchDate DATETIME)
GO
INSERT INTO UNITS
VALUES(102, 12, 'Dec 12 2015')
,(104, 14, 'Dec 12 2015')
,(102, 18, 'Dec 12 2015')
,(108, 18, 'Dec 12 2015')
,(102, 11, 'Dec 12 2015')
,(104, 10, 'Dec 12 2015')
,(103, NULL, NULL)
,(105, NULL, NULL)
,(106, NULL, NULL)
,(107, NULL, NULL)
GO
SELECT
UnitId
,Job_1 = JobIDs.value('/JobID[1]','INT')
,Job_2 = JobIDs.value('/JobID[2]','INT')
,Job_3 = JobIDs.value('/JobID[3]','INT')
FROM
(
SELECT
UnitID
,JobIDs = CONVERT(XML,'<JobID>'
+ REPLACE(Units.JobIDs, '|', '</JobID><JobID>')
+ '</JobID>')
FROM
(
SELECT DISTINCT
U.UnitId
,JobIDs = STUFF((
SELECT
'|' + CAST(UU.JobID AS NVARCHAR(25))
FROM
UNITS UU
WHERE
UU.UnitID = U.UnitID
ORDER BY
UU.ID, UU.JobID
FOR XML PATH (''), type).value('.', 'nvarchar(max)'), 1, 1, '')
FROM
UNITS U
GROUP BY
U.UnitID) Units) UJ
There is an easier way to get to the same result, where you don't need to run inner selects all the time. This yields a much simpler execution plan and faster execution if you put on the proper indexes.
Note that there is a LEFT JOIN in the inner query (LEFT JOIN JobUnits ju), that will produce an row for each row in Units, regardless if it has related JobUnit.
select UnitID,
SUM(CASE WHEN cnt=1 THEN JobID ELSE 0 END) AS Job1,
SUM(CASE WHEN cnt=2 THEN JobID ELSE 0 END) AS Job2,
SUM(CASE WHEN cnt=3 THEN JobID ELSE 0 END) AS Job3,
SUM(CASE WHEN cnt=4 THEN JobID ELSE 0 END) AS Job4,
SUM(CASE WHEN cnt=5 THEN JobID ELSE 0 END) AS Job5
FROM (
select ju.UnitID, ju.JobID, count(1) as cnt
FROM Units u
LEFT JOIN JobUnits ju on (u.UnitID = ju.UnitID)
LEFT JOIN JobUnits ju2 on (ju.UnitID = ju2.UnitID AND ju.UnitID <= ju2.UnitID)
)
GROUP BY UnitID
The only disadvantage of this SELECT that it will not preserve the order (12, 18, 11 in case of 102), instead it orders it ascending way (11, 12, 18) - I don't know if it is an advantage or disadvantage in your business case.
You can preserve the order if you compare the ROWNUMs, but that requires two more subselects, and I'm not sure if it worth or not.
I ended up solving this scenario with quite a mess of code, but it works and I'm tired, so I am just going to go with it.
select UnitID, case when (select COUNT(JobID)
from JobUnits
where UnitID=U.UnitID
and DispatchDate='Dec 4 2015') >= 1
then (select top 1 JobID
from JobUnits
where UnitID=U.UnitID
and DispatchDate='Dec 4 2015'
order by JobID asc )
else null
end as 'Job 1', case when (select COUNT(JobID)
from JobUnits
where UnitID=U.UnitID
and DispatchDate='Dec 4 2015') >= 1
then (select Customer
from Customers C
full
where CustomerID=())
else null
end as 'Customer 1',
case when (select COUNT(JobID)
from JobUnits
where UnitID=U.UnitID
and DispatchDate='Dec 4 2015') >= 2
then
(SELECT TOP 1 JobID
FROM
(
SELECT TOP 2 JobID
from JobUnits
where UnitID=U.UnitID
and DispatchDate='Dec 4 2015'
ORDER BY JobID desc
) sub
ORDER BY JobID asc)
else null
end as 'Job 2',
case when (select COUNT(JobID)
from JobUnits
where UnitID=U.UnitID
and DispatchDate='Dec 4 2015') >= 3
then
(SELECT TOP 1 JobID
FROM
(
SELECT TOP 3 JobID
from JobUnits
where UnitID=U.UnitID
and DispatchDate='Dec 4 2015'
ORDER BY JobID desc
) sub
ORDER BY JobID desc)
else null
end as 'Job 3'
from Units U
order by UnitID asc

Unpivotting multiple columns - substring of column name as a new column with CROSS APPLY

I have a table with the following format
YEAR, MONTH, ITEM, REQ_QTY1, REQ_QTY2 , ....REQ_QTY31 ,CONVERTED1, CONVERTED2 ....CONVERTED31
Where the suffix of each column is the day of the month.
I need to convert it to the following format, where Day_of_month is the numeric suffix of each column
YEAR, MONTH, DAY_OF_MONTH, ITEM, REQ_QTY, CONVERTED
I thought of using CROSS APPLY to retrieve the data, but I can't use CROSS APPLY to get the "Day of Month"
SELECT A.YEAR, A.MONTH, A.ITEM, B.REQ_QTY, B.CONVERTED
FROM TEST A
CROSS APPLY
(VALUES
(REQ_QTY1, CONVERTED1),
(REQ_QTY2, CONVERTED2),
(REQ_QTY3, CONVERTED3),
......
(REQ_QTY31, CONVERTED31)
)B (REQ_QTY, CONVERTED)
The only way I found is to use a nested select with inner join
SELECT A.YEAR, A.MONTH, A.DAY_OF_MONTH, A.ITEM,A.REQ_QTY, D.CONVERTED FROM
(SELECT YEAR, MONTH, ITEM, SUBSTRING(DAY_OF_MONTH,8,2) AS DAY_OF_MONTH, REQ_QTY FROM TEST
UNPIVOT
(REQ_QTY FOR DAY_OF_MONTH IN ([REQ_QTY1],[REQ_QTY2],[REQ_QTY3],......[REQ_QTY30],[REQ_QTY31])
) B
) A
INNER JOIN (SELECT YEAR, MONTH, ITEM, SUBSTRING(DAY_OF_MONTH,10,2) AS DAY_OF_MONTH, CONVERTED FROM TEST
UNPIVOT
(CONVERTED FOR DAY_OF_MONTH IN ([CONVERTED1],[CONVERTED2],[CONVERTED3],....[CONVERTED30],[CONVERTED31])
) C
) D
ON D.YEAR = A.YEAR AND D.MONTH = A.MONTH AND D.ITEM = A.ITEM AND D.DAY_OF_MONTH = A.DAY_OF_MONTH
Is there a way to use CROSS APPLY and yet get the DAY_OF_MONTH out?
This is not a solution with CROSS APPLY but it will definitely make it a bit faster as it uses a bit simpler approach and simpler execution plan.
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE Test_Table([YEAR] INT, [MONTH] INT, [ITEM] INT, REQ_QTY1 INT
, REQ_QTY2 INT ,REQ_QTY3 INT , CONVERTED1 INT, CONVERTED2 INT, CONVERTED3 INT)
INSERT INTO Test_Table VALUES
( 2015 , 1 , 1 , 10 , 20 , 30 , 100 , 200 , 300),
( 2015 , 2 , 1 , 10 , 20 , 30 , 100 , 200 , 300),
( 2015 , 3 , 1 , 10 , 20 , 30 , 100 , 200 , 300)
Query 1:
SELECT *
FROM
(
SELECT [YEAR]
,[MONTH]
,ITEM
,Vals
,CASE WHEN LEFT(N,3) = 'REQ' THEN SUBSTRING(N,8 ,2)
WHEN LEFT(N,3) = 'CON' THEN SUBSTRING(N,10,2)
END AS Day_Of_Month
,CASE WHEN LEFT(N,3) = 'REQ' THEN LEFT(N,7)
WHEN LEFT(N,3) = 'CON' THEN LEFT(N,9)
END AS Tran_Type
FROM Test_Table t
UNPIVOT (Vals FOR N IN ([REQ_QTY1],[REQ_QTY2],[REQ_QTY3],
[CONVERTED1],[CONVERTED2],[CONVERTED3]))up
)t2
PIVOT (SUM(Vals)
FOR Tran_Type
IN (REQ_QTY, CONVERTED))p
Results:
| YEAR | MONTH | ITEM | Day_Of_Month | REQ_QTY | CONVERTED |
|------|-------|------|--------------|---------|-----------|
| 2015 | 1 | 1 | 1 | 10 | 100 |
| 2015 | 1 | 1 | 2 | 20 | 200 |
| 2015 | 1 | 1 | 3 | 30 | 300 |
| 2015 | 2 | 1 | 1 | 10 | 100 |
| 2015 | 2 | 1 | 2 | 20 | 200 |
| 2015 | 2 | 1 | 3 | 30 | 300 |
| 2015 | 3 | 1 | 1 | 10 | 100 |
| 2015 | 3 | 1 | 2 | 20 | 200 |
| 2015 | 3 | 1 | 3 | 30 | 300 |
Well, I found a way using CROSS APPLY, but instead of taking a substring, I'm basically hardcoding the days. Works well enough so...
SELECT A.YEAR, A.MONTH, A.ITEM, B.DAY_OF_MONTH, B.REQ_QTY, B.CONVERTED
FROM TEST A
CROSS APPLY
(
VALUES
('01', REQ_QTY1, CONVERTED1),
('02', REQ_QTY2, CONVERTED2),
('03', REQ_QTY3, CONVERTED3),
('04', REQ_QTY4, CONVERTED4),
......
('31', REQ_QTY31, CONVERTED31)
) B (DAY_OF_MONTH, REQ_QTY, CONVERTED)

SQL - how do I generate rows for each month based on date ranges in existing dataset?

assume I have a dataset:
rowID | dateStart | dateEnd | Year | Month
121 | 2013-10-03 | 2013-12-03 | NULL | NULL
143 | 2013-12-11 | 2014-03-11 | NULL | NULL
322 | 2014-01-02 | 2014-02-11 | NULL | NULL
And I want sql to generate the following datasource based on the dateStart and the dateEnd. Note the year and month grouping.
rowID | dateStart | dateEnd | Year | Month
121 | 2013-10-03 | 2013-12-03 | 2013 | 10
121 | 2013-10-03 | 2013-12-03 | 2013 | 11
121 | 2013-10-03 | 2013-12-03 | 2013 | 12
143 | 2013-12-11 | 2014-03-11 | 2013 | 12
143 | 2013-12-11 | 2014-03-11 | 2014 | 1
143 | 2013-12-11 | 2014-03-11 | 2014 | 2
143 | 2013-12-11 | 2014-03-11 | 2014 | 3
322 | 2014-01-02 | 2014-02-11 | 2014 | 1
322 | 2014-01-02 | 2014-02-11 | 2014 | 2
I'm having a hard time wrapping my head around this one. Any ideas?
I find it easiest to approach these problems by creating a list of integers and then using that to increment the dates. Here is an example:
with nums as (
select 0 as n
union all
select n + 1 as n
from nums
where n < 11
)
select rowid, datestart, dateend,
year(dateadd(month, n.n, datestart)) as yr,
month(dateadd(month, n.n, datestart)) as mon
from table t join
nums n
on dateadd(month, n.n - 1, datestart) <= dateend;
First, create a tabled-valued function that takes the 2 dates and returns the year and month as a table:
create function dbo.YearMonths(#StartDate DateTime, #EndDate DateTime)
returns #YearMonths table
([Year] int,
[Month] int)
as
begin
set #EndDate = DATEADD(month, 1, #EndDate)
while (#StartDate < #EndDate)
begin
insert into #YearMonths
select YEAR(#StartDate), MONTH(#StartDate)
set #StartDate = DATEADD(month, 1, #StartDate)
end
return
end
As an example the following:
select *
from dbo.YearMonths('1/1/2014', '5/1/2014')
returns:
Then you would join to it like this to get what you wanted:
select m.*, ym.Year, ym.Month
from myTable m
cross apply dbo.YearMonths(dateStart, dateEnd) ym
Try this:
declare #months table(mth int)
insert into #months values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
declare #calendar table(yr int,mth int)
insert into #calendar
select distinct year(datestart),mth
from tbl cross join #months
union
select distinct year(dateend),mth
from tbl cross join #months
select t.rowID, t.datestart, t.dateend, y.yr [Year], y.mth [Month]
from
yourtable t
inner join #calendar y on year(datestart) = yr or year(dateend) = yr
where
(mth >= month(datestart) and mth <= month(dateend) and year(datestart) = year(dateend))
or
(year(datestart) < year(dateend))
and
(year(datestart) = yr and mth >= month(datestart) --All months of start year
or
(year(dateend) = yr and mth <= month(dateend))) -- All months of end year
order by t.rowID, [Year],[Month]
We create a 'Calendar table' which lists all the month and year combinations present in the source table. Then, we join the source table to the calendar table based on the year, and filter as required.

How to check the previous row value

Following is the model data in my table.
Data:
Date Worked Hours IsLate
8/1/2013 8:03:00 No
8/2/2013 10:52:00 No
8/3/2013 8:43:00 Yes
8/4/2013 9:26:00 No
IsLate column to be updated as 'Yes' if previous day worked hours is equal to 10 hours. How can I calculate the previous row value is equal to 10 hours or not.
Please help me out.
Query:
SQLFIDDLEExample
with table5
as (
SELECT Date,
[Worked Hours],
IsLate,
ROW_NUMBER() OVER (ORDER BY Date ASC, [Worked Hours] ASC) AS RowNumber1
FROM Table1
)
SELECT t1.Date,
t1.[Worked Hours],
t1.IsLate,
CASE WHEN LEFT(t2.[Worked Hours],2) = '10'
THEN 'Equal'
ELSE 'Not Equal' END AS isequal
FROM table5 t1
LEFT JOIN table5 t2
ON t1.RowNumber1 - 1 = t2.RowNumber1
Result:
| DATE | WORKED HOURS | ISLATE | ISEQUAL |
|-------------------------------|--------------|--------|-----------|
| August, 01 2013 00:00:00+0000 | 8:03:00 | No | Not Equal |
| August, 02 2013 00:00:00+0000 | 10:52:00 | No | Not Equal |
| August, 03 2013 00:00:00+0000 | 8:43:00 | Yes | Equal |
| August, 04 2013 00:00:00+0000 | 9:26:00 | No | Not Equal |
If you want just update table use this:
SQLFIDDLEExample
UPDATE Table1
SET [IsLate] = ISNULL((SELECT TOP 1 CASE WHEN LEFT(t1.[Worked Hours],2) = '10'
THEN 'Equal'
ELSE 'Not' END
FROM Table1 t1
WHERE t1.Date < Table1.Date
ORDER BY t1.Date DESC), 'Not')
Result:
| DATE | WORKED HOURS | ISLATE |
|-------------------------------|--------------|--------|
| August, 01 2013 00:00:00+0000 | 8:03:00 | Not |
| August, 02 2013 00:00:00+0000 | 10:52:00 | Not |
| August, 03 2013 00:00:00+0000 | 8:43:00 | Equal |
| August, 04 2013 00:00:00+0000 | 9:26:00 | Not |
IF 10 <= (SELECT DATEPART(hh, Worked_Hours) from Data)
Begin
Update Data set isLate = 'No'
where ..
End
update yourtable
set late='yes'
from yourtable
where
(select top 1 datepart(hh,worked)
from yourtable t2
where t2.workdate<yourtable.workdate order by workdate desc)=10
How about this?
declare #d table (WorkedDate date,WorkedHours time,IsLate varchar(10));
insert #d select '8/1/2013','8:03',null;
insert #d select '8/2/2013','10:52',null;
insert #d select '8/3/2013','8:43',null;
insert #d select '8/4/2013','9:26',null;
update #d set IsLate=case when exists (select WorkedHours from #d p where p.WorkedDate=dateadd(day,-1,d.WorkedDate) and WorkedHours>'10:00') then 'Yes' else 'No' end
from #d d;
select * from #d;

Resources