Mysql 自定义函数内部的orderby

Mysql 自定义函数内部的orderby,mysql,sql-order-by,Mysql,Sql Order By,我想做一个相当复杂的查询。我有一个带块的数据库。 每个块都有一个开始日期、结束日期和它所属的模块。 我必须计算营业额,即连续区块之间的差额(对于区块[I]): 块[i]。开始-块[i-1]。结束 让我们举个例子,我有这些数据: create table blocks (start datetime, end datetime, module integer); insert into blocks (start, end, module) values ('2016-04-13 09:00:0

我想做一个相当复杂的查询。我有一个带块的数据库。 每个块都有一个开始日期、结束日期和它所属的模块。 我必须计算营业额,即连续区块之间的差额(对于区块[I]):

块[i]。开始-块[i-1]。结束

让我们举个例子,我有这些数据:

create table blocks (start datetime, end datetime, module integer);
insert into blocks (start, end, module)
values 
('2016-04-13 09:00:00',  '2016-04-13 10:00:00', 1), -- diff: null or 0
('2016-04-13 11:00:00',  '2016-04-13 12:00:00', 1), -- diff: 1hour
('2016-04-13 12:30:00',  '2016-04-13 14:00:00', 1), -- diff: 30minutes
                                                    -- turnoverAvg: 45min = (1h + 30min) / 2

('2016-04-13 09:00:00',  '2016-04-13 10:00:00', 2), -- diff: null or 0
('2016-04-13 12:00:00',  '2016-04-13 12:30:00', 2), -- diff: 2hour
('2016-04-13 13:30:00',  '2016-04-13 14:30:00', 2), -- diff: 1hour
                                                    -- turnoverAvg: 90min = (2h + 1h) / 2

('2016-04-14 14:30:00',  '2016-04-14 16:00:00', 2), -- diff: null or 0
('2016-04-14 17:00:00',  '2016-04-14 18:00:00', 2), -- diff: 1hour
                                                    -- turnoverAvg: 60min = 1h/1

('2016-04-13 09:00:00',  '2016-04-13 10:00:00', 3), -- diff: null or 0
('2016-04-13 10:00:00',  '2016-04-13 11:00:00', 3), -- diff: 0
('2016-04-13 12:00:00',  '2016-04-13 13:00:00', 3), -- diff: 1hour
('2016-04-13 14:00:00',  '2016-04-13 15:00:00', 3), -- diff: 1hour
('2016-04-13 16:00:00',  '2016-04-13 17:00:00', 3), -- diff: 1hour
                                                    -- turnoverAvg: 45min = (0 + 1h + 1h + 1h) / 4

('2016-04-13 09:00:00',  '2016-04-13 10:00:00', 4), -- diff: null or 0
                                                    -- turnoverAvg: null

('2016-04-13 09:00:00',  '2016-04-13 15:00:00', 5), -- diff: null or 0
('2016-04-13 19:00:00',  '2016-04-13 20:00:00', 5); -- diff: 4hour
                                                    -- turnoverAvg: 240min = 4h/1
我应该进行以下查询(伪代码):

其中turnoverAvg是这样的函数(伪代码):


turnoverAVG如果在几分钟内就可以了,但我这样写是为了让大家更好地理解它。正如您所见,它从不计算第一个块,因为它不能与前一个块相减(没有前一个块)。

这样的函数被调用。它们仅从MySQL 8开始提供

在此之前,您必须找到另一种方式来编写并执行查询,请参见例如。大多数情况下,您将通过使用变量来实现,尽管sql方法是使用联接

但在您的特定情况下,您实际上并不需要这些:加班时间不仅是模块之间的总和(您需要知道前一行),而且是一天开始和结束之间的时间(您只需要
min
max
)减去模块运行的时间(不需要上一行)

所以试试这个:

select 
  module,
  date(start),
  case when count(module) > 1
    then (TIMESTAMPDIFF(Minute,min(start),max(end)) -
           sum(TIMESTAMPDIFF(Minute,start, end))) 
         / (count(module) - 1)
    else null 
  end as turnoverAVG,
  -- details, just for information:
  TIMESTAMPDIFF(Minute,min(start),max(end)) as total_day,
  sum(TIMESTAMPDIFF(Minute,start, end)) as module_duration,
  TIMESTAMPDIFF(Minute,min(start),max(end)) -
    sum(TIMESTAMPDIFF(Minute,start, end)) as turnover,
  count(module) as cnt
from blocks
group by date(start), module;
这4个附加列正好用于显示计算中使用的不同术语n,并且可以删除它们

所有模块都要求在同一日期开始和结束(尽管您可以简单地修改它以支持隔夜模块)。如果模块重叠,它也不会更正时间(但伪代码也不会)


不完全清楚您是否希望只包含一个模块的天数(如模块4的注释中所建议的)或不包含(如示例输出中所建议的)。如果您希望排除这些天数,您可以添加例如
having count(模块)>1
在查询结束时。

您能以格式化文本块的形式提供所需的结果吗。@当然很抱歉,现在我更新了问题。我正在努力理解以下条目:
'2016-04-13 12:30:00','2016-04-13 10:00:00'
很抱歉,编写示例是一个错误。如果这些块是按时间排序的(开始,结束),每个区块对应一个时间间隔。营业额查找每个间隔之间存在的时间,我尝试给你一个简单的例子:假设我有两个区块(我添加了一个名称以使其更简单):开始,结束,名称,模块2017-01-01 00:00,2017-01-01 08:00,“睡眠”,2017-01-01 09:00,2017-01-01 15:00,“工作”,1营业额将指示从醒来到上班所需的时间。睡觉前没有阻塞,因此我没有营业额。只需修复数据集(以及相应的预期结果)非常感谢!这是一个非常好的解决方案,我没有注意到这个属性。我当时也尝试使用ROW_NUMBER,问题是必须执行到临时表的自连接以计算相邻行之间的差异,并且我没有在不进行多次计算的情况下获得最佳解决方案。毫无疑问,这是非常有效的,我很感激你的代码和解释,这正是我想要的。
function turnoverAVG(rows):
  acc = 0.0
  for(i=1; i < rows.length; i++)
    d = row[i].start - rows[i - 1].end
    acc += d
  return acc/(rows.length - 1)
turnoverAVG, module, day
45min, 1, 2016-04-13
1:30hour, 2, 2016-04-13
1hour, 2, 2016-04-14 -- different day but same module
45min, 3, 2016-04-13
4hour, 5, 2016-04-13
select 
  module,
  date(start),
  case when count(module) > 1
    then (TIMESTAMPDIFF(Minute,min(start),max(end)) -
           sum(TIMESTAMPDIFF(Minute,start, end))) 
         / (count(module) - 1)
    else null 
  end as turnoverAVG,
  -- details, just for information:
  TIMESTAMPDIFF(Minute,min(start),max(end)) as total_day,
  sum(TIMESTAMPDIFF(Minute,start, end)) as module_duration,
  TIMESTAMPDIFF(Minute,min(start),max(end)) -
    sum(TIMESTAMPDIFF(Minute,start, end)) as turnover,
  count(module) as cnt
from blocks
group by date(start), module;