How can I PIVOT TABLE Or CrossTab By Datetime? - sql

I need crosstab or pivot table By select Datetime.
Table filesTA
EmpNo ChkDate ChkIn
00001 2012-10-10 00:00:00.000 2012-10-10 07:22:00.000
00002 2012-10-10 00:00:00.000 2012-10-10 07:30:00.000
00001 2012-10-11 00:00:00.000 2012-10-11 07:13:00.000
00002 2012-10-11 00:00:00.000 2012-10-11 07:34:00.000
00001 2012-10-12 00:00:00.000 2012-10-12 07:54:00.000
00002 2012-10-12 00:00:00.000 2012-10-12 07:18:00.000
I have tried following
SELECT tf.EmpNo,tf.ChkDate,tf.ChkIn
FROM (SELECT EmpNo,ChkDate,ChkIn
,ROW_NUMBER() OVER(PARTITION BY EmpNo ORDER BY ChkDate) as tfNum
FROM filesTA) AS tf
PIVOT(MIN(ChkDate) FOR tfNum IN ('2012-10-10'))
WHERE tf.ChkDate Between '2012-10-10' and '2012-10-12'
But getting following error
Incorrect syntax near 'PIVOT'. You may need to set the compatibility
level of the current database to a higher value to enable this feature.
See help for the SET COMPATIBILITY_LEVEL option of ALTER DATABASE.
Desired Output:
EmpNo 10 11 12
00001 07:22 07:13 07:54
00002 07:30 07:34 07:18
I'm beginning learn pivot and crosstab. please help me to get my query working.

If you are not able to use the PIVOT function, then you can use an aggregate function with a CASE statement:
select empno,
max(case when datepart(d, chkdate) = 10
then convert(char(5), ChkIn, 108) end) [10],
max(case when datepart(d, chkdate) = 11
then convert(char(5), ChkIn, 108) end) [11],
max(case when datepart(d, chkdate) = 12
then convert(char(5), ChkIn, 108) end) [12]
from filesTA
where ChkDate Between '2012-10-10' and '2012-10-12'
group by empno
See SQL Fiddle with Demo
If you have access to PIVOT, then your syntax will be:
select empno, [10], [11], [12]
from
(
select empno, datepart(d, chkdate) chkdate,
convert(char(5), ChkIn, 108) chkin
from filesTA
) src
pivot
(
max(chkin)
for chkdate in ([10], [11], [12])
) piv
See SQL Fiddle with Demo

IF you need to use PIVOT on databases with compatibility level below 90 it won't work.
Read this ALTER DATABASE Compatibility Level
After your modified DATABASE Compatibility Level your query will be look so
;WITH cte AS
(
SELECT EmpNo, CAST(ChkIn AS time) AS ChkIn, DATEPART(mm, ChkDate) as mm_ChkDate
FROM filesTA
WHERE ChkDate Between '2012-10-10' and '2012-10-12'
)
SELECT EmpNo, [10], [11], [12] FROM cte
PIVOT(
MIN(ChkIn) FOR cte.mm_ChkDate IN ([10], [11], [12])) x

Related

How can I add this new custom column to the output of my query?

