Select count Columns group by No - sql

Emp_No Emp_Shift Emp_Date
500 AL 1/5/2015
600 S 2/5/2015
600 H 3/5/2015
500 S 4/5/2015
500 AL 5/5/2015
600 AL 6/5/2015
I need help on this issue , HOW TO RETURN count in 3 Columns >>
EX:
Emp_No Count Al Count S Count H
500 2 1 0
600 1 1 1

this will work. you have to provide separate case statement to each condition
SQLFIDDLE for the same SQLFIDDLE
SELECT EMP_NO,
sum(CASE WHEN Emp_Shift = 'AL' THEN 1 ELSE 0 END) AS COUNT_AL,
sum(CASE WHEN Emp_Shift = 'S' THEN 1 ELSE 0 END) AS COUNT_S,
sum(CASE WHEN Emp_Shift = 'H' THEN 1 ELSE 0 END) AS COUNT_H
FROM YOUR_TABLE
GROUP BY EMP_NO;

You can try this:-
SELECT EMP_NO, COUNT(CASE
WHEN Emp_Shift = 'AL' THEN 1 ELSE 0 END AS COUNT_AL
WHEN Emp_Shift = 'S' THEN 1 ELSE 0 END AS COUNT_S
WHEN Emp_Shift = 'H' THEN 1 ELSE 0 END AS COUNT_H)
FROM YOUR_TABLE
GROUP BY EMP_NO;

