2 qq 34906828 qq_34906828 于 2016.09.12 16:32 提问

小白请教一个sql逻辑的问题,详见问题描述

现在有一个系统他十四个子模块,要求的是这个系统的停止时间,现在在一个表中记录着每一个子模块的停止时间,只要有一个子模块停止了,表里就会多一条关于子模块停止的开始时间、结束时间和子模块代码的记录。子模块停止就代表系统停止。现在要求一个周内系统的停止的时间。请问这个逻辑应该怎么写。注意的是如果子模块1停止时间为八点到九点,子模块2停止的时间为八点半到九点半,那么系统的停止时间是八点到九点半,要排除重复时间,请大神表述的详细一点,本人小白,不太懂这个o(╯□╰)o

14个回答

qq221746
qq221746   2016.09.12 17:19
已采纳

--看一下我理解的对不对
DECLARE

TYPE t_table IS TABLE OF 记录表%ROWTYPE;
v_data t_table;
v_second INTEGER:=0;
v_begin DATE;
v_end DATE;
BEGIN
SELECT * BULK COLLECT INTO v_data FROM 记录 ORDER BY 开始时间,结束时间 WHERE 开始时间>=sysdate-7;
FOR i IN 1..v_data.count LOOP
IF i=1 THEN
v_begin:=v_data(i).开始;
v_end:=v_data(i).结束;
ELSE
IF v_data(i).开始<v_data(i-1).结束 THEN
v_begin:=v_data(i-1).结束;
ELSE
v_begin:=v_data(i).开始;
END IF;
v_end:=v_data(i).结束;
END IF;
v_second:=(v_end-v_begin)*24*60*60+v_second;
END LOOP;
END;

qq221746
qq221746 回复qq_34906828: 不是只取重复时间, 第一条记录 1-5 v_second 就是 5-1+0 第二条3-9 就是9-5+(5-1+0) 循环最后都是:v_end-v_begin+v_second
一年多之前 回复
qq_34906828
qq_34906828 大哥,我这么写您觉得可以吗,就是现在已近取到重复的时间了,然后我把表内所用的子模块的停止时间做个和,这样就包含两次的重复时间,然后再减去一次的重复时间,您觉得咋样O(∩_∩)O
一年多之前 回复
qq_34906828
qq_34906828 大哥 我研究可以下,您这个思路是在大于两条数据的前提下,v_date(i).开始<v_date(i-1).结束时判断是否有共同时间,然后如果有的话就把第一个结束时间作为总的开始时间。然后把第二个结束时间作为总的结束时间,大哥,这样算的重复的时间吧,比如表里现在两条数据,第一个子模块的时间是1-5,第二个子模块的时间是3-9.我们要计算的系统的停止时间应该是1-9.大哥,您写的逻辑是取得重复的时间吧,不过您这个方法特别好。赞赞赞,您能不能再想想这个逻辑应该咋写n(*≧▽≦*)n
一年多之前 回复
qq_34906828
qq_34906828 大哥,我先研究下,有点看不懂了,刚入行,啥也不会。。。求原谅= ̄ω ̄=
一年多之前 回复
qq_34906828
qq_34906828   2016.09.12 16:41

急急急!拜托各位大神了!!小弟感激不尽!!!

xubo_ob
xubo_ob   2016.09.13 10:35

如果你懂java的话,就使用下我下面的代码吧 。
package zzQuestions;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

