【问题标题】:Employee Monthly Attendance Using SQL Server使用 SQL Server 的员工月度考勤
【发布时间】:2014-01-03 07:16:41
【问题描述】:

我有两张表,一张是Tab_Users 包含用户详细信息,另一张是Tab_CardEntry 包含他们的卡片条目。我需要每天为每个用户获取他们的最短和最长刷卡时间。

这是我使用的 SQL 查询

SELECT
    A.Name,
    ISNULL(CONVERT(VARCHAR, MIN(B.DATED), 108), 'OFF') LOGEDIN,
    ISNULL(CONVERT(VARCHAR, MAX(B.DATED), 108), 'OFF') LOGEDOUT
FROM TAB_USERS A
LEFT OUTER JOIN (
    SELECT
        USERNAME,
        DATED
    FROM TAB_CARDENTRY
    WHERE CONVERT(VARCHAR, DATED, 106) = CONVERT(VARCHAR, '02 Jan 2014', 106)
) B ON A.USERNAME = B.USERNAME
GROUP BY A.Name
ORDER BY 1

我需要这种类型的输出

【问题讨论】:

  • @Jaques 不,不是。我需要它整整一个月而不是一天。
  • 您可以创建一个每天包含一条记录的新表 - 并用几年时间填充它。然后 INNER JOIN 与 CardEntry 这个新表,你可以按天分组。您需要四处玩耍才能得到您所追求的。

标签: sql sql-server


【解决方案1】:

这是一个您可以尝试的存储过程

