Table Design for User Selectable Schedule and Select Using PIVOT - sql

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:

Related

SQL Pivot Percent Row Distribution Columns

I am looking for help with writing a query to display percent distribution to display across the values of a row in a pivot in SQL Server.
My data and current query for the pivot is as follows: https://dbfiddle.uk/CgwcAtRU
Existing Query:
SELECT StoreID, Quarter, MonthofYear, WeekOfYear, [0], [1], [2], [3], [4], [5], [6], [7]
FROM (SELECT Sales.StoreID, TimeGrouping, Sales.Quantity, MonthOfYear, Quarter, WeekOfYear
FROM Date INNER JOIN Sales ON Date.DateID = Sales.DateID) AS t
PIVOT (Sum(Quantity) FOR TimeGrouping IN ([0], [1], [2], [3], [4], [5], [6], [7])) p
GROUP BY StoreID, Quarter, MonthofYear, WeekOfYear,[0], [1], [2], [3], [4], [5], [6], [7]]
I am open to changing the existing query, but it provides a starting point of where my thinking was. In this example the results would return the following:
StoreID
Quarter
MonthofYear
WeekOfYear
0
1
2
3
4
5
6
7
1
1
1
2
null
null
null
71.43%
28.57%
null
null
null
2
1
1
2
null
null
100%
null
null
null
null
null
3
1
1
1
null
100%
null
null
null
null
null
null
Query to re-create data:
CREATE TABLE stores
(StoreID int, Location varchar(13), StoreName varchar(8));
INSERT INTO stores
(StoreID, Location, StoreName)
VALUES
(1, 'Miami', 'Joes''s'),
(2, 'Orlando', 'Franks''s'),
(3, 'Florida', 'Joes''s');
CREATE TABLE products
(ProductID int, Name varchar(10), Category varchar(5));
INSERT INTO products
(ProductID, Name, Category)
VALUES
(1, 'Milk', 'Food'),
(2, 'Eggs', 'Food'),
(3, 'Reese''s', 'Candy'),
(4, 'Hershey''s', 'Candy');
CREATE TABLE date
(DateID varchar(10), Year int, MonthofYear int, Quarter int, WeekofYear int, DayOfYear int);
INSERT INTO date
(DateID, Year, MonthofYear, Quarter, WeekOfYear, DayofYear)
VALUES
('1/1/2022',2022,'1',1,1,1),
('1/2/2022',2022,'1',1,2,2),
('1/3/2022',2022,'1',1,2,3),
('1/4/2022',2022,'1',1,2,4);
CREATE TABLE sales
(SaleID int, DateID varchar(10), ProductID int, StoreID int, Quantity int, TimeGrouping int);
INSERT INTO sales
(SaleID, DateID, ProductID, StoreID, Quantity, TimeGrouping)
VALUES
(1,'1/1/2022',4,3,6,1),
(2,'1/2/2022',2,2,6,2),
(3,'1/3/2022',3,1,5,3),
(4,'1/4/2022',2,1,2,4);

Count records by month & type

I have a table in SQL Server 2014 with this schema:
OccuredDate (date) TypeID (int)
2014-1-1 1
2014-1-2 1
2014-2-5 4
2015-5-23 2
2015-6-3 3
…it has thousands of rows comprised of dates & typeIDs, spanning years.
So that I can plot this to a charting component, I’m trying to build a query that for a given year 1) returns one row per-month that 2) counts the total number of TypeID instances for the given TypeIDs. The charting component prefers columns for the type counts.
So for "2014" it would look like this:
MonthDate TypeOne TypeTwo TypeThree TypeFour
2014-1-1 2 0 0 0
2014-2-1 0 0 0 1
or:
Year Month TypeOne TypeTwo TypeThree TypeFour
2014 Jan 2 0 0 0
2014 Feb 0 0 0 1
Spent most of the night on it but no luck. Is there some dark SQL magic that will do this?
thanks!
You can do it using pivot, with something like this:
SELECT OccuredDate, [1], [2], [3], [4]
FROM
(
SELECT OccuredDate, TypeID FROM Table1) AS SourceTable
PIVOT
(
count(TypeID) FOR TypeID IN ([1], [2], [3], [4])
) AS PivotTable
And per month version:
SELECT
DATEADD(month, DATEDIFF(month, 0, OccuredDate), 0) as Month,
sum([1]) as [1],
sum([2]) as [2],
sum([3]) as [3],
sum([4]) as [4]
FROM
(
SELECT OccuredDate, TypeID FROM Table1) AS SourceTable
PIVOT
(
count(TypeID) FOR TypeID IN ([1], [2], [3], [4])
) AS PivotTable
group by
DATEADD(month, DATEDIFF(month, 0, OccuredDate), 0)
You can test this in SQL Fiddle: daily and monthly
Edit: Rewrote the monthly SQL

How can I PIVOT TABLE Or CrossTab By Datetime?

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

How would I write a SQL query to include monthly counts for all categories with all months and categories showing even if no records exist

