Split single column data into two columns - sql

Name time type
--------------------------------------------
Abc 2014-01-11 10:41:37.000 In
Abc 2014-01-11 18:12:37.000 Out
def 2014-01-07 18:25:37.000 In
def 2014-01-07 20:00:02.000 Out
How to split this data by in out and by name in SQL Server? Result should look like this
Name IN Out
---------------------------------------------------------
Abc 2014-01-11 10:41:37.000 2014-01-11 18:12:37.000
def 2014-01-07 18:25:37.000 2014-01-07 20:00:02.000
Please help me

How about pair them together (in and out), if that's what you need. Then you can filter "in" and always find nearest corresponding "out" after that one. I guess the answer here can be really complicated or really easy depending on your data (what are assumptions and how dirty they can be).
In the following example [log] table contains your data.
select name,
time as [In],
(select top 1 time from [log] il where il.name=ol.name and il.time>ol.time order by il.time) as [Out]
from [log] ol
where type='in'
Hope that helps.

i would like to introduce with pivot in sql server which developer usually use in these situations :
http://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx
http://blogs.msdn.com/b/spike/archive/2009/03/03/pivot-tables-in-sql-server-a-simple-sample.aspx
for your example i am bulding a table and solving your problem using pivot:
Create Table #temp (Name varchar(10) , [time] datetime , type char(3)
)
puting some sample data
insert into #temp SELECT 'Abc' , '2014-01-11 10:41:37.000', 'In'
union all SELECT 'Abc' , '2014-01-11 18:12:37.000', 'Out' union
all SELECT 'def', '2014-01-07 18:25:37.000', 'In' union all
SELECT 'def' , '2014-01-07 20:00:02.000', 'Out'
and here is pivot query
SELECT Name , [IN] ,[OUT] FROM ( Select NAme , [Time] , [Type] from
#temp ) InnerTBL PIVOT
( max([Time]) FOR [Type] in ([IN] , [OUT]) )TBL

Related

UNPIVOT Holiday Hours

I have a table, that keeps track of store holiday hours:
LOCATION_ID DATE1 TIMES1 DATE2 TIMES2
123456 2020-12-12 10:00AM-09:00PM 2020-12-19 10:00AM-09:00PM
This is a highly oversimplified table. There's about 30 columns horzontially consisting of store operating hours by date - It continues (DATE3, TIMES3, DATE4, TIMES4, etc).
I need to unpivot the values vertically, ensuring the date and time values are on the same record.
(NOTE: Once I figure out to structure the UNPIVOT expression properly, I will use Dynamic SQL on my own to pivot the column names)
Desired Outcome:
LOCATION_ID DATE TIME
123456 2020-12-12 10:00AM-09:00PM
123456 2020-12-19 10:00AM-09:00PM
I tried using UNPIVOT, but I'm stuck. Any ideas?
SAMPLE DATA:
CREATE TABLE #HOURS (LOCATION_ID int, DATE1 varchar(255), TIMES1 varchar(255), DATE2
varchar(255), TIMES2 varchar(255));
INSERT INTO #HOURS VALUES ('123456', '2020-12-12', '10:00AM-09:00PM','2020-12-19','10:00AM-09:00PM' )
Code that I tried:
SELECT *
FROM (SELECT location_id,
[date1],
[times1],
[date2]
FROM #hours) AS cp
UNPIVOT ( pivotvalues
FOR pivvalues IN ([Date1],
[date2],
[times1]) ) AS up1
Gordon is 100% correct (+1).
However, if you are looking for a dynamic approach WITHOUT having to use Dynamic SQL, consider the following.
Example
Select Location_ID
,Date = max(case when [Item] like 'DATE%' then Value end)
,Time = max(case when [Item] like 'TIME%' then Value end)
From (
select A.Location_ID
,Grp = replace(replace([Item],'DATE',''),'TIMES','')
,B.*
from #hours A
Cross Apply [dbo].[tvf-XML-Unpivot-Row]( (Select A.* for XML RAW) ) B
Where [Item] not in ('LOCATION_ID')
) A
Group By Location_ID,Grp
Returns
Location_ID Date Time
123456 2020-12-12 10:00AM-09:00PM
123456 2020-12-19 10:00AM-09:00PM
The Table-Valued Function if Interested
CREATE FUNCTION [dbo].[tvf-XML-UnPivot-Row](#XML xml)
Returns Table
As
Return (
Select Item = xAttr.value('local-name(.)', 'varchar(100)')
,Value = xAttr.value('.','varchar(max)')
From #XML.nodes('//#*') xNode(xAttr)
)
Don't use unpivot. Use apply:
select h.location_id, v.date, v.time
from #hours h cross apply
(values (h.date1, h.times1), (h.date2, h.times2)
) v(date, time);
unpivot is non-standard syntax that does exactly one thing. APPLY is the SQL Server implementation of lateral joins. This is a very powerful join type -- using it for unpivoting is a good way to start learning the syntax.

Getting an average of span of dates when columns are the same but rows are different

I need help getting an average for lead times on service calls. So here is what i am currently working with.
***name Date_Assigned Date_Completed***
jon 8/17/2017 8/20/2017
jon 9/10/2017 9/11/2017
lucy 8/5/2017 8/5/2017
jon 8/19/2017 9/27/2017
I would like to find out:
1.) What the average service lead time for everyone collectively is.
2.) What the average service lead time per name is.
From what i have tried, i have to make the date format and cast it as datetime. but cannot seem to find the right solution! Can anyone help me out??? I am using SQL management Studio or t-sql syntax. Thank you!
If avg window function is supported you can use,
select distinct name
,avg(datediff(day,date_assigned,date_completed)) over(partition by name) as avg_per_name
,avg(datediff(day,date_assigned,date_completed)) over() as avg_overall
from tbl
One method is:
select name,
avg ( datediff(day, date_assigned, date_completed) )
from t
group by grouping sets ( (name), () );
This puts the overall average in a separate row with a NULL value for name.
-- Setup table
SET NOCOUNT ON;
declare #temp table
(
Name varchar(50),
Date_Assigned datetime,
Date_Completed datetime
)
INSERT INTO #temp
SELECT
'jon' , '8/17/2017' ,'8/20/2017' UNION SELECT ALL
'jon' , '9/10/2017' ,'9/11/2017' UNION SELECT ALL
'lucy', '8/5/2017' ,'8/5/2017' UNION SELECT ALL
'jon' , '8/19/2017' ,'9/27/2017'
select name, AVG(datediff(day, Date_Assigned, Date_Completed)) from #temp group by name
union select 'ALL', AVG(datediff(day, Date_Assigned, Date_Completed)) from #temp