public class TimeRemoveMulti {

/**
 * 现在有一个系统他十四个子模块,要求的是这个系统的停止时间,
 * 现在在一个表中记录着每一个子模块的停止时间,只要有一个子模块停止了,
 * 表里就会多一条关于子模块停止的开始时间、结束时间和子模块代码的记录。
 * 子模块停止就代表系统停止。现在要求一个周内系统的停止的时间。请问这个逻辑应该怎么写。
 * 注意的是如果子模块1停止时间为八点到九点,子模块2停止的时间为八点半到九点半,
 * 那么系统的停止时间是八点到九点半,要排除重复时间
 * 系统的停止时间是如果一个子模块出现了停止,那么这个子模块的停止时间就要算到系统的停止时间里面,
 * 最后要统计的是每周的工作时间,
 * 所以不能是单纯的每个子模块的停止时间求和,要剔除各个子模块停止时间重复的部分
 * @throws ParseException 
 */
public static void main(String args[]) throws ParseException {
    List<A> dataArray = new ArrayList<A>(); //存放数据库值
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    dataArray.add(new A(df.parse("2016-9-10 11:23:24"), df.parse("2016-9-10 17:23:24")));
    dataArray.add(new A(df.parse("2016-9-10 8:23:24"), df.parse("2016-9-10 13:23:24")));
    dataArray.add(new A(df.parse("2016-9-11 11:23:24"), df.parse("2016-9-11 17:23:24")));
    LinkedList<A> resultArray = new LinkedList<A>(); //存放最终无重复的时间段值
    for (A a : dataArray) { //循环处理
        compare(a, resultArray);
    }
    for (A a : resultArray) { //输出最后的记结果
        System.out.println(df.format(a.getStartTime()) + "\t" + df.format(a.getEndTime()));
    }
}

public static void compare(A a, LinkedList<A> resultArray) {
    for (int i = 0; i <= resultArray.size();) {
        //size为0或最终结束
        if (i == resultArray.size()) {
            resultArray.add(a);
            return;
        }
        A b = resultArray.get(i);
        //结束时间小于开始时间,插在前面返回,且不需要再与后面比较直接返回
        if (a.getEndTime().before(b.getStartTime())) {
            resultArray.add(i, a);
            return;
        }
        //开始时间大于开始时间,i++进入下一次循环
        if (a.getStartTime().after(b.getEndTime())) {
            i++;
            continue;
        }
        //a被b包含,则直接退出
        if (a.getStartTime().after(b.getStartTime()) && a.getEndTime().before(b.getEndTime())) {
            return;
        }
        //a将b的第i个元素包含或者2这有交集,则将并集付给A,并且从resultArray中移除B,然后进入下一次循环
        {
            Date startTime = a.getStartTime().compareTo(b.getStartTime()) > 0 ? b.getStartTime() : a.getStartTime();
            Date endTime = a.getEndTime().compareTo(b.getEndTime()) > 0 ? a.getEndTime() : b.getEndTime();
            a.setStartTime(startTime);
            a.setEndTime(endTime);
            resultArray.remove(i);
            continue; //写了方便看
        }

    }
}

static class A {
    private Date startTime;
    private Date endTime;

    public A(Date startTime, Date endTime) {
        this.startTime = startTime;
        this.endTime = endTime;
    }

    public Date getStartTime() {
        return startTime;
    }

    public void setStartTime(Date startTime) {
        this.startTime = startTime;
    }

    public Date getEndTime() {
        return endTime;
    }

    public void setEndTime(Date endTime) {
        this.endTime = endTime;
    }

}

}

输出结果 :
2016-09-10 08:23:24 2016-09-10 17:23:24
2016-09-11 11:23:24 2016-09-11 17:23:24

qq_34906828
qq_34906828 谢谢大哥,我刚看到有第二页,谢谢谢谢,真的很谢谢您,咱俩素未谋面,您就可以花这么多时间来帮我,真的很谢谢您,但是我用的是oracle,java不懂呀。嘿嘿。但是还是很谢谢您,谢谢您的帮助。好人一生平安,也祝您早日当上总经理,出任ceo,迎娶白富美,走向人生巅峰!O(∩_∩)O哈哈~
一年多之前 回复
qq221746
qq221746   2016.09.12 16:43

所有子模块都停止,系统才算停止吗?

qq_34906828
qq_34906828 不是,是有一个停止就算到系统的停止时间里面,大神!!!赶紧救救我啊
一年多之前 回复
xubo_ob
xubo_ob   2016.09.12 16:43

得确认:系统的停止时间是什么

1、如果系统的停止时间是指所有的模块都停止,最后一个模块停止时候的停止时间;
那么直接看数据库中是否有14条记录,如果有,那么根据结束时间(肯定含日期的)倒序排列获取第一条就好了,没有就是系统没停止。