I'm working with SSRS and I'm trying to populate a report to show monthly counts grouped by categories. The report should have the categories listed as row headers and all months of the year shown as column headers. The requirement is that I need to display all months and categories regardless if there is data that falls within those columns/rows.
My problem is constructing the SQL query to do just that.
Here are examples of the tables I'm working with:
Transaction table:
create table [Transaction] (
ContactID int Primary Key
, CategoryID int
, DateKey int
)
Calendar table:
Note that this table was originally created as a Date Dimension to be used with SSAS but I decided not to use SSAS as cube development was getting overwhelming for me. There are many other fields in this table but these are the fields of importance regarding this issue.
create table [Calendar] (
DateID int Primary Key
, Date datetime
, Year nchar(4)
, Month nvarchar(2)
)
Category table:
create table [Category] (
CategoryID int Primary Key
, CategoryName nvarchar
)
The query needs to return a dataset to be used in SSRS to populate a report similar to the following:
Category | Jan | Feb | Mar | Apr | May | June | etc...
--------------------------------------------------------------------
Category A | - | - | - | - | - | - | etc...
--------------------------------------------------------------------
Category B | - | - | - | - | - | - | etc...
--------------------------------------------------------------------
Category C | - | - | - | - | - | - | etc...
I know that it involved some combination of OUTER JOINS, GROUP BY, and subqueries. I just can't wrap my head around how to accomplish this.
I'd be very happy if someone could assist me in this issue.
Thanks
The following query gets all the categories in the transactions. It is pivoting on the month, by extracting the month from the date and counting the number of transactions. This will return a row for all categories in the data
select c.categoryName,
sum(case when extract(month from date) = 1 then 1 else 0 end) as Jan,
sum(case when extract(month from date) = 2 then 1 else 0 end) as Feb,
sum(case when extract(month from date) = 3 then 1 else 0 end) as Mar,
. . .
from transaction t join
category c
on t.categoryID = c.categoryID
group by categoryName
order by categoryName
If you actually need all categories, even when there are no transactions at all, then use a right outer join:
select c.categoryName,
sum(case when extract(month from date) = 1 then 1 else 0 end) as Jan,
sum(case when extract(month from date) = 2 then 1 else 0 end) as Feb,
sum(case when extract(month from date) = 3 then 1 else 0 end) as Mar,
. . .
from transaction t right outer join
category c
on t.categoryID = c.categoryID
group by categoryName
order by categoryName
You could use the PIVOT function:
WITH Data AS
( SELECT CategoryName,
Calendar.[Month],
ContactID
FROM Category
LEFT JOIN [Transaction]
ON [Transaction].CategoryID = Category.CategoryID
LEFT JOIN Calendar
AND [Transaction].DateKey = Calendar.DateID
)
SELECT CategoryName,
[1] AS [Jan],
[2] AS [Feb],
[3] AS [Mar],
[4] AS [Apr],
[5] AS [May],
[6] AS [Jun],
[7] AS [Jul],
[8] AS [Aug],
[9] AS [Sep],
[10] AS [Oct],
[11] AS [Nov],
[12] AS [Dec]
FROM Data
PIVOT
( COUNT(ContactID)
FOR [Month] IN ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])
) pvt

t-sql query between a table of events and a date range

Based on the following table
id Title Date Metadata
------------------------------------
1 A 08/01/2010 M1
1 A 10/05/2010 M2
1 A 03/15/2011 M3
2 B 09/20/2010 M1
2 B 01/15/2011 M2
3 C 12/15/2010 M1
Input variables will be start and end date. So for instance,
#startDate = '07/01/2010'
#endDate = '06/30/2011'
How to generate the below output?
Title Jul-10 Aug-10 Sep-10 Oct-10 Nov-10 Dec-10 Jan-11 Feb-11 Mar-11 Apr-11 May-11 Jun-11
-------------------------------------------------------------------------------------------
A Null M1 Null M2 Null Null Null Null M3 Null Null Null
B Null M1 Null Null Null Null M2 Null Null Null Null Null
C Null Null Null Null Null M1 Null Null Null Null Null Null
What you are seeking is commonly called a crosstab query. If what you are asking is how to build a crosstab query given a static list of columns, you can do something like so:
Select Title
, Min( Case When DatePart(mm, [Date]) = 7 And DatePart(yy, [Date]) = 2010 Then MetaData End ) As [Jul-10]
, Min( Case When DatePart(mm, [Date]) = 8 And DatePart(yy, [Date]) = 2010 Then MetaData End ) As [Aug-10]
, Min( Case When DatePart(mm, [Date]) = 9 And DatePart(yy, [Date]) = 2010 Then MetaData End ) As [Sep-10]
...
From Table
Where [Date] Between #StartDate And #EndDate
Group By Title
Similarly, you can use the PIVOT functionality as suggested by Broken Link. However, both the above solution and the PIVOT functionality rely on static column declarations. If what you want is a dynamic list of columns (a.k.a. dynamic crosstab), then you are outside the bounds of what T-SQL was primarily designed to do. It is possible with some fugly dynamic SQL but it is brittle and cumbersome. Instead, you should build the resultset in a middle-tier component or use a reporting tool that will build crosstab results.
Use Pivot tables..
A simple example..
USE AdventureWorks;
GO
SELECT DaysToManufacture, AVG(StandardCost) AS AverageCost
FROM Production.Product
GROUP BY DaysToManufacture;
DaysToManufacture AverageCost
0 5.0885
1 223.88
2 359.1082
4 949.4105
Query
SELECT 'AverageCost' AS Cost_Sorted_By_Production_Days,
[0], [1], [2], [3], [4]
FROM
(SELECT DaysToManufacture, StandardCost
FROM Production.Product) AS SourceTable
PIVOT
(
AVG(StandardCost)
FOR DaysToManufacture IN ([0], [1], [2], [3], [4])
) AS PivotTable;
Result
Cost_Sorted_By_Production_Days 0 1 2 3 4
AverageCost 5.0885 223.88 359.1082 NULL 949.4105