生成复杂的sql表

时间:2017-05-15 19:55:58

标签: mysql sql stored-procedures

我目前有一个员工记录sql表有3列

fromState: StringtoState: Stringtimestamp: DateTime

fromState可以是InOutIn表示员工进来,Out表示员工出去了。每行只能从In转换为OutOut转换为In

我想在sql中生成一个临时表,以便在给定的小时(小时,小时)内跟踪,公司中有多少员工。 Aka,结果表包含HourBucketNumEmployees列。

在非SQL代码中,我可以通过将numEmployees初始化为0并逐行查看表(按timestamp排序)并添加(员工进来)来完成此操作或减去(去掉)到numEmployees(按timestamp小时划分)。

我对如何在SQL中执行此操作毫无头绪。有线索吗?

2 个答案:

答案 0 :(得分:0)

使用COUNT ... GROUP BY查询。但是,从您的描述中看不到您使用的状态了!另外,假设您有一个employeeID字段。

E.g。

SELECT fromState AS 'Status', COUNT(*) AS 'Number' 
FROM StaffinBuildingTable
INNER JOIN (SELECT employeeID AS 'empID', MAX(timestamp) AS 'latest' FROM StaffinBuildingTable GROUP BY employeeID) AS LastEntry ON StaffinBuildingTable.employeeID = LastEntry.empID
GROUP BY fromState

LastEntry子查询将生成一个employeeID列表,该列表仅限于每位员工的上一个时间戳。

INNER JOIN会将主表限制为只与双方匹配的employeeID。

外部GROUP BY产生计数。

SELECT HOUR(SBT.timestamp) AS 'Hour', SBT.fromState AS 'Status', COUNT(*) AS 'Number' 
FROM StaffinBuildingTable AS SBT
INNER JOIN (
    SELECT SBIJ.employeeID AS 'empID', MAX(timestamp) AS 'latest' 
    FROM StaffinBuildingTable AS SBIJ
    WHERE DATE(SBIJ.timestamp) = CURDATE()
    GROUP BY SBIJ.employeeID) AS LastEntry ON SBT.employeeID = LastEntry.empID
GROUP BY SBT.fromState, HOUR(SBT.timestamp)

将CURDATE()替换为您感兴趣的日期。

请注意,这不是最佳的,因为它会计算两次HOUR - 一次是数据,一次是组。

您再次使用INNER JOIN来限制返回的行数,这次是指定日期的最后一个时间戳。

答案 1 :(得分:0)

对我而言,您对FromStateToState的描述似乎是错误的方式,我希望基于ToState执行此操作。但是假设我错了,以下内容应该指向正确的方向:

首先,我创建一个"数字"每天每小时包含24行的表格:

create table tblHours
(Number int);

insert into tblHours values
(0),(1),(2),(3),(4),(5),(6),(7),
(8),(9),(10),(11),(12),(13),(14),(15),
(16),(17),(18),(19),(20),(21),(22),(23);

然后,对于员工日志记录表中的每个日期,我在另一个新表中创建一行以包含您的计数:

create table tblDailyHours
(
HourBucket datetime,
NumEmployees int
);

insert into tblDailyHours (HourBucket, NumEmployees)
select distinct
    date_add(date(t.timeStamp), interval h.Number HOUR) as HourBucket,
    0 as NumEmployees
from 
tblEmployeeLogging t
CROSS JOIN tblHours h;

然后我更新此表以包含所有相关计数:

update tblDailyHours h
    join
    (select
        h2.HourBucket,
        sum(case when el.fromState = 'In' then 1 else -1 end) as cnt
     from
        tblDailyHours h2
        join tblEmployeeLogging el on
            h2.HourBucket >= el.timeStamp
     group by h2.HourBucket
    ) cnt ON
    h.HourBucket = cnt.HourBucket
set NumEmployees = cnt.cnt;

您现在可以使用

检索计数
select * 
from tblDailyHours
order by HourBucket;

计数会在显示的每个时间点显示网站上的数字,如果您想在所涉及的小时内,我们需要调整此数据小。

此代码有一个工作版本(在日志记录表中使用的不是非常现实的数据):rextester.com/DYOR23344

原始答案(基于所有统计数据)

如果您乐意搜索所有行,并希望获得当前的人数"你可以用这个:

select
    sum(case when t.FromState = 'In' then 1 else -1) as Heads
from
    MyTable t

但是如果你知道在午夜总会有一个人没有人,你可以添加一个where子句来阻止它查看比它需要更多的行:

where
    date(t.timestamp) = curdate()

同样,假设人头数在午夜时间达到零,您可以随意推广该方法以获得人数,如下所示:

where
    date(t.timestamp) = "CENSUS DATE" AND
    t.timestamp <= "CENSUS DATETIME"

显然,您需要使用返回感兴趣的日期和日期时间的代码替换我引用的字符串。如果员工人数在午夜没有返回零,您可以通过删除where子句的第一行来实现相同目的。