2、如果系统的停止时间,是指每天最后一个停止模块的结束时间,那么
先将所有的停止时间按天归类,然后在每个分类中将结束时间倒序排列,取第一个 。如果某天内没有数据,就是当天系统没停止。

xubo_ob
xubo_ob 回复qq_34906828:这样啊,那说下我的思路 。
一年多之前 回复
qq_34906828
qq_34906828 大神,系统的停止时间是如果一个子模块出现了停止,那么这个子模块的停止时间就要算到系统的停止时间里面,最后要统计的是每周的工作时间,所以不能是单纯的每个子模块的停止时间求和,要剔除各个子模块停止时间重复的部分。
一年多之前 回复
qq221746
qq221746   2016.09.12 16:48

select min(开始时间),max(停止时间) from 记录表 where 开始时间>=sysdate-7

呵呵 那么简单吗? 我是不是理解错了

是不是区间问题? 比如1点到2点

5点到8点

        10点到12点?
qq_34906828
qq_34906828 不是啊 大哥,比如子模块1八点到九点停的,子模块2十三点到十五点停的,再假设剩下的十二个子模块没有停止,那么系统停止的时间应该是三个小时。你这么算就成了七个小时了吧
一年多之前 回复
zhaihonghonghzh
zhaihonghonghzh   2016.09.12 17:20

看你描述 应该是有时间间隔的是吗
先查询出来这一周内的记录 然后把开始时间和结束时间都取出来放到一个集合中进行时间判断

qq_34906828
qq_34906828 是 姐,我也是这么想的,但是关于具体怎么写判断条件这一块不太会啊,感觉老是会漏或者重复,您可以具体说一下您的思路吗= ̄ω ̄=
一年多之前 回复
zhaihonghonghzh
zhaihonghonghzh   2016.09.12 17:21

查询时按着开始时间排序

u013829202
u013829202   Rxr 2016.09.12 17:32

做一个排序,然后判断第二个开始时间是否在第一个的里面,如果在,直接取第二个的结束时间。 不在就分别计算。

qq_34906828
qq_34906828 就是还有时间重叠的情况啊,比如模块1是八点到九点,模块2是八点半到九点半,这种最难办╮(╯▽╰)╭
一年多之前 回复
qq221746
qq221746   2016.09.12 18:02

SELECT SUM(times) FROM (
select b.*,case
when bd >= pre_end then
ed - bd
else
ed - pre_end
END AS times
from (select lag(a.ed, 1, a.bd) over(order by bd, bd) pre_end,a.*
from 表名 a
order by bd, ed) b)

            对应sql可以这么写,用lag开窗函数     bd 开始时间   ed 结束时间   
            有点绕  可能还不如我上面写的代码块好理解
qq221746
qq221746 回复qq_34906828: 4.最后将第三步算的时间sum一下 就可以了
一年多之前 回复
qq221746
qq221746 3.如果本次开始时间小于上次结束时间(意思是时间有交叉),则本次只能算本次结束-上次结束了
一年多之前 回复
qq221746
qq221746 2.然后判断本次开始时间如果大于上次结束时间 这条记录的总结束时间就是本次结束时间-本次开始时间
一年多之前 回复
qq221746
qq221746 回复qq_34906828: 1.你得首先知道 lag(a.ed, 1, a.bd) over(order by bd, bd) 这个lag函数的作用 ,在这里面的意思是取前一条记录的结束时间,具体你要百度下
一年多之前 回复
qq_34906828
qq_34906828 回复qq_34906828: 我也是oracleO(∩_∩)O哈哈~
一年多之前 回复
qq_34906828
qq_34906828 哥,可以稍微稍微一点点解释一下上面写的代码吗?恕小弟懂得实在不多,多谢多谢,我知道您是个好人n(*≧▽≦*)n
一年多之前 回复
共14条数据 1 尾页
Csdn user default icon
上传中...
上传图片
插入图片
准确详细的回答,更有利于被提问者采纳,从而获得C币。复制、灌水、广告等回答会被删除,是时候展现真正的技术了!