How do I alias particular values in SQL?

I will present a question about 'aliasing' values from a column. I will use days of the week as an intuitive example to get my question across, but I am not asking for datetime conversions.
Suppose I have the following SQL script:
SELECT DaysOfWeek
FROM [databasename].[dbo].[tablename]
Now, the column DaysOfWeek will return string values of the days' names, i.e. "Monday," "Tuesday," and so forth.
What if I wanted the query to return the integer 1 for 'Monday', 2 for 'Tuesday', and so forth? I would want to assign a particular value to each of the week's days in the SELECT statement, but I'm not sure how to go about doing that.
I'm relatively new to SQL, so I just thought I'd ask for an intuitive method to perform such a task.
Edited to add: I'm only using days of the week and their respective integer representation as an easy example; my task does not involve days of the week, but rather employee code numbers and corresponding titles.
You can do this using case:
SELECT (CASE DaysOfWeek
WHEN 'Monday' THEN 1
WHEN 'Tuesday' THEN 2
. . .
END)
Under most circumstances, it is unnecessary to store the day of the week like this. You can readily use a function, datepart() or datename(), to extract the day of the week from a date/time value.
If the column is in a table, and not part of a date, then you might want to include the above logic as a computed column:
alter table t add DayOfWeekNumber as (case DaysOfWeek when 'Monday' then 1 . . .);
Use CASE, here you have the definition and one example :
select
CASE
WHEN(DaysOfWeek="Monday") THEN 1
WHEN(DaysOfWeek="Thusday") THEN 2
....
ELSE -1
from table
Hope this help!
If you wanted to define your own corresponding value for another value, the best way is to use a table, and join that table.
For example:
create table dbo.EmployeeTitle (
id int not null identity(1,1) primary key
, title varchar(32)
);
create table dbo.Employee (
id int not null identity(1,1) primary key
, name nvarchar(128)
, title_id int references dbo.EmployeeTitle(id)
);
insert into dbo.EmployeeTitle values ('Big boss');
insert into dbo.Employee values ('daOnlyBG',1);
select e.*, et.title
from dbo.Employee e
inner join dbo.EmployeeTitle et
on e.title_id = et.id
rextester demo: http://rextester.com/FXIM78632
returns:
+----+----------+----------+----------+
| id | name | title_id | title |
+----+----------+----------+----------+
| 1 | daOnlyBG | 1 | Big boss |
+----+----------+----------+----------+
The easiest way I can think of is to have a table variable or CTE; create your lookup as rows and join to it. Something like this:
with cte as (
select 1 as emp_code, 'value1' as emp_title
union
select 2 as emp_code, 'value2' as emp_title
union
select 3 as emp_code, 'value3' as emp_title
)
select cte.emp_code, tableName.*
from tableName
inner join cte
on cte.emp_title = tableName.some_column

