当前位置: 首页 > 知识库问答 >
问题:

sql计数性能问题

宗波涛
2023-03-14

我有两个表用于存储员工出勤信息。

一个表存储emp Id以及相应的时间和日期时间信息。第二个表存储其他员工详细信息,如员工Id、员工姓名等。。。我需要生成一份报告,显示emp每天工作的总小时数,一个状态列存储详细信息,如Present if total hours

我写了查询来获取每一个细节,但性能是不可接受的,需要大约30-35分钟来获取所有细节

如果排除天数计算逻辑,大约需要1-2分钟

表的结构是

Ist Employee表EmployeeID,EmployeeName。。。。。其他细节(目前不需要)

出席表

Emp\u ID,INOUT\u时间

我的问题

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

是否有改进性能的建议,尤其是计日逻辑

共有1个答案

韦衡
2023-03-14

快速查看您的查询,建议使用以下索引。

-- Assuming you don't have EmployeeID as a clustered index
CREATE INDEX IX_Employees_EmployeeID 
             ON Employees (EmployeeID)
                  INCLUDE (FirstName, LastName)

CREATE INDEX IX_Attendance_EmployeeID_INOUTTime
             ON Attendance (EmployeeID, INOUT_Time)

您还可以为@formatTable创建索引,但这取决于查询计划的状态以及生成的行数。

 类似资料:
  • 问题内容: 我们的数据库中有一个函数,该函数搜索两个大表以查看是否存在值。这是一个相当大的查询,但已对其进行了优化以使用索引,并且通常运行速度非常快。 在过去的2周中,此功能有3次决定进入麻烦境地,并且运行极其缓慢,这会导致死锁和性能下降。即使在少于高峰使用时间的情况下,也会发生这种情况。 在SQL Server中使用“更改功能”重建功能似乎可以解决此问题。完成后,服务器使用率将恢复正常,一切正常

  • 我有一张大约有一百万行的桌子。我们的部分维护包括每天删除旧行,但这需要大约40分钟。 delete语句是: 我能做些什么来提高性能吗? 谢谢 根据要求: ([cder\u ID]ASC)在[PRIMARY]上具有(PAD\u INDEX=OFF,STATISTICS\u NORECOMPUTE=OFF,IGNORE\u DUP\u KEY=OFF,ALLOW\u ROW\u LOCKS=ON,AL

  • 问题内容: 这两个查询在具有1000万行的表上运行缓慢。我想知道为什么为什么从mysql保留所有insert,update和delete更新的计数器变得不容易? 有没有办法改善这个查询?我使用了说明,但并没有太大帮助。 问题答案: 正如cherouvim在评论中指出的那样,它取决于存储引擎。 确实会保留表行的计数,并且由于MyISAM支持的唯一锁是表锁,因此可以保持准确。 但是支持事务,因此需要进

  • 问题内容: 我试图重构一些旧代码…我重构了下面的当前代码,并突出显示了导致性能问题的语句。我试图用左外部联接重写该部分。 任何人都可以提供帮助,或者在可能的情况下提出更好的建议吗? 问题答案: 您有一个列级联,可防止任何索引的使用 尝试不存在将分别支持2列 之后,检查您的索引或课程… 这也是EXCEPT的一个很好的用法(ORDER BY像UNION一样结束了:感谢@Damien_The_Unbel

  • 问题内容: 我的数据库中有两个表,和。一个操作需要零个或多个属性。但是,属性的​​属性分配有一些逻辑: 操作需要设备和 操作无需任何设备 操作需要设备,或者 操作需要设备(或)和(或) 用SQL表示这一点的最佳方法是什么? 我敢肯定人们以前已经做过,但是我不知道从哪里开始。 (FWIW,我的应用程序是使用Python和Django构建的。) 更新1: 将有大约一千行和三十行。信息以CSV形式出现,

  • Windsor 3 引入了 Windows 性能计数器的支持。 现在 Windsor 只提供了一个计数器 - “通过释放策略跟踪的对象(Objects tracked by release policy)”,显示了指定容器通过释放策略跟踪的对象的总数量。 :information_source: 寻找内存泄露: 这是一个非常有用的特性,能够帮助快速确定是否有未释放被跟踪组件实例的问题。 使用计数器