【发布时间】:2014-01-19 12:06:56
【问题描述】:
我有两张表用于存储员工考勤详情。
一个表存储 emp Id 以及日期时间信息中的相应时间和时间,第二个表存储其他员工详细信息,如员工 ID、员工姓名等... 我需要生成一份报告,其中显示 emp 每天工作的总小时数,如果总小时数 > 4.5 否则不存在的状态列存储详细信息,例如 Present 还需要统计员工状态为在场的天数,员工总小时数大于6小于8.5的天数。
我已编写查询以获取所有详细信息,但性能无法接受,获取所有详细信息大约需要 30-35 分钟
如果我排除天数计算逻辑,大约需要 1-2 分钟
表结构是
Ist Employee 表 EmployeeID, EmployeeName.....其他细节(暂时不需要)
考勤表
Emp_ID、INOUT_Time
我的查询
DECLARE @currStartDate DATETIME
DECLARE @currEndDate DATETIME
declare @startDate datetime;
declare @endDate datetime;
set @startDate = CONVERT(Datetime, '12/16/2013');
set @endDate = CONVERT(Datetime, '01/16/2014');
SET @currStartDate=@startDate
SET @currEndDate=dateAdd(day,1,@startDate)
DECLARE @formatTable TABLE
(
EmployeeCode varchar(10),
EmployeeName varchar(100),
[Date] Datetime,
InTime datetime,
OutTime datetime,
TotalHrs varchar(10),
[Status] varchar(10)
)
WHILE @currEndDate <= @endDate
BEGIN
--get the day by day attendance Range
INSERT INTO @formatTable
(
EmployeeCode,
EmployeeName,
[Date],
InTime,
OutTime
)
SELECT
E.EmployeeID,
ISNULL(LTRIM(RTRIM(E.FirstName)),'') +' '+ISNULL(LTRIM(RTRIM(E.LastName)),'') AS EmployeeName,
@currStartDate,
MIN(AD.INOUT_Time) as INTIME,
CASE WHEN MAX(AD.INOUT_Time)=MIN(AD.INOUT_Time) THEN NULL ELSE MAX(AD.INOUT_Time) END as OUTTIME
FROM employees E WITH(NOLOCK)
LEFT OUTER JOIN Attendance AD
ON E.EmployeeID = AD.Emp_ID
AND INOUT_Time BETWEEN @currStartDate AND @currEndDate
GROUP BY E.EmployeeID,DATEADD(dd, 0, DATEDIFF(dd, 0, INOUT_Time ))
UPDATE @formatTable
SET TotalHrs=Convert(varchar(20),DATEDIFF(MINUTE, ISNULL(InTime,GETDATE()),ISNULL(OutTime,InTime))/Convert(decimal(4,2),60))
,[Status] =(CASE WHEN DATEDIFF(MINUTE, ISNULL(InTime,GETDATE()),ISNULL(OutTime,InTime))/Convert(decimal(4,2),60) >= 4.5
THEN 'P'
ELSE 'Abs' END )
,HoursStatus = (CASE WHEN DATEDIFF(MINUTE, ISNULL(InTime,GETDATE()),ISNULL(OutTime,InTime))/Convert(decimal(4,2),60) >= 8.5
THEN 'Greater Than 8.5'
WHEN DATEDIFF(MINUTE, ISNULL(InTime,GETDATE()),ISNULL(OutTime,InTime))/Convert(decimal(4,2),60) BETWEEN 6 AND 8.49
THEN '6-8.49'
WHEN DATEDIFF(MINUTE, ISNULL(InTime,GETDATE()),ISNULL(OutTime,InTime))/Convert(decimal(4,2),60) BETWEEN 4.5 AND 5.99
THEN '4.5-5.99' end)
WHERE [Date]=@currStartDate
-- moving to nextday
SELECT @currStartDate=DATEADD(DAY,1,@currStartDate)
SELECT @currEndDate=DATEADD(DAY,1,@currEndDate)
END
IF OBJECT_ID('tempdb..##output') IS NOT NULL
DROP TABLE ##output
SELECT EmployeeCode,EmployeeName,[Date],Convert(varchar(10),INTime,108) INTime,Convert(varchar(10),Outtime,108) Outtime,TotalHrs,[Status],Convert(varchar,@startDate,105) as StartDate,Convert(varchar,@endDate,105) as EndDate, (SELECT COUNT(*) FROM @formatTable counter
WHERE ft.EmployeeCode = counter.EmployeeCode AND counter.[Status] = 'P' ) AS TotalPresent, (SELECT COUNT(*) FROM @formatTable counter
WHERE ft.EmployeeCode = counter.EmployeeCode AND counter.HoursStatus = 'Greater Than 8.5' ) as gt8point5,(SELECT COUNT(*) FROM @formatTable counter
WHERE ft.EmployeeCode = counter.EmployeeCode AND counter.HoursStatus = '6-8.49' ) as gt6lessthan8,(SELECT COUNT(*) FROM @formatTable counter
WHERE ft.EmployeeCode = counter.EmployeeCode AND counter.HoursStatus = '4.5-5.99' ) as gt4point5lessthan6
INTO ##output FROM @formatTable as ft
GROUP BY EmployeeCode,EmployeeName,[Date],TotalHrs,INTime,Outtime,[Status]
SELECT * FROM ##output AS AttendanceReport
任何改进性能的建议,尤其是天数计算逻辑
【问题讨论】:
-
您是否检查过执行计划以验证您使用的索引是否正确?
-
我试过了,但无法理解执行计划的大部分内容。我的猜测是在计算不同场景的天数逻辑时,我会多次迭代每条记录,这很耗时,因为表包含大量接近 50,000 条的记录
-
50k 行是简单模式,假设您有正确的索引。分享你的执行计划!
-
刚才我已经执行了执行计划的查询,一旦执行了查询我将共享相同的
-
您可以在较小的日期范围内执行它以进行评估,例如一周。
标签: sql sql-server sql-server-2000