T-SQL pivot with count on pivoted results

I have the following data that I would like to pivot and get a count based on the pivoted results.
DECLARE #tempMusicSchoolStudent TABLE
(school VARCHAR(50),
studentname VARCHAR(50),
instrumentname VARCHAR(255),
expertise INT)
INSERT INTO #tempMusicSchoolStudent(school, studentname, instrumentname, expertise)
SELECT 'Foster','Matt','Guitar','10'
UNION
SELECT 'Foster','Jimmy','Guitar','5'
UNION
SELECT 'Foster','Jimmy','Keyboard','8'
UNION
SELECT 'Foster','Ryan','Keyboard','9'
UNION
SELECT 'Midlothean','Kyle','Keyboard','10'
UNION
SELECT 'Midlothean','Mary','Guitar','4'
UNION
SELECT 'Midlothean','Mary','Keyboard','7'
Raw data:
I'd like the results to look like the data below....
I got this data using the sql query below. The problem with this query is that I have a dynamic amount of instruments (I've only shown 2 in this example for simplicity sake). I'd like to use pivot because it will be cleaner dynamic sql. Otherwise I would have to dynamically left join the table to itself for each instrument.
SELECT
t.school, t.instrumentname, t.expertise,
t1.instrumentname, t1.expertise,
COUNT(DISTINCT t.studentname) [DistinctStudentCount]
FROM
#tempMusicSchoolStudent t
LEFT JOIN
#tempMusicSchoolStudent t1 ON t1.school = t.school
AND t1.studentname = t.studentname
AND t.instrumentname <> t1.instrumentname
GROUP BY
t.school, t.instrumentname, t.expertise, t1.instrumentname, t1.expertise
ORDER BY
t.school, t.instrumentname, t.expertise, t1.instrumentname, t1.expertise
If anyone has any ideas on how I can do this in a cleaner way than dynamically left joining the table to itself it would be much appreciated. Thanks.
You just need conditional aggregation:
SELECT t.school, t.instrumentname, t.expertise, t.instrumentname,
COUNT(DISTINCT t.studentname) as DistinctStudentCount
FROM #tempMusicSchoolStudent t
GROUP BY t.school, t.instrumentname, t.expertise, t.instrumentname;
You have rows with NULL values. It is entirely unclear where those come from. Your question is focused on some notion of "pivoting" where it seems that you only need aggregation. But it doesn't explain where the NULL rows comes from.
You can try to make it dynamic for multipe instruments. Refer
;with cte
as
(
SELECT * from
(SELECT * FROM #tempMusicSchoolStudent t) x
PIVOT
(MAX(expertise) FOR instrumentname in ([Guitar], [Keyboard])) y
)
SELECT school, studentname,
expertise = case when Guitar is not null then 'Guitar' else NULL end,
Guitar AS instrumentname,
expertise = case when Keyboard is not null then 'Keyboard' else NULL end,
Keyboard AS instrumentname,
count(distinct studentname) AS [DistinctStudentCount]
from cte
group by school,studentname, Guitar, Keyboard
OUTPUT:
Foster Jimmy Guitar 5 Keyboard 8 1
Foster Matt Guitar 10 NULL NULL 1
Foster Ryan NULL NULL Keyboard 9 1
Midlothean Kyle NULL NULL Keyboard 10 1
Midlothean Mary Guitar 4 Keyboard 7 1
Here's the solution I was looking for, I had to use unpivot + pivot.
The real thing that I was struggling with was selecting multiple values for the column that is being pivoted, instead of the max value.
So in this case I wanted multiple "expertise" numbers under a given "instrument expertise" column. Not just the maximum expertise for that instrument.
The first key to understanding the solution is that the pivot statement is doing an implicit group by on the columns being selected. So in order to achieve multiple values under your pivoted column you have to keep the integrity of the column you are grouping on by including some type of dense_rank/rank/row_number. This basically represents changes in the value of the column you are pivoting on and is then included in the implicit group by the pivot is doing, which results in getting multiple values in the pivoted column, not just the max.
So in the code below the "expertisenum" column is keeping the integrity of the expertise data.
DECLARE #tempMusicSchoolStudent TABLE
(school VARCHAR(50),
studentname VARCHAR(50),
instrumentname VARCHAR(255),
expertise INT)
INSERT INTO #tempMusicSchoolStudent(school, studentname, instrumentname, expertise)
SELECT 'Foster','Matt','Guitar','10'
UNION
SELECT 'Foster','Jimmy','Guitar','5'
UNION
SELECT 'Foster','Jimmy','Keyboard','8'
UNION
SELECT 'Foster','Ryan','Keyboard','9'
UNION
SELECT 'Midlothean','Kyle','Keyboard','10'
UNION
SELECT 'Midlothean','Mary','Guitar','4'
UNION
SELECT 'Midlothean','Mary','Keyboard','7'
SELECT school, [Guitar expertise], [Keyboard expertise], COUNT(*) [Count]
FROM
(
SELECT school,[expertiseNum],
CASE WHEN [Columns]='expertise' THEN instrumentname + ' expertise'
END [Columns1], [Values] AS [Values1]
FROM
(
SELECT school, studentname, instrumentname, DENSE_RANK() OVER(PARTITION BY school,instrumentname ORDER BY expertise) AS [expertiseNum],
CONVERT(VARCHAR(255),expertise) AS [expertise]
FROM #tempMusicSchoolStudent
) x
UNPIVOT (
[Values] FOR [Columns] IN ([expertise])
) unpvt
) p
PIVOT (
MAX([Values1]) FOR [Columns1] IN ([Guitar expertise], [Keyboard expertise])
) pvt
GROUP BY school,[Guitar expertise], [Keyboard expertise]

Semivariance of variable

I'm new with sql and I struggle with such a problem. Let's suppose I have a table like this:
Date Value
2014-01-01 1248.56
2014-01-02 1247.24
2014-01-03 1245.82
2014-01-04 1252.07
...
All I want to do is count semivariance of variable 'Value'.
Semivariance only takes into account those records which are less than the average of the sample. So basically it is just a transformartion of simply variance.
Any help would be appreciated!
you can try something like this
SELECT COUNT(*)
FROM
Table1
WHERE Value < (SELECT AVG(Value) FROM Table1)
If you need avg value then you can use such code.
CREATE TABLE #test
(
date DATE,
value NUMERIC(10,2)
)
INSERT INTO #test VALUES ('2014-01-01' , 1248.56 ),
('2014-01-02' , 1247.24),
('2014-01-03' , 1245.82),
('2014-01-04' , 1252.07);
SELECT * FROM #test a
CROSS JOIN (SELECT AVG(value) avg_value FROM #test) b
WHERE a.value < b.avg_value