CREATE PROCEDURE usp_GetTimes @StartDate DATETIME, @EndDate DATETIME = NULL
AS
BEGIN
    --Make sure that only 31 days of data is used otherwise the temp table will be too big
    IF @EndDate IS NULL OR DATEDIFF(dd, @StartDate, @EndDate) > 31
      SET @EndDate = DATEADD(dd, 31, @StartDate)

    SELECT UserID, CONVERT(DATETIME, FLOOR(CONVERT(FLOAT, DateTimeIn))) ForDate, DateTimeIn, DateTimeOut
    INTO #Times
    FROM
    (
    SELECT  DISTINCT MIN(a.DATED) OVER (PARTITION BY a.USERNAME, CONVERT(DATETIME, FLOOR(CONVERT(FLOAT, DATED)))) DateTimeIn,
            MAX(a.DATED) OVER (PARTITION BY a.USERNAME, CONVERT(DATETIME, FLOOR(CONVERT(FLOAT, DATED)))) DateTimeOut, 
            USERNAME AS UserID
    FROM TAB_CARDENTRY a
    WHERE DATED BETWEEN @StartDate and @EndDate + 1
    ) a

    SELECT DISTINCT ForDate INTO #Dates FROM #Times

    --Select * from #Times order by 1
    --Select * from #Dates

    DECLARE @SQLstring VARCHAR(MAX)

    SET @SQLstring = 'CREATE TABLE ##Test (UserID varchar(10), '

    DECLARE @ForDate DATETIME

    WHILE 0 < (SELECT COUNT(*) FROM #Dates)
        BEGIN
        SET ROWCOUNT 1
        SELECT @ForDate = ForDate FROM #Dates order by ForDate
        SET ROWCOUNT 0
        SET @SQLstring = @SQLstring + '[Entry ' + CONVERT(VARCHAR(12), @ForDate, 106) + '] datetime, [Exit ' + CONVERT(VARCHAR(12), @ForDate, 106) + '] datetime,' + CHAR(13) + CHAR(10)
        DELETE #Dates WHERE ForDate = @ForDate
        END

    SET @SQLstring = SUBSTRING(@SQLSTRING, 1, LEN(@SQLSTRING) - 3) + ')'
    EXEC (@SQLstring)
    CREATE INDEX idx1 on ##Test(UserID)

    DECLARE @UserID VARCHAR(10),
            @DateIn DATETIME,
            @DateOut DATETIME

    WHILE 0 < (SELECT COUNT(*) FROM #Times)
        BEGIN
        SET ROWCOUNT 1
        SELECT @UserID = UserID, @DateIn = DateTimeIn, @DateOut = DateTimeOut, @ForDate = ForDate from #Times
        SET ROWCOUNT 0
        SET @SQLString = 'IF NOT EXISTS(SELECT 1 FROM ##Test WHERE UserID = ''' + @UserID + ''')' + CHAR(13) + CHAR(10) +
                         '  INSERT ##Test (UserID, [Entry ' + CONVERT(VARCHAR(12), @ForDate, 106) + '], [Exit ' + CONVERT(VARCHAR(12), @ForDate, 106) + ']) ' +
                         '      SELECT ''' + @UserID + ''', ''' + CONVERT(VARCHAR(20), @DateIn, 113) + ''', ''' + CONVERT(VARCHAR(20), @DateOut, 113) + '''' + CHAR(13) + CHAR(10) +
                         'ELSE' + CHAR(13) + CHAR(10) +
                         '  UPDATE ##Test SET [Entry ' + CONVERT(VARCHAR(12), @ForDate, 106) + '] = ''' + CONVERT(VARCHAR(20), @DateIn, 113) + ''', ' + CHAR(13) + CHAR(10) +
                         '  [Exit ' + CONVERT(VARCHAR(12), @ForDate, 106) + '] = ''' + CONVERT(VARCHAR(20), @DateOut, 113) + ''' WHERE UserID = ''' + @UserID + ''''
        EXEC (@SQLstring)
        DELETE #Times WHERE UserID = @UserID AND ForDate = @ForDate
        END

    SELECT * FROM ##Test ORDER BY 1
    DROP TABLE ##Test
END

编辑

我再次查看了查询,您可能不喜欢输出,因此您可以将 SP 的一部分更改为如下所示:

创建临时表的部分

WHILE 0 < (SELECT COUNT(*) FROM #Dates)
            BEGIN
            SET ROWCOUNT 1
            SELECT @ForDate = ForDate FROM #Dates order by ForDate
            SET ROWCOUNT 0
            SET @SQLstring = @SQLstring + '[Entry ' + CONVERT(VARCHAR(12), @ForDate, 106) + '] varchar(20), [Exit ' + CONVERT(VARCHAR(12), @ForDate, 106) + '] varchar(20),' + CHAR(13) + CHAR(10)
            DELETE #Dates WHERE ForDate = @ForDate
            END

以及向表中插入数据的部分

WHILE 0 < (SELECT COUNT(*) FROM #Times)
    BEGIN
    SET ROWCOUNT 1
    SELECT @UserID = UserID, @DateIn = DateTimeIn, @DateOut = DateTimeOut, @ForDate = ForDate from #Times
    SET ROWCOUNT 0
    SET @SQLString = 'IF NOT EXISTS(SELECT 1 FROM ##Test WHERE UserID = ''' + @UserID + ''')' + CHAR(13) + CHAR(10) +
                     '  INSERT ##Test (UserID, [Entry ' + CONVERT(VARCHAR(12), @ForDate, 106) + '], [Exit ' + CONVERT(VARCHAR(12), @ForDate, 106) + ']) ' +
                     '      SELECT ''' + @UserID + ''', ''' + CONVERT(VARCHAR(20), ISNULL(@DateIn, '''OFF''', 108) + ''', ''' + CONVERT(VARCHAR(20), ISNULL(@DateOut, '''OFF'''), 108) + '''' + CHAR(13) + CHAR(10) +
                     'ELSE' + CHAR(13) + CHAR(10) +
                     '  UPDATE ##Test SET [Entry ' + CONVERT(VARCHAR(12), @ForDate, 106) + '] = ''' + CONVERT(VARCHAR(20), ISNULL(@DateIn, '''OFF'''), 108) + ''', ' + CHAR(13) + CHAR(10) +
                     '  [Exit ' + CONVERT(VARCHAR(12), @ForDate, 106) + '] = ''' + CONVERT(VARCHAR(20), ISNULL(@DateOut, '''OFF''', 108) + ''' WHERE UserID = ''' + @UserID + ''''
    EXEC (@SQLstring)
    DELETE #Times WHERE UserID = @UserID AND ForDate = @ForDate
    END

【讨论】:

  • Thanks.but getting Error usp_GetTimes '1-dec-2013','31-dec-2013' Msg 2715, Level 16, State 7, Line 1 Column, parameter, or variable #2: 不能查找数据类型时间。
  • 我以为您使用的是 SQL 2012。将其设为日期时间,它应该可以工作。
猜你喜欢
  • 2012-04-21
  • 2019-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多