Using Pivot :
declare #YOUR_TABLE TABLE
([Emp_No] int, [Emp_Shift] varchar(2), [Emp_Date] datetime)
;
INSERT INTO #YOUR_TABLE
([Emp_No], [Emp_Shift], [Emp_Date])
VALUES
(500, 'AL', '2015-01-05 00:00:00'),
(600, 'S', '2015-02-05 00:00:00'),
(600, 'H', '2015-03-05 00:00:00'),
(500, 'S', '2015-04-05 00:00:00'),
(500, 'AL', '2015-05-05 00:00:00'),
(600, 'AL', '2015-06-05 00:00:00')
;
select Emp_No,[AL] AS [Count Al],[S] As [Count S],[H] AS [Count H] from (
select Emp_No,[Emp_Shift],[Emp_Date] from #YOUR_TABLE)T
PIVOT(COUNT(Emp_Date) FOR Emp_Shift IN ([AL],[S],[H]))P
Using Pivot in Dynamic QUery :
if object_id('tempdb..#t') is not null
drop table #t
CREATE TABLE #t
([Emp_No] int, [Emp_Shift] varchar(2), [Emp_Date] datetime)
;
INSERT INTO #t
([Emp_No], [Emp_Shift], [Emp_Date])
VALUES
(500, 'AL', '2015-01-05 00:00:00'),
(600, 'S', '2015-02-05 00:00:00'),
(600, 'H', '2015-03-05 00:00:00'),
(500, 'S', '2015-04-05 00:00:00'),
(500, 'AL', '2015-05-05 00:00:00'),
(600, 'AL', '2015-06-05 00:00:00')
;
DECLARE #statement NVARCHAR(max)
,#columns NVARCHAR(max)
SELECT #columns = ISNULL(#columns + ', ', '') + N'[' + tbl.Emp_Shift + ']'
FROM (
SELECT DISTINCT [Emp_Shift]
FROM #t
) AS tbl
SELECT #statement = ' select Emp_No,[AL] AS [Count Al],[S] As [Count S],[H] AS [Count H] from (
select Emp_No,[Emp_Shift],[Emp_Date] from #t)T
PIVOT(COUNT(Emp_Date) FOR Emp_Shift IN (' + #columns + ')) as pvt'
EXEC sp_executesql #statement = #statement

Related

How can I manipulate a string a T-SQL?

business_id
open_day
open_time
close_day
close_time
1
FRIDAY
08:00
FRIDAY
12:00
1
FRIDAY
13:00
FRIDAY
17:00
1
MONDAY
08:00
MONDAY
17:00
2
SATURDAY
08:00
SATURDAY
16:00
2
SUNDAY
08:00
SUNDAY
16:00
3
MONDAY
08:00
MONDAY
16:00
I have a task to create and format opening hours for a business, I'm required to group these by days in a single string. However, it is repeating the day. Would it be possible to search for a particular value if it occurs twice within a string? I have gotten this far:
create table open_times (business_id int, open_day varchar(10), open_time varchar(10), close_day varchar(10), close_time varchar(10) )
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (1, 'FRIDAY', '08:00', 'FRIDAY', '12:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (1, 'FRIDAY', '13:00', 'FRIDAY', '17:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (1, 'MONDAY', '08:00', 'MONDAY', '17:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (2, 'SATURDAY', '08:00', 'SATURDAY', '16:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (2, 'SUNDAY', '08:00', 'SUNDAY', '16:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (3, 'MONDAY', '08:00', 'MONDAY', '16:00')
drop table open_times
I appreciate your help.
select
business_id
,left(name_values, LEN(name_values)-1) as opening_hours
from
(
select
results.business_id
,STUFF((
select
( case when open_day = 'FRIDAY' then 'Fr' when open_day = 'MONDAY' then 'Mo' when open_day = 'TUESDAY' then 'Tu' when open_day = 'WEDNESDAY' then 'We' When open_day = 'THURSDAY' then 'Th' when open_day = 'SATURDAY' then 'Sa' else 'Su' end )
+ ' ' + open_time + '-' + close_time + '; '
from open_times
where business_id = results.business_id
for xml path(''),type).value('(./text())[1]','VARCHAR(MAX)'),1,0, '') as name_values
from open_times results
group by business_id
) innerquery
Current Output for Business 1: 'Fr 08:00-12:00; Fr 13:00-17:00; Mo 08:00-17:00'
Desired Output For Business 1: 'Fr 08:00-12:00, 13:00-17:00; Mo 08:00-17:00'
You can use STRING_AGG here. You just need to do two levels of grouping, once per day, then again for the whole business_id
SELECT
ot.business_id,
Times = STRING_AGG(CONCAT(
UPPER(LEFT(ot.open_day, 1)),
LOWER(SUBSTRING(ot.open_day, 2, 1)),
' ',
ot.Times)
, '; ')
FROM (
SELECT
ot.business_id,
ot.open_day,
Times = STRING_AGG(CONCAT(
ot.open_time,
'-',
ot.close_time),
', ')
FROM open_times ot
GROUP BY ot.business_id, ot.open_day
) ot
GROUP BY ot.business_id;
db<>fiddle
You can do this with one level of aggregation by incorporating window functions into your logic:
select ot.business_id,
stuff((select (case when seqnum = 1 then '; ' else ', ' end) +
(case when seqnum = 1 and ot2.open_day = 'FRIDAY' then 'Fr '
when seqnum = 1 and ot2.open_day = 'MONDAY' then 'Mo '
when seqnum = 1 and ot2.open_day = 'TUESDAY' then 'Tu '
when seqnum = 1 and ot2.open_day = 'WEDNESDAY' then 'We '
when seqnum = 1 and ot2.open_day = 'THURSDAY' then 'Th '
when seqnum = 1 and ot2.open_day = 'SATURDAY' then 'Sa '
when seqnum = 1 then 'Su '
else ''
end ) +
ot2.open_time + '-' + ot2.close_time
from (select ot2.*,
row_number() over (partition by ot2.open_day order by ot2.open_time) as seqnum
from open_times ot2
where ot2.business_id = ot.business_id
) ot2
order by ot2.open_day, ot2.open_time
for xml path(''),type
).value('(./text())[1]', 'VARCHAR(MAX)'), 1, 2, ''
) as name_values
from open_times ot
group by ot.business_id;
Here is a db<>fiddle.
Your version of the query is actually incorrect, because you do not have an order by in the XML subquery. The results can be in any order.
In addition, you don't just want to remove the duplicate names, but you also want to change the delimiter from ; to ,.
The idea in the above query is that the times are enumerated on each day. The delimiter is ; for the first time and , for the subsequent ones. The abbreviation is used only for the first one. I also added qualifications to all the column references, a highly recommended practice.
Note that you can simplify the case logic to:
left(ot2.open_day, 1) + lower(substring(ot2.open_day, 2, 1))
For older versions use FOR XML PATH aggregation trick. Assuming no more then 2 open intervals a day
with t as (
select business_id, left(open_day,2) + ' ' + min(open_time + '-' + close_time) +
case when min(open_time) = max(open_time) then ''
else ', ' + max(open_time + '-' + close_time) end ots
from open_times
group by business_id, open_day
)
select business_id, stuff(
(select '; ' + ots
from t t2
where t2.business_id = t1.business_id
order by left(ots,2)
for xml path(''))
, 1, 2, '') opening_hours
from t t1
group by business_id

SQL Server : group by calculated column

I have a table with columns like this:
I want to know the average compliance rate for every question for that period.
I am passing start dates and end dates as parameters to query.
So if I want for two periods I am passing #StartDate (e.g. 1/6/17) and #EndDate (e.g. 30/6/17) for first period and #StartDate2 (1/10/17) and #EndDate2 (31/10/17) for second period.
My SQL query is:
;WITH tmpTab AS
(
SELECT
question,
SUM((CASE WHEN AnswerValue = 1 THEN 1 ELSE 0 END)) Met,
SUM((CASE WHEN AnswerValue = 3 THEN 1 ELSE 0 END)) NA,
SUM((CASE WHEN (ISNULL(AnswerValue,3) <> 3) THEN 1 ELSE 0 END)) MetNotMet,
DATENAME(DAY, #StartDate) + ' ' + DATENAME(MONTH, #StartDate) + ' ' +
DATENAME(YEAR, #StartDate) + ' To ' + DATENAME(DAY,#EndDate) + ' ' +
DATENAME(MONTH, #EndDate) + ' ' + DATENAME(YEAR, #EndDate) AS RepMonthAndYear
FROM
tableA
WHERE
startdate >= #StartDate AND endate <= #EndDate
GROUP BY
Question
UNION ALL
SELECT
question,
SUM((CASE WHEN AnswerValue = 1 THEN 1 ELSE 0 END)) Met,
SUM((CASE WHEN AnswerValue = 3 THEN 1 ELSE 0 END)) NA,
SUM((CASE WHEN (ISNULL(AnswerValue,3) <> 3) THEN 1 ELSE 0 END)) MetNotMet,
DATENAME(DAY,#StartDate2)+' '+DATENAME(MONTH,#StartDate2)+' '+DATENAME(YEAR,#StartDate2)+ ' To ' + DATENAME(DAY,#EndDate2)+' '+DATENAME(MONTH,#EndDate2)+' '+DATENAME(YEAR,#EndDate2) AS RepMonthAndYear
FROM
tableA
WHERE
startdate >= #StartDate2 AND endate <= #EndDate2
GROUP BY
Question
)
SELECT
Question, Met, NA, MetNotMet,
CASE WHEN (Met) = 0 THEN 0 ELSE ROUND(((CONVERT(FLOAT,(Met))/(MetNotMet))* 100),4) END as CompRate
FROM
tmpTab
In this SQL query, I need to group by RepMonthAndYear column also which I can not do as it is a calculated column. I get an error "invalid column".
And if I use this GROUP BY clause:
(DATENAME(DAY,#StartDate2)+' '+DATENAME(MONTH,#StartDate2)+' '+DATENAME(YEAR,#StartDate2)+ ' To ' + DATENAME(DAY,#EndDate2)+' '+DATENAME(MONTH,#EndDate2)+' '+DATENAME(YEAR,#EndDate2) )
I get this error:
Each GROUP BY expression must contain at least one column that is not an outer reference.
How can I solve this problem?
Is there any other way to know average rate group by particular periods?
Given the following Test Data and your SQL I get what you discibe.
CREATE TABLE tableA (
question VARCHAR(50),
AnswerValue INT,
startdate DATE,
endate DATE
);
INSERT INTO tableA VALUES ('Question A', 1, '2017-06-25', '2017-06-26');
INSERT INTO tableA VALUES ('Question A', 1, '2017-06-27', '2017-06-27');
INSERT INTO tableA VALUES ('Question A', 2, '2017-06-27', '2017-06-27');
INSERT INTO tableA VALUES ('Question B', 1, '2017-06-11', '2017-06-12');
INSERT INTO tableA VALUES ('Question B', 2, '2017-06-13', '2017-06-13');
INSERT INTO tableA VALUES ('Question B', 1, '2017-06-13', '2017-06-13');
INSERT INTO tableA VALUES ('Question C', 1, '2017-06-20', '2017-06-20');
INSERT INTO tableA VALUES ('Question C', 1, '2017-06-23', '2017-06-23');
INSERT INTO tableA VALUES ('Question D', 2, '2017-06-01', '2017-06-01');
INSERT INTO tableA VALUES ('Question E', 1, '2017-10-11', '2017-10-11');
INSERT INTO tableA VALUES ('Question E', 1, '2017-10-15', '2017-10-15');
INSERT INTO tableA VALUES ('Question F', 1, '2017-10-20', '2017-10-20');
INSERT INTO tableA VALUES ('Question F', 2, '2017-10-20', '2017-10-20');
INSERT INTO tableA VALUES ('Question F', 2, '2017-10-20', '2017-10-20');
INSERT INTO tableA VALUES ('Question G', 1, '2017-10-26', '2017-10-26');
INSERT INTO tableA VALUES ('Question H', 1, '2017-10-26', '2017-10-26');
INSERT INTO tableA VALUES ('Question H', 2, '2017-10-26', '2017-10-26');
INSERT INTO tableA VALUES ('Question I', 1, '2017-10-26', '2017-10-26');
Here the outcome of your Query:
Question A 2 0 3 66,6667
Question B 2 0 3 66,6667
Question C 2 0 2 100
Question D 0 0 1 0
Question E 2 0 2 100
Question F 1 0 3 33,3333
Question G 1 0 1 100
Question H 1 0 2 50
Question I 1 0 1 100
I could simplify your SQL and have removed some code duplicates, maybe that was your intended question.
DECLARE #StartDate DATE = '2017-06-01';
DECLARE #EndDate DATE = '2017-06-30';
DECLARE #StartDate2 DATE = '2017-10-01';
DECLARE #EndDate2 DATE = '2017-10-30';
WITH
periods AS (
SELECT #StartDate as startdate,
#EndDate as enddate
UNION
SELECT #StartDate2 as startdate,
#EndDate2 as enddate
)
SELECT t.Question, t.Met, t.NA, t.MetNotMet,
CASE WHEN (t.Met) = 0 THEN 0 ELSE ROUND(((CONVERT(FLOAT,(t.Met))/(t.MetNotMet))* 100),4) END as CompRate
FROM (SELECT question,
SUM((CASE WHEN AnswerValue = 1 THEN 1 ELSE 0 END)) Met,
SUM((CASE WHEN AnswerValue = 3 THEN 1 ELSE 0 END)) NA,
SUM((CASE WHEN (ISNULL(AnswerValue,3) <> 3) THEN 1 ELSE 0 END)) MetNotMet
FROM tableA AS a
JOIN ( SELECT p.startdate,
p.enddate,
DATENAME(DAY, p.startdate) + ' ' + DATENAME(MONTH, p.startdate) + ' ' +
DATENAME(YEAR, p.startdate) + ' To ' + DATENAME(DAY, p.enddate) + ' ' +
DATENAME(MONTH, p.enddate) + ' ' + DATENAME(YEAR, p.enddate) AS RepMonthAndYear
FROM periods p ) AS p ON a.startdate >= p.startdate AND a.endate <= p.enddate
GROUP BY a.Question, p.RepMonthAndYear) AS t

SQL for islands-and-gaps: islands can overlap

I have robots with certificates. There are two kinds of certificates. For each kind of certificate (identified by Certif_ID), for each robot I need the most recent certified date-span.
Update for clarity: Date-spans that do not overlap but are contiguous are treated as a single span. See the first two records in the sample table that is at the top of the code.
Date-spans may overlap! These must be treated as a single span. This is where I'm having a problem.
in SQL Server 2012, run this code as-is to see what's happening.
BEGIN -- #certif_span
IF OBJECT_ID('TEMPDB..#certif_span') IS NOT NULL DROP TABLE #certif_span;
CREATE TABLE #certif_span
( Robot_ID CHAR(3)
, Certif_ID SMALLINT
, d_Start SMALLDATETIME
, d_End SMALLDATETIME );
INSERT INTO #certif_span VALUES ('210', '1', '2000-01-01', '2001-02-02');
INSERT INTO #certif_span VALUES ('210', '1', '2001-02-03', '2001-12-31');
INSERT INTO #certif_span VALUES ('210', '1', '2000-01-01', '2000-12-31');
INSERT INTO #certif_span VALUES ('880', '1', '2001-01-01', '2001-12-31');
INSERT INTO #certif_span VALUES ('880', '1', '2002-02-02', '2003-02-01');
INSERT INTO #certif_span VALUES ('880', '1', '2003-01-01', '2004-12-31'); -- *
INSERT INTO #certif_span VALUES ('880', '7', '2010-05-05', '2011-05-04');
INSERT INTO #certif_span VALUES ('880', '7', '2011-05-05', '2012-02-10');
INSERT INTO #certif_span VALUES ('880', '7', '2013-03-03', '2013-04-04');
INSERT INTO #certif_span VALUES ('880', '7', '2013-04-01', '2013-05-05'); -- *
-- * This line has dates that overlap with the line above
END
SELECT Robot_ID
, Certif_ID
, d_Start = FORMAT(d_Start, 'yyyy-MM-dd')
, d_End = FORMAT(d_End, 'yyyy-MM-dd')
, commentary = 'Here is the raw data'
FROM #certif_span AS cs
ORDER BY Robot_ID
, Certif_ID
, d_End
IF OBJECT_ID('TEMPDB..#prac_date_span') IS NOT NULL DROP TABLE #prac_date_span;
SELECT DISTINCT
cs.Robot_ID
, cs.Certif_ID
, cs.d_Start
, cs.d_End
INTO
--DROP TABLE --SELECT * FROM
#prac_date_span
FROM
#certif_span AS cs
GROUP BY
cs.Robot_ID
, cs.Certif_ID
, cs.d_Start
, cs.d_End
ORDER BY 1, 2, 3;
BEGIN
IF OBJECT_ID('TEMPDB..#prac_date_span_grp') IS NOT NULL
DROP TABLE #prac_date_span_grp;
WITH cte as (
SELECT
a.Robot_ID, a.Certif_ID
, a.d_Start, a.d_End
FROM
#prac_date_span a
LEFT JOIN #prac_date_span b
ON a.Robot_ID = b.Robot_ID
AND b.Certif_ID = a.Certif_ID
AND a.d_Start - 1 = b.d_End
WHERE
b.Robot_ID IS NULL
UNION ALL -----------------------------
SELECT
a.Robot_ID, a.Certif_ID
, a.d_Start, b.d_End
FROM
cte a
JOIN
#prac_date_span b
ON a.Robot_ID = b.Robot_ID
AND b.Certif_ID = a.Certif_ID
AND b.d_Start - 1 = a.d_End
)
SELECT
Robot_ID
, Certif_ID
, d_Start
, d_End = MAX(d_End)
INTO
--drop table --select * from
#prac_date_span_grp
FROM cte
GROUP BY Robot_ID, Certif_ID, d_Start
ORDER BY Robot_ID, Certif_ID;
END
SELECT
Robot_ID
, Certif_ID
, d_Start = FORMAT(d_Start, 'yyyy-MM-dd')
, d_End = FORMAT(d_End, 'yyyy-MM-dd')
, commentary = 'Here is the grouped data (flawed)'
FROM #prac_date_span_grp
SELECT
Robot_ID
, Certif_ID
, d_Start = FORMAT(MAX(d_Start), 'yyyy-MM-dd')
, d_End = FORMAT(MAX(d_End), 'yyyy-MM-dd')
, commentary = 'Final result: Start date ' +
CASE FORMAT(MAX(d_Start), 'yyyy-MM-dd')
WHEN '2003-01-01' THEN 'should be 2002-02-02'
WHEN '2013-04-01' THEN 'should be 2013-03-03'
ELSE 'good' END
FROM #prac_date_span_grp
GROUP BY Robot_ID, Certif_ID
The final result should be:
Robot_ID Certif_ID d_Start d_End
210 1 2000-01-01 2001-12-31
880 1 2002-02-02 2004-12-31
880 7 2013-03-03 2013-05-05
I've been fiddling with the date comparisons. In this bit from the cte, the -1 looks like it allows for a one-day stagger in date-spans:
AND b.Certif_ID = a.Certif_ID
AND a.d_Start - 1 = b.d_End
...
AND b.Certif_ID = a.Certif_ID
AND b.d_Start - 1 = a.d_End
I feel certain this is the point that needs fixing. I've tried changing the date compare to >=. (This requires me to deal with max recursion.) The grouping changes, but is not correct.
This is not a simple task. I hope this will answer the problem.
Declare #certif_span TABLE(Robot_ID CHAR(3), Certif_ID SMALLINT, StartDate date, EndDate date);
INSERT INTO #certif_span VALUES ('210', '1', '2000-01-01', '2001-02-02');
INSERT INTO #certif_span VALUES ('210', '1', '2001-02-03', '2001-12-31');
INSERT INTO #certif_span VALUES ('210', '1', '2000-01-01', '2000-12-31');
INSERT INTO #certif_span VALUES ('880', '1', '2001-01-01', '2001-12-31');
INSERT INTO #certif_span VALUES ('880', '1', '2002-02-02', '2003-02-01');
INSERT INTO #certif_span VALUES ('880', '1', '2003-01-01', '2004-12-31'); -- *
INSERT INTO #certif_span VALUES ('880', '7', '2010-05-05', '2011-05-04');
INSERT INTO #certif_span VALUES ('880', '7', '2011-05-05', '2012-02-10');
INSERT INTO #certif_span VALUES ('880', '7', '2013-03-03', '2013-04-04');
INSERT INTO #certif_span VALUES ('880', '7', '2013-04-01', '2013-05-05'); -- *
;with Src as(
SELECT ROW_NUMBER() Over(Partition by Robot_ID, Certif_ID order by StartDate, EndDate) as RN
,a.*
FROM #certif_span as a
)
, Islands as(
SELECT RN, Robot_ID, Certif_ID, StartDate, EndDate, 0 as islandNo, EndDate AS MovingEnd
FROM Src as a WHERE a.RN=1
UNION ALL
SELECT a.RN, a.Robot_ID, a.Certif_ID, a.StartDate, a.EndDate
, b.islandNo + CASE WHEN DATEDIFF(d, a.StartDate, b.MovingEnd)>=-1 THEN 0 ELSE 1 END as IslandNO
, CASE WHEN a.EndDate>b.MovingEnd THEN a.EndDate ELSE b.MovingEnd END as MovingEnd
FROM Src as a
INNER JOIN Islands as b on a.Robot_ID=b.Robot_ID and a.Certif_ID=b.Certif_ID and a.RN=b.RN+1
) -- SELECT * FROM Islands order by Robot_ID, Certif_ID, IslandNo
, LastIsland as(
SELECT Robot_ID, Certif_ID, islandNo, MIN(StartDate) as startDate, MAX(EndDate) as EndDate
,ROW_NUMBER() over(partition by Robot_ID, Certif_ID order by IslandNO desc) as RN
FROM Islands
Group by Robot_ID, Certif_ID, islandNo
)
SELECT Robot_ID, Certif_ID, startDate, EndDate
FROM LastIsland
where RN=1
This was a head scratcher, because it is not your typical Gaps-and-Islands, so it dawned on me to create the gaps-and-islands off the date dimension first.
Now, I do have one additional island than perhaps you were expecting. But, not matter how I look at it, it seems to hold true.
I should also note that I use a TVF (table-valued user-defined function) to create dynamic date ranges. This logic could easily be ported into a preliminary cte. A tally/calendar table would to the trick as well.
The SQL
;with cte0 as(
Select A.*,GrpSeq=RetSeq-Row_Number() over (Order by RetSeq)
From (
Select Distinct RetSeq,RetVal
From [dbo].[udf-Range-Date]((Select min(d_Start) from #certif_span),(Select max(d_End) from #certif_span),'DD',1) A
Join #certif_span B on A.RetVal between B.d_Start and B.d_End
) A
)
, cte1 as(
Select d_Start = min(A.RetVal)
,d_End = max(A.RetVal)
From cte0 A
Group By GrpSeq
)
Select Robot_ID = min(Robot_ID)
,Certif_ID = min(Certif_ID)
,A.d_Start
,A.d_End
from cte1 A
Join #certif_span B on B.d_Start Between A.d_Start and A.d_End
Group By A.d_Start,A.d_End
Returns
Robot_ID Certif_ID d_Start d_End
210 1 2000-01-01 2001-12-31
880 1 2002-02-02 2004-12-31
880 7 2010-05-05 2012-02-10 << Extra Mentioned
880 7 2013-03-03 2013-05-05
The UDF if Needed
CREATE FUNCTION [dbo].[udf-Range-Date] (#R1 datetime,#R2 datetime,#Part varchar(10),#Incr int)
Returns Table
Return (
with cte0(M) As (Select 1+Case #Part When 'YY' then DateDiff(YY,#R1,#R2)/#Incr When 'QQ' then DateDiff(QQ,#R1,#R2)/#Incr When 'MM' then DateDiff(MM,#R1,#R2)/#Incr When 'WK' then DateDiff(WK,#R1,#R2)/#Incr When 'DD' then DateDiff(DD,#R1,#R2)/#Incr When 'HH' then DateDiff(HH,#R1,#R2)/#Incr When 'MI' then DateDiff(MI,#R1,#R2)/#Incr When 'SS' then DateDiff(SS,#R1,#R2)/#Incr End),
cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cte2(N) As (Select Top (Select M from cte0) Row_Number() over (Order By (Select NULL)) From cte1 a, cte1 b, cte1 c, cte1 d, cte1 e, cte1 f, cte1 g, cte1 h ),
cte3(N,D) As (Select 0,#R1 Union All Select N,Case #Part When 'YY' then DateAdd(YY, N*#Incr, #R1) When 'QQ' then DateAdd(QQ, N*#Incr, #R1) When 'MM' then DateAdd(MM, N*#Incr, #R1) When 'WK' then DateAdd(WK, N*#Incr, #R1) When 'DD' then DateAdd(DD, N*#Incr, #R1) When 'HH' then DateAdd(HH, N*#Incr, #R1) When 'MI' then DateAdd(MI, N*#Incr, #R1) When 'SS' then DateAdd(SS, N*#Incr, #R1) End From cte2 )
Select RetSeq = N+1
,RetVal = D
From cte3,cte0
Where D<=#R2
)
/*
Max 100 million observations -- Date Parts YY QQ MM WK DD HH MI SS
Syntax:
Select * from [dbo].[udf-Range-Date]('2016-10-01','2020-10-01','YY',1)
Select * from [dbo].[udf-Range-Date]('2016-01-01','2017-01-01','MM',1)
*/

CROSS Join is not working in SQL Server

see the output which i am trying to generate by SQL with dummy data
this is my sql which is throwing error Incorrect syntax near the keyword 'GROUP'.
DECLARE #t TABLE(
CallStar datetime,
Direction char(1),
PartyName varchar(100))
INSERT INTO #t VALUES
('20150609 08:02:51','I','VM Channel 1'),
('20150609 08:04:14','I','VM Channel 1'),
('20150609 08:35:51','O','VM Channel 1'),
('20150609 08:40:14','O','VM Channel 1'),
('20150609 08:02:51','I','VM Channel 2'),
('20150609 08:04:14','I','VM Channel 2'),
('20150609 08:35:51','O','VM Channel 2'),
('20150609 08:40:14','O','VM Channel 2'),
('20150609 08:04:14','O','ACC'),
('20150609 08:04:14','I','ACC'),
('20150609 08:04:14','I','ACC')
DECLARE #StartTime datetime = '2015-06-09 09:00:00',
#EndTime datetime = '2015-06-09 18:00:00',
#Interval int = 30 -- this can be changed.
;WITH cSequence AS
(
SELECT
#StartTime AS StartRange,
DATEADD(MINUTE, #Interval, #StartTime) AS EndRange
UNION ALL
SELECT
EndRange,
DATEADD(MINUTE, #Interval, EndRange)
FROM cSequence
WHERE DATEADD(MINUTE, #Interval, EndRange) < #EndTime
)
SELECT
PartyName,
DATEADD(minute, DATEDIFF(minute,0,CallStar) / 30 * 30, 0) AS "DateStart",
DATEADD(minute, 30, DATEADD(minute, DATEDIFF(minute, 0, CallStar) / 30 * 30, 0)) AS "DateEnd",
SUM(CASE WHEN Direction = 'I' THEN 1 ELSE 0 END) AS "Incoming",
SUM(CASE WHEN Direction = 'O' THEN 1 ELSE 0 END) AS "Outgoing",
SUM(CASE WHEN Direction = 'T' THEN 1 ELSE 0 END) AS "Transfer"
FROM #t CROSS JOIN (SELECT * FROM cSequence) s
GROUP BY
PartyName,
DATEADD(minute, DATEDIFF(minute,0,CallStar) / 30 * 30, 0)
ORDER BY
PartyName,
DATEADD(minute, DATEDIFF(minute,0,CallStar) / 30 * 30, 0)
where i am making the mistake for which cross join is not applying?
EDIT 1
For Incoming data calculation
where direction='I' and
Is_Internal=0 and continuation=0 and
RIGHT(convert(varchar,[call duration]),8)<> '00:00:00'
For outgoing data calculation
where direction='O'
and Is_Internal=0 and continuation=0
For outgoing data calculation
where continuation=1
and RIGHT(convert(varchar,[call duration]),8)<> '00:00:00'
For misscall data calculation
where direction='I' and
RIGHT(convert(varchar,[call duration]),8)= '00:00:00' and [Ring duration]>0
just see and tell me can i add more clause here as below
SUM(CASE WHEN (Direction = 'I' AND Is_Internal=0 AND continuation=0 AND
RIGHT(convert(varchar,[call duration]),8)<> '00:00:00') THEN 1 ELSE 0 END) AS "Incoming",
SUM(CASE WHEN (Direction = 'O' AND Is_Internal=0 AND continuation=0
) THEN 1 ELSE 0 END) AS "Outgoing"
EDIT 2
DECLARE #t TABLE(CallStar datetime, Direction char(1), PartyName varchar(100))
INSERT INTO #t VALUES
('2015-06-09 08:02:51','I','VM Channel 1'),
('2015-06-09 08:04:14','I','VM Channel 1'),
('2015-06-09 08:02:51','O','VM Channel 1'),
('2015-06-09 08:02:51','I','VM Channel 1'),
('2015-06-09 08:34:14','I','VM Channel 1'),
('2015-06-09 18:02:51','I','VM Channel 1'),
('2015-06-09 18:04:14','I','VM Channel 1'),
('2015-06-09 18:02:51','O','VM Channel 1'),
('2015-06-09 18:02:51','T','VM Channel 1'),
('2015-06-09 01:02:51','I','VM Channel 2'),
('2015-06-09 02:04:14','I','VM Channel 2'),
('2015-06-09 03:02:51','O','VM Channel 2'),
('2015-06-09 04:02:51','I','VM Channel 2'),
('2015-06-09 05:34:14','I','VM Channel 2'),
('2015-06-09 16:02:51','I','VM Channel 2'),
('2015-06-09 17:04:14','I','VM Channel 2'),
('2015-06-09 18:02:51','O','VM Channel 2'),
('2015-06-09 19:02:51','T','VM Channel 2')
;WITH times AS (
SELECT CAST('1900-01-01 00:00:00.000' AS DATETIME) AS time, 0 AS sortSeq
UNION ALL
SELECT DATEADD(MINUTE,30,time) AS time, sortSeq + 1 AS sortSeq
FROM times
WHERE CAST(DATEADD(MINUTE,30,time) AS DATE) = CAST(time AS DATE)
),
final AS (
SELECT PartyName, DateStar, LEFT(CONVERT(VARCHAR,DATEADD(minute, DATEDIFF(minute,0,time) / 30 * 30, 0),108),5)
+ ' - ' + LEFT(CONVERT(VARCHAR,DATEADD(minute, 30, DATEADD(minute, DATEDIFF(minute, 0, time) / 30 * 30, 0)),108),5) AS groupName, sortSeq
FROM times
CROSS APPLY (SELECT DISTINCT PartyName, CAST(callStar AS DATE) AS dateStar
FROM #t
) a
)
SELECT f.PartyName, f.DateStar, f.GroupName, COALESCE(t.Incoming,0) AS Incoming, COALESCE(t.Outgoing,0) AS Outgoing, COALESCE(t.Transfer,0) AS Transfer
FROM final f
LEFT OUTER JOIN (
SELECT PartyName, CAST(CallStar AS DATE) AS DateStar,
LEFT(CONVERT(VARCHAR,DATEADD(minute, DATEDIFF(minute,0,CallStar) / 30 * 30, 0),108),5)
+ ' - ' + LEFT(CONVERT(VARCHAR,DATEADD(minute, 30, DATEADD(minute, DATEDIFF(minute, 0, CallStar) / 30 * 30, 0)),108),5) AS groupName,
DATEADD(minute, DATEDIFF(minute,0,CallStar) / 30 * 30, 0) AS DateStart,
DATEADD(minute, 30, DATEADD(minute, DATEDIFF(minute, 0, CallStar) / 30 * 30, 0)) AS DateEnd,
SUM(CASE WHEN Direction = 'I' THEN 1 ELSE 0 END) AS Incoming,
SUM(CASE WHEN Direction = 'O' THEN 1 ELSE 0 END) AS Outgoing,
SUM(CASE WHEN Direction = 'T' THEN 1 ELSE 0 END) AS Transfer
FROM #t t
GROUP BY PartyName, CAST(CallStar AS DATE), DATEADD(minute, DATEDIFF(minute,0,CallStar) / 30 * 30, 0)
) t
ON f.PartyName = t.PartyName
AND f.groupName = t.groupName
AND f.dateStar = t.dateStar
ORDER BY f.PartyName, f.DateStar, f.sortSeq
If you want all time slots for all parties you need to do a cross join on the ranges and parties, and then use a left join to fill in the actual data.
I think this is what you want:
with cSequence as (
....
)
SELECT
s.StartRange, s.EndRange,
s.PartyName,
SUM(CASE WHEN Direction = 'I' THEN 1 ELSE 0 END) AS "Incoming",
SUM(CASE WHEN Direction = 'O' THEN 1 ELSE 0 END) AS "Outgoing",
SUM(CASE WHEN Direction = 'T' THEN 1 ELSE 0 END) AS "Transfer"
-- this is what changed
FROM (SELECT * FROM cSequence CROSS JOIN (SELECT DISTINCT PARTYNAME FROM #T) p) S
LEFT JOIN #t t ON s.StartRange = DATEADD(minute, DATEDIFF(minute,0,CallStar) / 30 * 30, 0)
AND s.EndRange = DATEADD(minute, 30, DATEADD(minute, DATEDIFF(minute, 0, CallStar) / 30 * 30, 0))
AND t.PartyName = s.PartyName
GROUP BY
s.PartyName,
s.StartRange,s.EndRange,
DATEADD(minute, DATEDIFF(minute,0,CallStar) / 30 * 30, 0)
ORDER BY
s.PartyName,
s.StartRange,s.EndRange,
DATEADD(minute, DATEDIFF(minute,0,CallStar) / 30 * 30, 0)
;
Sample SQL Fiddle
I left out the temp table and cte and only included the last select.
The query can most likely be improved a bit, but I'll leave that to you.
When you have two or more tables with the same structure
you could select the data this way:
Select * from
(
Select Col_1,Col_2,Col_3 from Table_1 WHERE xxxx
UNION
Select Col_1,Col_2,Col_3 from Table_2 WHERE xxxx
UNION
Select Col_1,Col_2,Col_3 from Table_3 WHERE xxxx
) as NewTable
if you hate JOIN syntax
Below is the code I ended up using. It's based on the SQL from jpw's answer (slightly adapted, as jpw's SQL did not return the data properly).
DECLARE #StartTime DATETIME, #EndTime DATETIME
SELECT #StartTime = '09:00:00'
SELECT #EndTime = '17:30:00'
;WITH times AS (
SELECT CAST(#StartTime AS DATETIME) AS time, 0 AS sortSeq
UNION ALL
SELECT DATEADD(MINUTE,30,time) AS time, sortSeq + 1 AS sortSeq
FROM times
WHERE time < #EndTime
)
, final AS (
SELECT Party1Name, DateStar,
LEFT(CONVERT(VARCHAR,DATEADD(minute, DATEDIFF(minute,0,time) / 30 * 30, 0),108),5) + ' - '
+ LEFT(CONVERT(VARCHAR,DATEADD(minute, 30, DATEADD(minute, DATEDIFF(minute, 0, time) / 30 * 30, 0)),108),5) AS groupName,
sortSeq FROM times
CROSS APPLY (SELECT DISTINCT Party1Name, CAST([call Start] AS DATE) AS dateStar
FROM CSRPhoneData_Interval
) a
)
SELECT f.Party1Name as [CSR Name], f.GroupName as Time, COALESCE(t.Incoming,0) AS Incoming,
COALESCE(t.Outgoing,0) AS OutGoing, COALESCE(t.Transfer,0) AS [Call Transfer],COALESCE(t.misscall,0) AS MissCall
FROM final f
LEFT OUTER JOIN (
SELECT Party1Name, CAST([call Start] AS DATE) AS DateStar,
LEFT(CONVERT(VARCHAR,DATEADD(minute, DATEDIFF(minute,0,[call Start]) / 30 * 30, 0),108),5) + ' - '
+ LEFT(CONVERT(VARCHAR,DATEADD(minute, 30, DATEADD(minute, DATEDIFF(minute, 0, [call Start]) / 30 * 30, 0)),108),5) AS groupName,
DATEADD(minute, DATEDIFF(minute,0,[call Start]) / 30 * 30, 0) AS DateStart,
DATEADD(minute, 30, DATEADD(minute, DATEDIFF(minute, 0, [call Start]) / 30 * 30, 0)) AS DateEnd,
SUM(CASE WHEN (Direction = 'I'
AND Is_Internal=0
AND continuation=0
AND RIGHT(convert(varchar,[call duration]),8)<> '00:00:00') THEN 1 ELSE 0 END) AS Incoming,
SUM(CASE WHEN (Direction = 'O' AND Is_Internal=0 and continuation=0) THEN 1 ELSE 0 END) AS Outgoing,
SUM(CASE WHEN (Direction = 'T'
AND continuation=1
AND RIGHT(convert(varchar,[call duration]),8)<> '00:00:00') THEN 1 ELSE 0 END) AS Transfer,
SUM(CASE WHEN (Direction = 'I'
AND RIGHT(convert(varchar,[call duration]),8)= '00:00:00'
AND [Ring duration]>0) THEN 1 ELSE 0 END) AS misscall
FROM CSRPhoneData_Interval t
GROUP BY Party1Name, CAST([call Start] AS DATE), DATEADD(minute, DATEDIFF(minute,0,[call Start]) / 30 * 30, 0)
) t
ON f.Party1Name = t.Party1Name
AND f.groupName = t.groupName
AND f.dateStar = t.dateStar
ORDER BY f.Party1Name, f.DateStar, f.sortSeq

SQL Server : list by Nearest Birthday

Table A - column B [DateTime]
Need list users in that table, ordering at nearest birthday. Users who have already made birthday should be at the end of the list considering them for the following year.
select B from A (order by/where)?
Table Example
USER DATE
MARCELO 1988-04-11
RICARDO 1965-12-30
WILSON 1977-02-20
PABLO 1985-01-10
JOHN NULL
Expected Result
WILSON 20/02 (Month/Day)
MARCELO 11/04
RICARDO 30/12
PABLO 10/01
(JOHN NOT IN THE LIST)
Test Data
DECLARE #TABLE TABLE(Name VARCHAR(100),Dob DATETIME)
INSERT INTO #TABLE VALUES
('Mark', '19961017'),('Josh', '19801119'),('Sam', '19700709'),
('Vicky', '19500210'),('Dom', '19890308'),('Paul', '19840401')
,('Nick', NULL)
Query
SELECT Name, CAST(MONTH(Dob) AS NVARCHAR(2))
+ '/' + CAST(DAY(Dob) AS NVARCHAR(2)) [Dob Month/Day]
FROM #TABLE
WHERE DATEPART(DAYOFYEAR,Dob) - DATEPART(DAYOFYEAR,GETDATE()) > 0
ORDER BY ABS(DATEPART(DAYOFYEAR,Dob) - DATEPART(DAYOFYEAR,GETDATE()))
Result Set
User vicky will be filtered out as birthday is gone.
╔══════╦═══════════════╗
║ Name ║ Dob Month/Day ║
╠══════╬═══════════════╣
║ Dom ║ 3/8 ║
║ Paul ║ 4/1 ║
║ Sam ║ 7/9 ║
║ Mark ║ 10/17 ║
║ Josh ║ 11/19 ║
╚══════╩═══════════════╝
Result set is ordered by who's birthday is next
select name, birthdate
from yourtable
where birthdate is not null
order by datepart(dy,dateadd(d,- DATEPART(dy, getdate()),birthdate ))
This solution:
"wraps around" for a full year.
and also allows a "how many months to project out" filter.
IF OBJECT_ID('tempdb..#Employee') IS NOT NULL
begin
drop table #Employee
end
CREATE TABLE #Employee
(
SurrogateKeyIDENTITY int not null IDENTITY (1,1) ,
NameOf varchar(12) not null ,
BirthDate datetime not null
)
Insert into #Employee (NameOf, BirthDate)
Select 'A', '01/01/1999'
UNION ALL Select 'B', '01/16/1941'
UNION ALL Select 'C', '01/29/1965'
UNION ALL Select 'D', '02/13/1944'
UNION ALL Select 'P', '02/14/1978'
UNION ALL Select 'Q', '02/15/1984'
UNION ALL Select 'R', '03/13/1948'
UNION ALL Select 'S', '04/16/1983'
UNION ALL Select 'T', '05/17/1953'
UNION ALL Select 'U', '07/19/1959'
UNION ALL Select 'V', '08/16/1959'
UNION ALL Select 'W', '09/1/1959'
UNION ALL Select 'X', '10/30/1959'
UNION ALL Select 'Y', '11/16/1959'
UNION ALL Select 'Z', '12/31/1972'
Declare #MyCURRENT_TIMESTAMP datetime
/* select #MyCURRENT_TIMESTAMP = CURRENT_TIMESTAMP */
select #MyCURRENT_TIMESTAMP = '02/14/2014'
Declare #NumberOfMonthsToGoOut int
select #NumberOfMonthsToGoOut = 12
;WITH myCalculationsCTE (NameOf, BirthDate, [NoYearBirthDate] , [NoYearCurrentDate] )
AS
(
SELECT NameOf, BirthDate
, DATEFROMPARTS( 1900 , MONTH(BirthDate), DAY (BirthDate)) [NoYearBirthDate]
, DATEFROMPARTS( 1900 , MONTH(#MyCURRENT_TIMESTAMP), DAY (#MyCURRENT_TIMESTAMP)) [NoYearCurrentDate]
FROM #Employee
WHERE BirthDate IS NOT NULL
)
,
myCTE (NameOf, BirthDate, [NoYearBirthDate] , [NoYearCurrentDate] , DerivedMonthDiff)
AS
(
SELECT NameOf, BirthDate
, [NoYearBirthDate]
, [NoYearCurrentDate]
, [DerivedMonthDiff] = CASE
WHEN [NoYearBirthDate] >= [NoYearCurrentDate]
then DATEDIFF(m , [NoYearCurrentDate], [NoYearBirthDate] )
else
DATEDIFF(m , [NoYearCurrentDate], [NoYearBirthDate]) + 12 end
FROM myCalculationsCTE
)
SELECT NameOf, BirthDate , [NoYearBirthDate] , [NoYearCurrentDate]
, DerivedMonthDiff
, MONTH([NoYearBirthDate]) [Month]
, DAY ([NoYearBirthDate]) [DayOf]
FROM myCTE
where [DerivedMonthDiff] <= #NumberOfMonthsToGoOut
order by DerivedMonthDiff , MONTH([NoYearBirthDate]) , DAY ([NoYearBirthDate])
IF OBJECT_ID('tempdb..#Employee') IS NOT NULL
begin
drop table #Employee
end
SELECT Name, CAST(MONTH(BIRTHDAY) AS NVARCHAR(2))
+ '/' + CAST(DAY(BIRTHDAY) AS NVARCHAR(2)) [Dob Month/Day],DATEPART(DAYOFYEAR,BIRTHDAY) as [dayofyear],
case when (DATEPART(DAYOFYEAR,BIRTHDAY) - DATEPART(DAYOFYEAR,GETDATE()) > 0)
THEN
DATEPART(DAYOFYEAR,BIRTHDAY) - DATEPART(DAYOFYEAR,GETDATE())
ELSE
DATEPART(DAYOFYEAR,BIRTHDAY) + 365-DATEPART(DAYOFYEAR,GETDATE())
END
AS [DAYSTILLBURTHDAY]
FROM CONTACTS
WHERE BIRTHDAY <> '9999-01-01 00:00:00.000'
ORDER BY (case when (DATEPART(DAYOFYEAR,BIRTHDAY) - DATEPART(DAYOFYEAR,GETDATE()) > 0)
THEN
DATEPART(DAYOFYEAR,BIRTHDAY) - DATEPART(DAYOFYEAR,GETDATE())
ELSE
DATEPART(DAYOFYEAR,BIRTHDAY) + 365-DATEPART(DAYOFYEAR,GETDATE())
END)