I am using SQL Server 2014 and I have the following T-SQL query:
SELECT
[Date],
(CASE
WHEN [Date] BETWEEN '2016-07-01' AND '2017-06-30' THEN 'FY 16-17'
WHEN [Date] BETWEEN '2017-07-01' AND '2018-06-30' THEN 'FY 17-18'
WHEN [Date] BETWEEN '2018-07-01' AND '2019-06-30' THEN 'FY 18-19'
ELSE 'Not Stated'
END) AS [Period]
FROM
DateDimension
WHERE
[Date] BETWEEN '2016-07-01' AND '2019-06-30'
The output is as follows (extract):
Date Period
-----------------------
2016-07-01 FY 16-17
2016-07-02 FY 16-17
2016-07-03 FY 16-17
... ...
2017-07-01 FY 17-18
2017-07-02 FY 17-18
2017-07-03 FY 17-18
... ...
2018-07-01 FY 18-19
2018-07-02 FY 18-19
2018-07-03 FY 18-19
... ...
I want to add a new column to the output as follows:
Date Period Day
-------------------------------
2016-07-01 FY 16-17 D1
2016-07-02 FY 16-17 D2
2016-07-03 FY 16-17 D3
... ... ...
2017-07-01 FY 17-18 D1
2017-07-02 FY 17-18 D2
2017-07-03 FY 17-18 D3
... ... ...
2018-07-01 FY 18-19 D1
2018-07-02 FY 18-19 D2
2018-07-03 FY 18-19 D3
... ... ...
To note that D1 starts again at the beginning of each new financial year (that is,2016-07-01, 2017-07-01 and 2018-07-01).
How do I write the SQL code for this new column?
Additional note: D1 should be continuous till the end of each financial year. Example, from 2016-07-01 till 2017-06-30, column Period will show D1, D2, ..., D365)
You could use DATEDIFF to get the difference in days between the start of the financial year & [Date].
SELECT
[Date]
,(CASE WHEN [Date] between '2016-07-01' and '2017-06-30' THEN 'FY 16-17'
WHEN [Date] between '2017-07-01' and '2018-06-30' THEN 'FY 17-18'
WHEN [Date] between '2018-07-01' and '2019-06-30' THEN 'FY 18-19'
ELSE 'Not Stated'
END) as [Period]
, CASE WHEN [Date] < DATEFROMPARTS(DATEPART(Year, GETDATE()), 7, 1)
THEN CONCAT('D', (DATEDIFF(DAY, DATEFROMPARTS(DATEPART(Year, [Date]) - 1, 7, 1), [Date] + 1)))
ELSE CONCAT('D', (DATEDIFF(DAY, DATEFROMPARTS(DATEPART(Year, [Date]), 7, 1), [Date] + 1)))
END AS [Day]
FROM DateDimension
WHERE [Date] between '2016-07-01' and '2019-06-30'
This method also means that the dates could be in any order or even have some days missing & the Day column should still be correct.
Here is an example how you can simplify your existing FY calculation, and get the day of financial year as well:
declare #date date = '20190702';
select year(dateadd(month, -6, #date)) as [FY],
datediff(day, datefromparts(year(dateadd(month, -6, #date)), 6, 30), #date) as [DOFY];
The hard-coded constants that designate the offset between the calendar and financial year can also be parameterised, if you need it.
You can use DATEDIFF to calculate number of days in that Financial year. You just need an extra effort to get the Financial year first date for the DATE column.
DECLARE #DateDimension TABLE ([DATE] DATETIME)
INSERT INTO #DateDimension
SELECT '2019-03-25'
UNION ALL
SELECT '2018-12-06'
UNION ALL
SELECT '2018-05-15'
UNION ALL
SELECT '2017-11-22'
UNION ALL
SELECT '2019-07-06'
SELECT [DATE]
,'D'+CAST( DATEDIFF(DD, CASE WHEN MONTH([DATE]) BETWEEN 7 AND 12
THEN DATEFROMPARTS(YEAR([DATE]),07,01)
ELSE DATEFROMPARTS(YEAR([DATE])-1,07,01) END,[DATE])+1
AS VARCHAR(3)) AS DAY_IN_FY
FROM #DateDimension
Result:
+-------------------------+-----------+
| DATE | DAY_IN_FY |
+-------------------------+-----------+
| 2019-03-25 00:00:00.000 | D268 |
| 2018-12-06 00:00:00.000 | D159 |
| 2018-05-15 00:00:00.000 | D319 |
| 2017-11-22 00:00:00.000 | D145 |
| 2019-07-06 00:00:00.000 | D6 |
+-------------------------+-----------+
I want to point out that you can express the query as:
SELECT d.[Date], v.period,
CONCAT('D', ROW_NUMBER() OVER (PARTITION BY period ORDER BY date)) as [Day]
FROM DateDimension dd CROSS APPLY
(VALUES (RIGHT(DATENAME(year, d.[Date]), 2) + '-' +
RIGHT(DATENAME(year, DATEADD(year, 1, d.[Date])), 2)
)
) as v(period)
WHERE [Date] BETWEEN '2016-07-01' AND '2019-06-30';
The period can also be defined as:
(VALUES (CONCAT(YEAR([Date] % 100, '-',
1 + YEAR([Date] % 100
)
)
) as v(period)
use row_number()
with cte as
(
SELECT [Date]
,(CASE WHEN [Date] between '2016-07-01' and '2017-06-30' THEN 'FY 16-17'
WHEN [Date] between '2017-07-01' and '2018-06-30' THEN 'FY 17-18'
WHEN [Date] between '2018-07-01' and '2019-06-30' THEN 'FY 18-19'
ELSE 'Not Stated'
END) as [Period]
FROM DateDimension
)
select *,concat('D',row_number() over(partition by period order by date)) as DayNo
from cte

Pivoting for DATEPART(wk, date)

I have a table like this,
mytable
date | value
2018.09.12 | 1
2018.09.11 | 2
2018.09.10 | 3
I need a query to return sum(value) for the last six weeks. Exactly like this.
week# | value
37 | 6
36 | 0
35 | 0
34 | 8
33 | 9
32 | 10
31 | 11
I have a query to return sumvalue for each week.
SELECT Sum(Value) AS Sumvalue, DATEPART(wk, date) AS [weekNo]
FROM mytable
WHERE Date BETWEEN DATEADD(DAY, -42, GETDATE()) AND GETDATE()
GROUP BY DATEPART(wk, date)
But this can't handle zero values for a week.
How can write a pivoted query to obtain the format?
My try;
SELECT *
FROM (
SELECT Value, DATEPART(wk, date) AS [weekNo]
FROM mytable
WHERE Date BETWEEN DATEADD(DAY, -42, GETDATE()) AND GETDATE()
) As sourcetable
PIVOT
(
Sum(Value) for DATEPART(wk, date) IN (SELECT date FROM mytable where date between
DATEADD(DAY, -42, GETDATE()) and GETDATE())
) AS pivotable
I am getting the syntax error near for keyword. How can I put the six weeks in pivot statemet
Found a partial solution!
SELECT *
FROM (
SELECT Value, DATEPART(wk, date) AS [weekNo]
FROM mytable
WHERE Date BETWEEN DATEADD(DAY, -42, GETDATE()) AND GETDATE()
) As sourcetable
PIVOT
(
Sum(Value) for [week] IN ([1], [2], [3], ... ,[54])
) AS pivotable
It is partial since it is kind of hardcoding the for statement and cannot control the unnecessary weeks in the query.

Convert Rows into Columns in Sql query

I am having tough time to figure out this please help me
I have time in/out sql query
I have a table looks like below. there are four columns display time in/out info such as...
Date Day Day TimeStamp CheckType
10/11/2014 Sat 8:30 am in
10/11/2014 Sat 11:30am out
10/11/2014 Sat 1:30pm in
10/11/2014 Sat out
10/12/2014 Sun 9:00am in
10/12/2014 Sun 11:20pm out
10/12/2014 Sun 5:20pm out
10/13/2014 Mon 8:00am in
10/13/2014 Mon 6:10pm in
so whoever checkin or checkout then the record will display the result in order and if someone is supposed to check out but accidently pressed in button then this will display as it is (in) or if someone forget to check out then that space will show blank
I am trying to convert rows into column and display such information in below
Date Day Time Type Time Type Time Type Time Type etc-----
10/11/2014 Sat 8:30am in 11:30am out 1:30pm in
10/12/2014 Sun 9:00am in 11:20am out 1:20pm in 6:20pm in
10/13/2014 Mon 8:00am in 6:10pm out
10/14/2014 Tus 8:20am in
etc
I have tried to use pivot
select Date, Day, [1],[2],[3],[4],[5],[6],[7],[8],[9],[10] etc---
from
(
select Date, Day, Stamptime, CheckTime, userID
from a table
)
pivot
(
max(StampTime)
for stamptime in ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10] etc---)
) as PivotTable
can anyone explain how to convert the rows into columns I have spent many days already.
Here's something close to what you're after, making use of XML to get the variable number of columns. As mentioned in my comment above though, I don't recommend this approach.
SQL Fiddle: http://sqlfiddle.com/#!3/e5b325/2
select [Date]
, [Day]
, (
select [TimeStamp] [Time]
, [CheckType] [Type]
from aTable b
where b.[Date] = a.[Date]
order by [TimeStamp], [CheckType]
for xml path ('')
) CheckInAndOutInfo
from aTable a
group by [Date], [Day]
order by [Date]
Output:
DATE DAY CHECKINANDOUTINFO
2014-10-11 Sat <Type>Out</Type><Time>08:30:00</Time><Type>In</Type><Time>11:30:00</Time><Type>Out</Type><Time>13:30:00</Time><Type>In</Type>
2014-10-12 Sun <Time>09:00:00</Time><Type>In</Type><Time>17:20:00</Time><Type>Out</Type><Time>23:20:00</Time><Type>Out</Type>
2014-10-13 Mon <Time>08:00:00</Time><Type>In</Type><Time>18:10:00</Time><Type>In</Type>
Alternatively, if you can guarantee you'll never have more than a certain number of check-ins/outs per day, you could do the following (this assumes no more than 5 per day):
SQL Fiddle: http://sqlfiddle.com/#!3/e5b325/4
select *
from
(
select [Date], [Day]
, 'T' + CAST(ROW_NUMBER() over (partition by [Date] order by [TimeStamp], [CheckType]) as nvarchar) r
, cast([TimeStamp] as nvarchar) pvtVal
from aTable
where [TimeStamp] is not null
union all
select [Date], [Day]
, 'C' + CAST(ROW_NUMBER() over (partition by [Date] order by [TimeStamp], [CheckType]) as nvarchar) r
, cast([CheckType] as nvarchar) pvtVal
from aTable
where [TimeStamp] is not null
) x
pivot
(
min(pvtVal)
for r in ([T1], [C1], [T2], [C2], [T3], [C3], [T4], [C4], [T5], [C5])
) y
order by [Date]
Output:
DATE DAY T1 C1 T2 C2 T3 C3 T4 C4 T5 C5
2014-10-11 Sat 08:30:00.0000000 In 11:30:00.0000000 Out 13:30:00.0000000 In (null) (null) (null) (null)
2014-10-12 Sun 09:00:00.0000000 In 17:20:00.0000000 Out 23:20:00.0000000 Out (null) (null) (null) (null)
2014-10-13 Mon 08:00:00.0000000 In 18:10:00.0000000 In (null) (null) (null) (null) (null) (null)
...Or if you wanted to use dynamic SQL, you could do this:
SQL Fiddle: http://sqlfiddle.com/#!3/e5b325/6
declare #sql nvarchar(max)
select #sql = coalesce(#sql+',','') + QUOTENAME('T' + CAST(x as nvarchar)) + ',' + QUOTENAME('C' + CAST(x as nvarchar))
from
(
select distinct row_number() over (partition by [Date] order by [Date]) x
from aTable
where [TimeStamp] is not null
) y
order by x
set #sql =
'select *
from
(
select [Date], [Day]
, ''T'' + CAST(ROW_NUMBER() over (partition by [Date] order by [TimeStamp], [CheckType]) as nvarchar) r
, cast([TimeStamp] as nvarchar) pvtVal
from aTable
where [TimeStamp] is not null
union all
select [Date], [Day]
, ''C'' + CAST(ROW_NUMBER() over (partition by [Date] order by [TimeStamp], [CheckType]) as nvarchar) r
, cast([CheckType] as nvarchar) pvtVal
from aTable
where [TimeStamp] is not null
) x
pivot
(
min(pvtVal)
for r in (' + #sql + ')
) y
order by [Date]'
exec (#sql)
Output:
DATE DAY T1 C1 T2 C2 T3 C3
2014-10-11 Sat 08:30:00.0000000 In 11:30:00.0000000 Out 13:30:00.0000000 In
2014-10-12 Sun 09:00:00.0000000 In 17:20:00.0000000 Out 23:20:00.0000000 Out
2014-10-13 Mon 08:00:00.0000000 In 18:10:00.0000000 In (null) (null)

Table Design for User Selectable Schedule and Select Using PIVOT

I am creating a scheduling application that allows employees to input their desired schedule which is stored in a table. The current design I'm looking at is below using SQL 2008 R2.
CREATE TABLE [dbo].[Schedule] (
[EmpNum] [varchar](10) NOT NULL,
[Start] [datetime] NOT NULL,
[Length] [decimal](18, 2) NOT NULL,
[Reason] [varchar](1) NOT NULL,
CONSTRAINT [PK_Schedule] PRIMARY KEY CLUSTERED
(
[EmpNum] ASC,
[Start] ASC
)
)
A few things to note
A user may have entered no schedule for a specific date range and still want to see their name listed but with no schedule
A user may select nothing for a day in a week, but select something for other days
Start is the beginning of the shift
Length is the number of hours a shift is, may vary WILDLY from day to day and person to person
The Reason column is for what has been selected by the user (W - Work, P - PTO, etc)
Here is sample data
EmpNum Start Length Reason
---------- ----------------------- --------------------------------------- ------
000001 2012-08-02 09:00:00.000 12.00 W
000001 2012-08-04 08:00:00.000 9.50 P
000002 2012-08-02 08:30:00.000 10.00 W
000002 2012-08-03 19:00:00.000 12.00 W
000003 2012-08-03 08:00:00.000 8.00 P
The output I desire is something like this
EmpNum [0] [1] [2] [3] [4] [5] [6]
---------- ------ ------ ------ ------ ------ ------ ------
000001 NULL NULL NULL NULL W NULL P
000002 NULL NULL NULL NULL W W NULL
000003 NULL NULL NULL NULL NULL P NULL
I've never used a PIVOT query before since we just upgraded from SQL 2000, so bear with me. I've constructed the below query which fails with the below error and I'm stuck.
Msg 102, Level 15, State 1, Line 14
Incorrect syntax near '('.
Query
declare #FirstDayOfWeek date
set #FirstDayOfWeek = '7/29/2012'
select EmpNum,
#FirstDayOfWeek [0],
dateadd(day, 1, #FirstDayOfWeek) [1],
dateadd(day, 2, #FirstDayOfWeek) [2],
dateadd(day, 3, #FirstDayOfWeek) [3],
dateadd(day, 4, #FirstDayOfWeek) [4],
dateadd(day, 5, #FirstDayOfWeek) [5],
dateadd(day, 6, #FirstDayOfWeek) [6]
from Schedule
pivot (
max(Reason)
for Start in ([0], [1], [2], [3], [4], [5], [6])
) as Pvt
Any thoughts on how to best implement this or how badly wrong I am here?
It looks like you are trying to PIVOT your data based on the Day of the Week 1-7. I suggest a slight change to this to get it to work:
SELECT *
FROM
(
select EmpNum,
reason,
datepart(dw, start) as DyOfWk
from #Schedule
) s
pivot (
max(Reason)
for dyofwk in ([1], [2], [3], [4], [5], [6], [7])
) as Pvt
See SQL Fiddle with Demo
Results:

Displaying weeks as columns

Below is the script to display the number of weeks between the given dates.
SET DATEFIRST 1
SELECT ta.account, ta.customer, SUM(amount), DATEPART(ww,ta.dt) WeekNumber
FROM tablename ta
WHERE dt >= '12/01/2011 00:00:00'
and dt < '12/29/2011 00:00:00'
GROUP BY ta.account, ta.customer, DATEPART(ww,ta.dt)
How do I display the diff weeks as diff columns in my result. Any suggestion would be helpful.
Sample O/P for the above is:
Date Account Customer TotalSeconds Amount WeekNumber
2011-11-01 xx0918252 198303792R 394 2.99 45
2011-11-08 xx1006979 200100567G 92 0.16 46
2011-11-15 xx1005385 A6863744I 492 1.275 47
2011-11-21 xx1012872 D7874694G 770 0.52 48
2011-11-28 xx1006419 C7112151H 1904 2.64 49
2011-11-28 xx1006420 G7378945A 77 0.3 49
I want the O/P like:
Date Account Customer TotalSeconds Amount WeekNumber45 WeekNumber46 WeekNumber47 WeekNumber8 WeekNumber49
and their corresponding data. Hope u understand my question. Thanks in advance.
Hi All, Thanks for the suggestions n help. Finally, I got the results that i wanted for time being. I still believe that it is hard coding. Is there a better solution for this. Thanks in advance. My code is as follows:
SELECT ta.account, ta.customer,
isnull(SUM(CASE WHEN DATEPART(ww,ta.dt) = '49' THEN amount END),0) AS "Week49",
isnull(SUM(CASE WHEN DATEPART(ww,ta.dt) = '50' THEN amount END),0) AS "Week50",
isnull(SUM(CASE WHEN DATEPART(ww,ta.dt) = '51' THEN amount END),0) AS "Week51",
isnull(SUM(CASE WHEN DATEPART(ww,ta.dt) = '52' THEN amount END),0) AS "Week52",
isnull(SUM(CASE WHEN DATEPART(ww,ta.dt) = '53' THEN amount END),0) AS "Week53",
FROM (
select * from tablename
where dt >= '12/01/2011 00:00:00' and dt <= '12/31/2011 00:00:00'
) ta
group by ta.account, ta.customer
First of all I would put your result in temporary table for later calculations. Let's imagine that following CTE is your result:
if object_id('tempdb..#tab') is not null drop table #tab
;with cte (Date,Account,Customer,TotalSeconds,Amount,WeekNumber) as (
select cast('20111101' as datetime),'xx0918252','198303792R',394,2.99,45 union all
select '20111108','xx1006979','200100567G',92,0.16,46 union all
select '20111115','xx1005385','A6863744I',492,1.275,47 union all
select '20111121','xx1012872','D7874694G',770,0.52,48 union all
select '20111128','xx1006419','C7112151H',1904,2.64,49 union all
select '20111128','xx1006420','G7378945A',77,0.3,49
)
select * into #tab from cte
Now your computed data is in #tab table and the following query returns pivoted table for those weeknumbers:
select date, account, customer, totalSeconds, amount, [45], [46], [47], [48], [49] from
(
select date, account, customer, totalSeconds, amount, weeknumber as weeknumber from #tab
) src
pivot
(
max(weeknumber) for weekNumber in ([45], [46], [47], [48], [49])
) pvt
Dynamic version of this query might look like this:
declare #sql nvarchar(max), #cols varchar(max)
select #cols = coalesce(#cols + ',', '') + '[' + cast(weeknumber as varchar) + ']'
from (select distinct weeknumber from #tab) t
order by weeknumber
set #sql = N'
select date, account, customer, totalSeconds, amount, ' + #cols + ' from
(
select date, account, customer, totalSeconds, amount, weeknumber as weeknumber from #tab
) src
pivot
(
max(weeknumber) for weekNumber in (' + #cols + ')
) pvt
'
exec sp_executesql #sql
The result (in both cases):
date account customer totalSeconds amount 45 46 47 48 49
----------------------- --------- ---------- ------------ ---------- ------ ------ ------ ------ ------
2011-11-01 00:00:00.000 xx0918252 198303792R 394 2.990 45 NULL NULL NULL NULL
2011-11-08 00:00:00.000 xx1006979 200100567G 92 0.160 NULL 46 NULL NULL NULL
2011-11-15 00:00:00.000 xx1005385 A6863744I 492 1.275 NULL NULL 47 NULL NULL
2011-11-21 00:00:00.000 xx1012872 D7874694G 770 0.520 NULL NULL NULL 48 NULL
2011-11-28 00:00:00.000 xx1006419 C7112151H 1904 2.640 NULL NULL NULL NULL 49
2011-11-28 00:00:00.000 xx1006420 G7378945A 77 0.300 NULL NULL NULL NULL 49
Take a look at the PIVOT function.
Tsql pivot command
T-SQL Pivot function combined with dynamic SQL. Examples:
SQL Server 2005 Pivot on Unknown Number of Columns.
Pivots with Dynamic Columns in SQL Server 2005/2008.