【问题标题】:SQL Query to return records in excess of allowed amount within rangeSQL查询返回超出范围内允许数量的记录
【发布时间】:2019-09-30 16:00:00
【问题描述】:

想象一个工人被允许在定义的时间段内进行可变数量的轮班。我们希望查询任何时期内超过允许数量的班次以及所有时期外的班次。

我已将 db-fiddle 与测试查询相关联。这里的问题是:

  1. 对于多余的班次,合并班次的顺序是不确定的。我只想看到多余的班次(2016-05-30 是在仅授权 3 个班次的时期内的第 4 个班次)。
  2. 我也想看看根本没有授权的3班(2019-04-25、2019-06-02、2019-06-04)。

我希望我需要翻转查询(即从 Shift join Authorization 中选择)并使用 group by、order by 和 limit 的某种组合,但我没有取得任何成功。任何意见将不胜感激。

CREATE TABLE `Authorization` (
  `AuthorizationId` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `WorkerId` int(10) unsigned NOT NULL,
  `Start` date NOT NULL,
  `End` date NOT NULL,
  `ShiftsAllowed` int(10) unsigned NOT NULL,
  PRIMARY KEY (`AuthorizationId`)
);

CREATE TABLE `Shift` (
  `ShiftId` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `WorkerId` int(10) unsigned NOT NULL,
  `Date_` date NOT NULL,
  PRIMARY KEY (`ShiftId`)
);

INSERT INTO Authorization (WorkerId,Start,End,ShiftsAllowed) VALUES 
(1,'2019-05-01','2019-05-15',2),
(1,'2019-05-16','2019-05-31',3);

INSERT INTO Shift (WorkerId,Date_) VALUES 
(1,'2019-04-25'),
(1,'2019-05-01'),
(1,'2019-05-10'),
(1,'2019-05-16'),
(1,'2019-05-20'),
(1,'2019-05-25'),
(1,'2019-05-30'),
(1,'2019-06-02'),
(1,'2019-06-04');

select 
    Authorization.Start, 
    Authorization.End, 
    Authorization.ShiftsAllowed, 
    count(Shift.Date_), 
    group_concat(Shift.Date_),
    reverse(
      substring_index(
        reverse(group_concat(Shift.Date_)),
        ',',
        count(Shift.Date_) - Authorization.ShiftsAllowed
      )
    )
from Authorization
left join Shift
on
    Shift.WorkerId = Authorization.WorkerId
    and Shift.Date_ between Authorization.Start and Authorization.End
group by Authorization.AuthorizationId
having 
count(Shift.ShiftId) > Authorization.ShiftsAllowed

View on db-fiddle

【问题讨论】:

  • 学会正确使用GROUP BY
  • @Eric 你介意描述或展示一个例子吗?
  • Authorization.StartAuthorization.EndAuthorization.ShiftsAllowed 都是非聚合列,必须包含在GROUP BY 中。没有其他 dbms 系统将运行您的代码。即使是较新版本的 MySQL 也不会运行您的代码。

标签: mysql sql join group-by count


【解决方案1】:

由于 MySQL 5.7 不支持窗口功能。

我使用了相关子查询而不是它。

试试这个:

select a3.shiftid, a3.workerid, a3.date_ from (
select a2.*, 
(select count(*) 
 from (select s.*, a.start, a.end, a.shiftsallowed
        from Shift s
        left join Authorization a
        on a.workerid = s.workerid
        and s.date_ between a.start and a.end) a1 
        where a1.date_ <= a2.date_ 
        and a1.workerid = a2.workerid and a1.start = a2.start and a1.end  = a2.end) rnk
from (
select s.*, a.start, a.end, a.shiftsallowed
from Shift s
left join Authorization a
on a.workerid = s.workerid
and s.date_ between a.start and a.end )a2)a3
where a3.rnk > a3.shiftsallowed or rnk = 0

如果您不理解答案,我建议您运行此查询。

select s.*, a.start, a.end, a.shiftsallowed
from Shift s
left join Authorization a
on a.workerid = s.workerid
and s.date_ between a.start and a.end 

然后添加排名列--相关子查询

测试结果:

DB-Fiddle

【讨论】:

    【解决方案2】:

    提供 MCVE 做得很好。

    对于早于 8.0 的版本...

    SELECT workerid
         , date_   FROM 
         ( SELECT s.workerid
                , a.authorizationid
                , a.start
                , a.end
                , a.shiftsallowed
                , s.date_
                , CASE WHEN @prev = authorizationid THEN @i:=@i+1 ELSE @i:=1 END i
                , @prev:=authorizationid prev 
             FROM shift s 
             LEFT
             JOIN authorization a 
               ON a.workerid = s.workerid 
              AND s.date_ BETWEEN a.start AND a.end 
             JOIN 
                ( SELECT @prev:=null,@i:=0 ) vars 
            ORDER 
               BY a.authorizationid
                , s.date_
         ) x  WHERE i>shiftsallowed OR authorizationid IS NULL;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-31
      • 1970-01-01
      相关资源
      最近更新 更多