PIVOT Attendance - sql

I have an attendance table with the result as shown below
I need to get the result in below format:
Below is sample PIVOT query I used. Please help me with a query to get the desired result.
CREATE TABLE #Temp
(
[empcode] varchar(15),
[empDesc] nvarchar(300),
[hrlog_Date] date,
[hrlog_in] datetime,
[hrlog_Out] datetime,
[hrlog_Latein] datetime,
[hrlog_EarlyOut] datetime,
)
DECLARE #Start datetime,#End datetime,#DList varchar(2000),#Sql varchar(max),#Sql1 varchar(max);
SET #Start=#StartDate;
SET #End=#EndDate;
INSERT INTO #Temp
Select h.emp_code,e.emp_desc as emp_Desc,h.hrlog_Date, CONVERT(datetime,h.hrlog_in, 105) as hrlog_in
,CONVERT(datetime,h.hrlog_out,105) as hrlog_out
,
case when cast(h.hrlog_in as time)> cast('8:45' as time) then
cast(h.hrlog_Date as NVARCHAR(50))+' ' +CAST(DATEDIFF(second, cast('8:45' as time), cast(h.hrlog_in as time) ) / 60 / 60 % 24 AS NVARCHAR(50))+ ':' +
CAST(DATEDIFF(second,cast('8:45' as time), cast(h.hrlog_in as time) ) / 60 % 60 AS NVARCHAR(50))
else cast(h.hrlog_Date as nvarchar(50)) +' 0:00' end
mrngLate
,case when cast(h.hrlog_out as time) < cast('17:15' as time) then
cast(h.hrlog_Date as NVARCHAR(50))+' ' +CAST(DATEDIFF(second, cast(h.hrlog_out as time), cast('17:15' as time) ) / 60 / 60 % 24 AS NVARCHAR(50))+ ':' +
CAST(DATEDIFF(second, cast(h.hrlog_out as time),cast('17:15' as time) ) / 60 % 60 AS NVARCHAR(50))
else cast(h.hrlog_Date as nvarchar(50)) +' 0:00' end EvenEarly
from HRDailyLog h INNER JOIN FTEmployee e ON h.emp_code=e.Emp_Code
LEFT OUTER JOIN sys_CostCenterDep c ON e.sys_ccdepcode = c.sys_ccDepCode
LEFT OUTER JOIN FTJobDesc j ON e.job_code = j.Job_code
LEFT OUTER JOIN sysDep d ON j.sys_depcode = d.Sys_Depcode
LEFT OUTER JOIN HrShift s ON e.SFT_Code = s.SFT_Code
Where (h.hrlog_date between #Start and #End and e.SFT_Code is not null)
and CASE WHEN #sftCode = '-1' then #sftCode else e.SFT_Code end= #sftCode
and CASE WHEN #depCode = '-1' then #depCode else d.Sys_Depcode end= #depCode
and h.sys_bRcode = #brCode
SELECT DATEADD(dd,number,#Start) AS Date INTO #Date
FROM master..spt_values
WHERE type='p'
AND DATEADD(dd,number,#Start)<=#End
SELECT #DList=LEFT(dl.Dates,LEN(dl.Dates)-1)
FROM
(SELECT CAST(Date as varchar(11)) + ','
FROM #Date
FOR XML PATH('')
)dl(Dates)
SET #Sql='SELECT * FROM (
SELECT d.Date as dateIn ,t.empcode as empcode,t.empDesc as empDesc,(CONVERT(nvarchar(5),t.hrlog_in, 108)+'' to ''+CONVERT(nvarchar(5),t.hrlog_Out, 108)) as hrlog_in
FROM #Date d
LEFT JOIN #Temp t
ON CONVERT(nvarchar(10), t.hrlog_in, 111)=d.Date
)tm
PIVOT (MIN(hrlog_in) FOR dateIn IN ([' + REPLACE(#DList,',','],[')+ ']))p
WHERE empcode IS NOT NULL'
EXEC(#Sql)

Related

How to write a loop for such sql query

I need to write such query:
SELECT CAST (date_time_column AS DATE) as 'Date',
AVG(CASE WHEN FORMAT(date_time_column, 'HH:mm') = '00:00' then my_values ELSE NULL end) as '00:00',
........
AVG(CASE WHEN FORMAT(date_time_column, 'HH:mm') = '23:59' then my_values ELSE NULL end) as '23:59'
FROM table
where date_time_column > '2021-08-12'
GROUP BY CAST (date_time_column AS DATE)
What is a way to avoid writing 1440 lines in a query?
Try the below method (Change the variables to match your table and field)
/*Generate all minutes in a day in a string variable*/
Declare #timeRange varchar(max)=null
declare #startdate Datetime='2021-08-12 00:00';
; WITH cte AS
(
SELECT 1 i, #startdate AS resultDate
UNION ALL
SELECT i + 1, DATEADD(minute, i, #startdate )
FROM cte
WHERE DATEADD(minute, i, #startdate ) < DateAdd(day,1,#startdate)
)
SELECT #timeRange=Coalesce(#timeRange +',' + '['+Format(resultDate,'HH:mm')+']','['+Format(resultDate,'HH:mm')+']') FROM cte
OPTION (MAXRECURSION 2000);
/* (Change These variables to match your table & fields */
declare #filterQuery varchar(300)=' where {date_time_column}>''2021-01-01''';
declare #dateTimeColumn varchar(30)='{date_time_column}';
declare #valueColumn varchar(30)='{value_column}';
declare #myTable varchar(20)='{my_table}';
/*Generate Pivot Query */
DECLARE #query AS NVARCHAR(MAX);
set #query= '
SELECT *
FROM (SELECT Cast('+ #dateTimeColumn +' as Date) Resultdate,
       FORMAT(' + #dateTimeColumn + ', ''HH:mm'') DateMinute,
       ' + #valueColumn + ' FROM ' + #myTable + ' ' + #filterQuery +'
     ) FormattedData
PIVOT( AVG('+ #valueColumn + ')  
    FOR DateMinute IN ('+ #timeRange +')
) AS pivotTable
';
/*Execute Generated Query*/
execute(#query)
What you want to do is to return your data in rows, not columns. That is a row for every minute rather than a column for every minute.
Try it something like this:
WITH
cteNums AS
(
Select TOP (1440) ROW_NUMBER() OVER(order by (Select Null)) - 1 as num
From sys.all_columns
Order By num
)
, cteMinutes AS
(
Select FORMAT(CAST(num/cast(1440.0 as real) as DATETIME), N'HH:mm') as Minutes
From cteNums
)
select CAST(t.date_time_column AS DATE) as 'Date',
m.Minutes,
AVG(CASE WHEN FORMAT(t.date_time_column, 'HH:mm') = m.Minute then t.my_values ELSE NULL end) as AvgValues
FROM cteMinutes m
LEFT JOIN [table] t ON m.Minutes = FORMAT(t.date_time_column, 'HH:mm')
where t.date_time_column > '2021-08-12'
GROUP BY CAST(t.date_time_column AS DATE), m.Minutes
ORDER BY CAST(t.date_time_column AS DATE), m.Minutes
;
I cannot, of course, test this as no test data or table definitions were provided.

Need monthly report where I need summation of Qty for each day

I have one table STOCK_REGISTER where I have columns Id, ItmId, Qty, CreatedDate. And there are daily transaction data like item inward, outward and all.
Now I am developing one Monthly Report where I want to display Day wise summation of item qty for whole month. I have written following query to do so.
SELECT CONVERT(Date,CreatedDate) AS CreatedDate,ItmId, SUM(Qty)
FROM STOCK_REGISTER
GROUP BY CONVERT(Date,CreatedDate),ItmId
This query returns correct data but if I am asking for April-2017 data and STOCK_REGISTER don't have any record on 5th April then it's not displaying 5th April at all where I need 0 value in qty for that item in 5th April.
Can you help me out to get this type of data?
Edit :
I have created query which gives all days of any particular month and have applied left join with STOCK_REGISTER, but still not solving my issue.
declare #month int, #year int
set #month = 4
set #year = 2017
SELECT CAST(CAST(#year AS VARCHAR) + '-' + CAST(#Month AS VARCHAR) + '-01' AS DATETIME) + A.Number AS STKFG_CREATED_DATE,
B.STKFG_ITM_ID,
0 AS OPENING_STOCK,
SUM(STKFG_QTY)
FROM master..spt_values A
LEFT JOIN STOCK_REGISTER_FG B
ON CONVERT(DATE,CAST(CAST(#year AS VARCHAR) + '-' + CAST(#Month AS VARCHAR) + '-01' AS DATETIME) + A.Number) = CONVERT(DATE,B.STKFG_CREATED_DATE)
AND C.ITM_ID = B.STKFG_ITM_ID
WHERE type = 'P'
AND (CAST(CAST(#year AS VARCHAR) + '-' + CAST(#Month AS VARCHAR) + '-01' AS DATETIME) + A.Number ) <
DATEADD(mm,1,CAST(CAST(#year AS VARCHAR) + '-' + CAST(#Month AS VARCHAR) + '-01' AS DATETIME) )
AND STKFG_TRAT_ID = 6
GROUP BY A.Number, B.STKFG_ITM_ID
ORDER BY B.STKFG_ITM_ID,CAST(CAST(#year AS VARCHAR) + '-' + CAST(#Month AS VARCHAR) + '-01' AS DATETIME) + A.Number
Thank you for your comments.
I have found one solution which works fine for me.
Here #TEMP_TABLE contains all dates.
SELECT A.CREATED_DATE,B.ITM_NAME,ISNULL(C.STKFG_QTY,0)
FROM #TEMP_TABLE A
CROSS APPLY ITEM_MASTER B
LEFT JOIN ( SELECT CONVERT(DATE,STKFG_CREATED_DATE) AS STKFG_CREATED_DATE, STKFG_ITM_ID, SUM(STKFG_QTY) AS STKFG_QTY
FROM STOCK_REGISTER_FG GROUP BY CONVERT(DATE,STKFG_CREATED_DATE),STKFG_ITM_ID) C
ON CONVERT(DATE,A.CREATED_DATE) = CONVERT(DATE,C.STKFG_CREATED_DATE)
AND B.ITM_ID = C.STKFG_ITM_ID
ORDER BY B.ITM_NAME,A.CREATED_DATE
Try below script. Here #tblData is your actual table name.
declare #tblData table
(id int,datecolumn date)
INSERT INTO #tblData
(id,datecolumn)
SELECT 1,'2010-01-01' UNION ALL
SELECT 3,'2010-01-01' UNION ALL
SELECT 1,'2010-01-03' UNION ALL
SELECT 2,'2010-01-04' UNION ALL
SELECT 1,'2010-01-05' UNION ALL
SELECT 3,'2010-01-06' UNION ALL
SELECT 1,'2010-01-10' UNION ALL
SELECT 1,'2010-01-10' UNION ALL
SELECT 2,'2010-01-11' UNION ALL
SELECT 1,'2010-01-11' UNION ALL
SELECT 2,'2010-01-11' UNION ALL
SELECT 1,'2010-01-12' UNION ALL
SELECT 3,'2010-01-13'
DECLARE #MaxDate DATE,
#MinDate DATE,
#iDate DATE
DECLARE #DateSequence TABLE(
DATE1 DATE
)
SELECT #MaxDate = Convert(DATE,Max(datecolumn)),
#MinDate = Convert(DATE,Min(datecolumn))
FROM #tblData -- Put your month condition
SET #iDate = #MinDate
WHILE (#iDate <= #MaxDate)
BEGIN
INSERT #DateSequence
SELECT #iDate
SET #iDate = Convert(DATE,Dateadd(DAY,1,#iDate))
END
SELECT a.DATE1 ,isnull(sum(b.id),0) as total
FROM #DateSequence a left join #tblData b on a.DATE1=b.datecolumn
group by a.DATE1
Thanks,

How to sum set of period times in specific format

If I have result set like that :
Work_hour(hh:mm)
10:24
12:59
06:28
where Work_hour is of type varchar
How to sum those hours and minutes with the same format ?
SELECT CAST(FLOOR(TMP1.MINS/60) AS VARCHAR) + ':' + CAST((TMP1.MINS % 60) AS VARCHAR) FROM (
SELECT SUM (CAST(LEFT(time_column, 2) AS INT) * 60 + CAST(RIGHT(time_column, 2) AS INT)) as MINS FROM table1
) AS TMP1
Where time_column is the column in the table and table1 is the name of the table. Example:
create table table1 (
time_column varchar(10)
);
insert into table1 (time_column) values ('12:20'), ('10:40'), ('15:50');
Results in: 38:50
Try this one -
Query:
DECLARE #temp TABLE
(
work_hour CHAR(5)
)
INSERT INTO #temp (work_hour)
VALUES
('10:24'),
('12:59'),
('06:28')
;WITH cte AS
(
SELECT mn = SUM(DATEDIFF(MINUTE, '19000101', CAST('19000101 ' + work_hour AS DATETIME)))
FROM #temp
)
SELECT CAST(FLOOR(mn / 60) AS VARCHAR(5)) + ':' + CAST(mn % 60 AS VARCHAR(2))
FROM cte
Output:
hm
--------
29:51
Update 2:
DECLARE #temp TABLE
(
transtime_out DATETIME
, transtime_in DATETIME
)
INSERT INTO #temp (transtime_out, transtime_in)
VALUES
('2013-05-19 16:40:53.000', '2013-05-19 08:58:07.000'),
('2013-05-19 16:40:53.000', '2013-05-19 08:58:07.000')
SELECT diff = LEFT(CONVERT(VARCHAR(10), CAST(SUM(CAST(a.transtime_out - a.transtime_in AS FLOAT)) AS DATETIME), 108), 5)
FROM #temp a

sql : calculate the total birthday moth based on month

I want to code a query to calculate the total birthday moth based on month . Here is the sample data . Two person's bithday are April, 1980 .
How to write this query ?
John 02/21/1980
Peter 02/22/1980
Lucy 04/21/1980
------ result -----
01/1980 0
02/1980 2
03/1980 0
04/1980 1
05/1980 0
.....
You can loop for every months like this:
DECLARE #month INT
DECLARE #year INT
DECLARE #result TABLE (MonthYear varchar(7),BirthdaysCount INT)
SET #month = 1
SET #year = 1980
WHILE(#month < 13)
BEGIN
INSERT INTO #result
SELECT (CAST(#month as VARCHAR) + '/' + CAST(#year as VARCHAR)),
COUNT(*)
FROM test
WHERE MONTH(birth) = #month AND YEAR(birth) = #year
SET #month = #month + 1
END
select * from #result
See the fiddle: http://sqlfiddle.com/#!3/20692/2
In MySQL you would do this:
select concat(month(dob), '/', year(dob)) monthYear, count(*) cnt from t
group by monthYear
order by year(dob), month(dob)
However, in order to get the "missing dates" you'll have to generate data, because 01/1980 is not in any table, as far as I can see. Check this answer to see how.
how about this for sql-server
create table #temp
(
name varchar(50),
DOB datetime
)
insert into #temp values ('john', '2/21/1980')
insert into #temp values ('peter', '2/22/1980')
insert into #temp values ('lucy', '4/21/1980')
select convert(varchar(2), MONTH(DOB)) + '/' + Convert(varchar(4), YEAR(DOB)) as [MM/YYYY]
, count(*) as TotalCount
from #temp
group by convert(varchar(2), MONTH(DOB)) + '/' + Convert(varchar(4), YEAR(DOB))
drop table #temp
EDIT - This will get you all records for the dates included in the dates in the table. It will use the Min/Max Date in the table to get the date range, you then use this range to get the count of the birthdays in each month:
create table #temp
(
name varchar(50),
DOB datetime
)
insert into #temp values ('john', '2/21/1980')
insert into #temp values ('peter', '2/22/1980')
insert into #temp values ('lucy', '4/21/1980')
;with cte as
(
select min(DOB) as MinDate, max(DOB) as MaxDate
from #temp
union all
SELECT dateadd(mm, 1, t.MinDate), t.MaxDate
from cte t
where dateadd(mm, 1, t.MinDate) <= t.MaxDate
)
select convert(varchar(2), MONTH(c.MinDate)) + '/' + Convert(varchar(4), YEAR(c.MinDate))
, IsNull(t.TotalCount, 0) as TotalCount
from cte c
LEFT JOIN
(
SELECT convert(varchar(2), MONTH(DOB)) + '/' + Convert(varchar(4), YEAR(DOB)) as [MM/YYYY]
, count(*) as TotalCount
FROM #temp
group by convert(varchar(2), MONTH(DOB)) + '/' + Convert(varchar(4), YEAR(DOB))
) t
on convert(varchar(2), MONTH(C.MinDate)) + '/' + Convert(varchar(4), YEAR(C.MinDate))
= t.[MM/YYYY]
drop table #temp

Need help returning data in defined buckets

declare #dateto as datetime
set #dateto='8/1/11'
SELECT ti.userkey, SUM(l.Price * l.OrderQty) AS SumOfOrders, count (distinct ti.orderid) AS Ordercount
FROM SOLine AS l WITH (nolock) LEFT OUTER JOIN
SOImported AS ti ON ti.OrderId = l.OrderId LEFT OUTER JOIN
Sale AS o ON o.SaleId = ti.Inventory_Id
Where ti.createddate < #DateTo
GROUP BY ti.userkey, o.WootSiteId
Need this query to bucket order count like the following:
Group userkeycount sumoforders
1 trans
2-30
31-60
61-90
91-120
121-150
151-180
181-210
211-240
241-270
271-300
301-330
331-360
360 or More
declare #dateto as datetime
set #dateto='8/1/11'
SELECT
CASE WHEN Ordercount >= 360 THEN '360 or More'
WHEN Ordercount = 1 THEN '1 trans'
ELSE CAST (30 * FLOOR(Ordercount / 30) AS VARCHAR) + '-' + CAST (30 * CEILING (Ordercount / 30) AS VARCHAR)
END grp,
COUNT(*) userkeycount,
SUM(SumOfOrders) SumOfOrders
FROM (
SELECT ti.userkey,
SUM(l.Price * l.OrderQty) AS SumOfOrders,
count (distinct ti.orderid) AS Ordercount
FROM SOLine AS l WITH (nolock) LEFT OUTER JOIN
SOImported AS ti ON ti.OrderId = l.OrderId LEFT OUTER JOIN
Sale AS o ON o.SaleId = ti.Inventory_Id
Where ti.createddate < #DateTo
GROUP BY ti.userkey
) rs
GROUP BY CASE WHEN Ordercount >= 360 THEN '360 or More'
WHEN Ordercount = 1 THEN '1 trans'
ELSE CAST (30 * FLOOR(Ordercount / 30) AS VARCHAR) + '-' + CAST (30 * CEILING (Ordercount / 30) AS VARCHAR)
END
I have made some changes in The Scrum Meister's query... below you can see the result.
create table #T (ID int identity(0,1) primary key, Ordercount int)
insert #T (Ordercount) values (1)
insert #T (Ordercount) values (2)
insert #T (Ordercount) values (5)
insert #T (Ordercount) values (30)
insert #T (Ordercount) values (37)
insert #T (Ordercount) values (60)
insert #T (Ordercount) values (61)
insert #T (Ordercount) values (360)
insert #T (Ordercount) values (361)
select Ordercount,
CASE WHEN Ordercount > 360 THEN '360 or More'
WHEN Ordercount = 1 THEN '1 trans'
WHEN Ordercount <= 30 THEN '2-30'
ELSE
CAST (30*FLOOR(case when Ordercount%30 = 0 then Ordercount-1 else Ordercount end/30)+1 AS VARCHAR)
+ '-' +
CAST (30*FLOOR(case when Ordercount%30 = 0 then Ordercount-1 else Ordercount end/30)+30 AS VARCHAR)
END
from #T