I have the following problem: I need to implement a calendar using SQL Server 2005. Here's an example of what my stored procedure gives me so far:
TIME | DATE | CALENDAR_ID | SUBJECT | NOTES | STATUS_ID
===================================================================
09:00 | 19/08/2013 | 1 | SUBJECT 1 | NOTES 1 | 1
10:00 | 19/08/2013 | 2 | SUBJECT 2 | NOTES 2 | 2
11:00 | 19/08/2013 | 3 | SUBJECT 3 | NOTES 3 | 3
12:00 | 19/08/2013 | 4 | SUBJECT 4 | NOTES 4 | 1
09:00 | 20/08/2013 | 5 | SUBJECT 5 | NOTES 5 | 4
10:00 | 20/08/2013 | 6 | SUBJECT 6 | NOTES 6 | 3
11:00 | 20/08/2013 | 7 | SUBJECT 7 | NOTES 7 | 1
12:00 | 20/08/2013 | 8 | SUBJECT 8 | NOTES 8 | 1
But I'd like to display it like this:
TIME | 19/08/2013 | 20/08/2013
===============================
09:00 | SUBJECT 1 | SUBJECT 5
10:00 | SUBJECT 2 | SUBJECT 6
11:00 | SUBJECT 3 | SUBJECT 7
12:00 | SUBJECT 4 | SUBJECT 8
I know about the PIVOT function in SQL Server which seems to be useful for these cases, and I searched for examples and explanations, but I still don't understand completely. Besides, so far I've seen only examples like getting the total sum of sales per month; I'm not sure my calendar could use the same logic (or even if I can do what I intend to do using PIVOT). Anyway, could someone point me in the right direction about my problem? Thanks in advance.
Yes you can use the PIVOT function to convert the rows of data into columns. You will just use the aggregate function max or min to select the subject for each date. If you have a limited number of dates that you want to convert into columns then you can hard-code the query:
select [time], [19/08/2013], [20/08/2013]
from
(
select [time], [date], subject
from yourtable
) d
pivot
(
max(subject)
for [date] in ([19/08/2013], [20/08/2013])
) piv;
See SQL Fiddle with Demo
But if you have an unknown number of values, then you can use dynamic SQL in a stored procedure to get the result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(DATE)
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT [time], ' + #cols + '
from
(
select [time], [date], subject
from yourtable
) x
pivot
(
max(subject)
for [date] in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo. Both will give a result:
| TIME | 19/08/2013 | 20/08/2013 |
-----------------------------------
| 09:00 | SUBJECT 1 | SUBJECT 5 |
| 10:00 | SUBJECT 2 | SUBJECT 6 |
| 11:00 | SUBJECT 3 | SUBJECT 7 |
| 12:00 | SUBJECT 4 | SUBJECT 8 |
Related
I'm trying to sort some shipment data using a SQL Pivot but i can not figure it out.
I've the data sorted in this way (one row with the total items shipped for a family for each month of each year starting from 2015 to ):
TABLE A
Year | Month | ItemFamilyCode | TotalShipped
2018 | 9 | FA01 | 5
2018 | 9 | FA04 | 4
2018 | 10 | FA01 | 2
2018 | 11 | FA02 | 1
2018 | 12 | FA03 | 3
2019 | 1 | FA04 | 7
and so on. I want to achieve the following result:
ItemFamilyCode | 2018-9 | 2018-10 | 2018-11 | 2018-12 | 2019-1 | [..]
FA01 | 5 | 2 | 0 | 0 | 0 |
FA02 | 0 | 0 | 1 | 0 | 0 |
FA03 | 0 | 0 | 0 | 3 | 0 |
FA04 | 4 | 0 | 1 | 0 | 7 |
and so on ... the family code in order and all the values for each month of each year, from the older month/year to now. Is it possible? Thanks to anyone who can help.
If you want to use it as view :
SELECT * FROM
(
SELECT
Concat([Year],'-', [Month]) as [Date],
ItemFamilyCode,
TotalShipped
FROM Shipping -- Or any Table Name
) t
PIVOT(
Sum(TotalShipped)
FOR [Date] IN (
[2018-9],
[2018-10],
[2018-11],
[2018-12],
[2019-1],
[2019-2] -- You have to type all months until today
)
) AS pivot_table;
And, dynamic sql if you can use it in stored procedure :
Make a table with the content of date list to generate date list string
DECLARE
#columns NVARCHAR(MAX) = '',
#sql NVARCHAR(MAX) = '';
-- select the category names
SELECT
#columns+=QUOTENAME(Date) + ','
FROM
DateList
ORDER BY
DateList;
-- remove the last comma
SET #columns = LEFT(#columns, LEN(#columns) - 1);
-- construct dynamic SQL
SET #sql ='
SELECT * FROM
(
SELECT
Concat([Year],'-', [Month]) as [Date],
ItemFamilyCode,
TotalShipped
FROM Shipping -- Or any Table Name
) t
PIVOT(
Sum(TotalShipped)
FOR [Date] IN ('+ #columns +')
) AS pivot_table;';
-- execute the dynamic SQL
EXECUTE sp_executesql #sql;
Source : sqlservertutorial
I have a table like this.
------------------------------------
Id | Name | Date
-----------------------------------
1 | Syam | 2017-05-23 13:53:20.000
2 | Syam | 2017-05-22 13:53:20.000
3 | Syam | NULL
4 | Ram | 2017-05-23 13:53:20.000
5 | Ram | 2017-05-21 13:53:20.000
I need to find out the users who have submitted their log in 7 days prior to the specified date input by user.
The result will be like this
Input: 2017-05-28 13:53:20.000
Id | Name | 05/23/2017 tuesday | 05/22/2017 Monday | 05/21/2017
--------------------------------------------------------------
1 |Syam | True | true | False
2 |Ram | True | False | True
Can some one help me to do this. I think pivot can be used for this. But I am not familiar with that.
you can use pivot
select pvt.* from
(
select id,name,DATENAME(WEEKDAY, date1) as DayName from t
) as t1
PIVOT
(max(name) for DayName in ([Tuesday],[Monday],[Sunday] )) as pvt -- you can put 7days name here
I'm attempting to combine multiple rows using a UNION but I need to pull in additional data as well. My thought was to use a UNION in the outer query but I can't seem to make it work. Or am I going about this all wrong?
The data I have is like this:
+------+------+-------+---------+---------+
| ID | Time | Total | Weekday | Weekend |
+------+------+-------+---------+---------+
| 1001 | AM | 5 | 5 | 0 |
| 1001 | AM | 2 | 0 | 2 |
| 1001 | AM | 4 | 1 | 3 |
| 1001 | AM | 5 | 3 | 2 |
| 1001 | PM | 5 | 3 | 2 |
| 1001 | PM | 5 | 5 | 0 |
| 1002 | PM | 4 | 2 | 2 |
| 1002 | PM | 3 | 3 | 0 |
| 1002 | PM | 1 | 0 | 1 |
+------+------+-------+---------+---------+
What I want to see is like this:
+------+---------+------+-------+
| ID | DayType | Time | Tasks |
+------+---------+------+-------+
| 1001 | Weekday | AM | 9 |
| 1001 | Weekend | AM | 7 |
| 1001 | Weekday | PM | 8 |
| 1001 | Weekend | PM | 2 |
| 1002 | Weekday | PM | 5 |
| 1002 | Weekend | PM | 3 |
+------+---------+------+-------+
The closest I've come so far is using UNION statement like the following:
SELECT * FROM
(
SELECT Weekday, 'Weekday' as 'DayType' FROM t1
UNION
SELECT Weekend, 'Weekend' as 'DayType' FROM t1
) AS X
Which results in something like the following:
+---------+---------+
| Weekday | DayType |
+---------+---------+
| 2 | Weekend |
| 0 | Weekday |
| 2 | Weekday |
| 0 | Weekend |
| 10 | Weekday |
+---------+---------+
I don't see any rhyme or reason as to what the numbers are under the 'Weekday' column, I suspect they're being grouped somehow. And of course there are several other columns missing, but since I can't put a large scope in the outer query with this as inner one, I can't figure out how to pull those in. Help is greatly appreciated.
It looks like you want to union all a pair of aggregation queries that use sum() and group by id, time, one for Weekday and one for Weekend:
select Id, DayType = 'Weekend', [time], Tasks=sum(Weekend)
from t
group by id, [time]
union all
select Id, DayType = 'Weekday', [time], Tasks=sum(Weekday)
from t
group by id, [time]
Try with this
select ID, 'Weekday' as DayType, Time, sum(Weekday)
from t1
group by ID, Time
union all
select ID, 'Weekend', Time, sum(Weekend)
from t1
group by ID, Time
order by order by 1, 3, 2
Not tested, but it should do the trick. It may require 2 proc sql steps for the calculation, one for summing and one for the case when statements. If you have extra lines, just use a max statement and group by ID, Time, type_day.
Proc sql; create table want as select ID, Time,
sum(weekday) as weekdayTask,
sum(weekend) as weekendTask,
case when calculated weekdaytask>0 then weekdaytask
when calculated weekendtask>0 then weekendtask else .
end as Task,
case when calculated weekdaytask>0 then "Weekday"
when calculated weekendtask>0 then "Weekend"
end as Day_Type
from have
group by ID, Time
;quit;
Proc sql; create table want2 as select ID, Time, Day_Type, Task
from want
;quit;
Given a table that stores every revision for every item.
For example:
+--------+----------+---------------+--------+---------------------+
| ItemId | Revision | PreviousState | State | DateChanged |
+--------+----------+---------------+--------+---------------------+
| 1 | 1 | NULL | New | 2014-11-13 10:00:00 |
| 1 | 2 | New | Active | 2014-11-15 10:00:00 |
| 1 | 3 | Active | New | 2014-11-17 10:00:00 |
| 1 | 4 | New | Active | 2014-11-19 10:00:00 |
| 1 | 5 | New | Active | 2014-11-20 10:00:00 |
| 1 | 6 | Active | Closed | 2014-11-22 10:00:00 |
| 2 | 1 | NULL | New | 2014-11-13 10:00:00 |
| 2 | 2 | New | Active | 2014-11-16 10:00:00 |
| 2 | 3 | Active | Closed | 2014-11-17 10:00:00 |
| 2 | 4 | Closed | Active | 2014-11-19 10:00:00 |
| 2 | 5 | Active | Closed | 2014-11-21 10:00:00 |
+--------+----------+---------------+--------+---------------------+
I need to calculate how many days each item was in each state (except 'Close').
Result should be like this:
+--------+-----+--------+
| ItemId | New | Active |
+--------+-----+--------+
| 1 | 4 | 5 |
| 2 | 3 | 3 |
+--------+-----+--------+
I tried to use two approaches - GROUP BY and nested cursors.
Using cursors (especially nested cursors) is a bad practice. And the are very slow.
GROUP BY also won't work because there is no strict order of states (New -> Active -> Closed). It could be chaotic New -> Active -> Closed -> Active -> Closed -> New -> Closed.
I don't see any other way to calculate it without iterating all the records and comparing states.
Is there any solution?
Thanks in advance.
This gives you the same results you're asking for, in a slightly different format (but you can easily find PIVOT solutions if you need the exact same result set):
declare #t table (ItemId int,Revision int,State varchar(19),DateChanged datetime2)
insert into #t(ItemId,Revision,State,DateChanged) values
(1,1,'New', '2014-11-13T10:00:00'),
(1,2,'Active','2014-11-15T10:00:00'),
(1,3,'New', '2014-11-17T10:00:00'),
(1,4,'Active','2014-11-19T10:00:00'),
(1,5,'Active','2014-11-20T10:00:00'),
(1,6,'Closed','2014-11-22T10:00:00'),
(2,1,'New', '2014-11-13T10:00:00'),
(2,2,'Active','2014-11-16T10:00:00'),
(2,3,'Closed','2014-11-17T10:00:00'),
(2,4,'Active','2014-11-19T10:00:00'),
(2,5,'Closed','2014-11-21T10:00:00')
;With Joined as (
select t1.ItemId,t1.State,DATEDIFF(day,t1.DateChanged,t2.DateChanged) as Days
from
#t t1
inner join
#t t2
on
t1.ItemId = t2.ItemId and
t1.Revision = t2.Revision -1
)
select ItemId,State,SUM(Days)
from Joined
where State <> 'Closed'
group by ItemId,State
Result:
ItemId State
----------- ------------------- -----------
1 Active 5
1 New 4
2 Active 3
2 New 3
Note that I'm ignoring the PreviousState column from your question and am instead constructing Joined because what really matters is when the next state came into effect.
Issues not dealt with because you've not described them in your question: 1) What to do if the current final state isn't Closed - i.e. do we ignore that, or count until today?, and 2) What to do if the time of day for each DateChanged isn't the same - do we have to handle partial days?
Personally I like the CTE from [Damien_The_Unbeliever], I need to use them more often. Using inner joins I basically do the same thing the add a pivot wrapper around the results to get what you are looking for: (replace #t for your real table name)
SELECT ItemId , [New],[Active]
FROM
(
SELECT
ItemId , LASTSTATE, DATEDIFF(D, LASTDATE, DateChanged) AS D
FROM
#T AS T
INNER JOIN
(SELECT
ItemId as ItemLink,
Revision + 1 AS RevLink ,
DateChanged AS LASTDATE ,
State AS LASTSTATE from #t
) AS L ON T.ItemId = L.ItemLink AND T.Revision = L.RevLink
) AS P PIVOT ( SUM(D) FOR LASTSTATE IN ([New],[Active],[Closed])) AS DATA
I have a table:
Comments (Id int identity primary key, PersonId Int(6), Comment1 Char(60))
The field Comment1 is supposed to contain nonessential comments only, however the field has been used to store nonessential comments but also important dates in the format dd/mm/yyyy.
So for example, the data in the table appears like the following:
* Id * PersonId * Comment1 *
| 1 | 5 | Likes Chocolate |
| 2 | 5 | 19/05/1992 |
| 3 | 5 | 23/07/1999 |
| 4 | 5 | 05/06/1994 |
| 5 | 8 | 07/12/1998 |
| 6 | 8 | Is very Tall |
| 7 | 8 | 24/05/1995 |
| 8 | 8 | 16/10/2002 |
| 9 | 11 | 13/11/2005 |
| 10 | 11 | 21/09/2000 |
| 11 | 11 | 8/99/100/23-1 |
SQL Fiddle
They now need to find the one row for each person which contains the most recent date in the Comment1 field.
Firstly I tried to remove those rows which did not contain a valid date using the following Query:
Select
Id,
PersonId,
Case
When IsDate(Comment1) = 1
Then convert(date, cast(rtrim(Comment1) as nvarchar), 103)
Else NULL
End
From Comments
But this only returned me:
* Id * PersonId * Comment1 *
| 4 | 5 | 1994-06-05 |
| 5 | 8 | 1998-12-07 |
The other rows being (null). This can be seen in the SQL Fiddle link.
Could some one please tell me where I'm going wrong?
Once this select statement returns what I require I expect I should be able to return the most recent date per PersonId using an aggregate function.
Use SET DATEFORMAT
set dateformat dmy
Select
Id,
PersonId,
Case
When IsDate(Comment1) = 1
Then convert(date, cast(rtrim(Comment1) as nvarchar), 103)
Else NULL
End
From Comments
SQL Fiddle