如何在Oracle SQL

时间:2015-10-26 06:24:45

标签: sql oracle

我需要一个查询,每天为特定部门的一组员工收集办理登机手续的状态信息。

我需要收集的人员保存在名为LEADER的简单表格中,例如:

 - LEADERNAME    -NUM-

 - TOM             1
 - BEN             2
 - LUCY            3
 - ERIC            4

我公司所有员工的办理登机手续状态保存在名为TB_DAYWORK的表格中。我们知道,每天办理登机手续后,桌子都会更新一行,包括您的姓名和办理登机手续的时间。

我为一个人的登记信息编写了一个SQL。 SQL是这样的:

select m.* ,n.starttime,n.peoplename 
from ( 
select 
everyDay,
to_char(everyday,'dy') as weekday,
lpad(to_char(everyday,'w'),6) as weekinmonth,
lpad(to_char(everyday,'ww'),6) as weekinyear 
from 
  (select to_date('20150901','yyyymmdd') + level - 1 as everyDay from dual connect by level <= (last_day(to_date('20150901','yyyymmdd')) - to_date('20150901','yyyymmdd') +1))) m 
left join 

(
select distinct STARTTIME,
Peoplename
from TB_DAYWORK where CREATETIME between to_date('2015-09-01 00:00:00','yyyy-mm-dd hh24:mi:ss') and to_date('2015-09-30 23:59:59','yyyy-mm-dd hh24:mi:ss') 
and PEOPLENAME in (select leadername from leader where num=1) ) n on m.EVERYDAY=n.STARTTIME
order by 1;

查询结果如下:

sql preview

从图像中可以看出,对于一个人来说,左边的连接和动态日历都找到了所有状态。如果PEOPLENAMESTARTTIME中的数据不为空,这意味着他们已在当天登记,或者如果该列为空,则表示他不在。

由于此处需要选择的所有人都在表LEADER中,并按列NUM排序。我认为应该有一种方法可以使用循环语句通过使用一个SQL来收集我们需要的所有人的状态。有没有办法做到这一点?

/ ********************** UPDATE *********************** ********* /

@APC感谢您的帮助,我刚刚测试了您的代码,我使用的最后一个代码是这样的:

    select m.* , n.starttime, n.peoplename 
from ( 
        select 
            everyDay,
            to_char(everyday,'dy') as weekday,
            lpad(to_char(everyday,'w'),6) as weekinmonth,
            lpad(to_char(everyday,'ww'),6) as weekinyear 
        from 
          (select to_date('20150901','yyyymmdd') + level - 1 as everyDay 
           from dual 
           connect by level <= (last_day(to_date('20150901','yyyymmdd')) - to_date('20150901','yyyymmdd') +1))
       ) m 
    left join 
        (
        select  t.STARTTIME,
                t.Peoplename,
                l.num
        from leader l
             join TB_DAYWORK t
                  on t.PEOPLENAME = leadername
        where t.STARTTIME between to_date('2015-09-01 00:00:00','yyyy-mm-dd hh24:mi:ss') and to_date('2015-09-30 23:59:59','yyyy-mm-dd hh24:mi:ss')
        order by l.num, t.Starttime
        ) n 
on m.EVERYDAY=n.STARTTIME
order by n.num,m.everyDay ;

这是我得到的结果:

[results of revised query

everydayleadername完美排序,但突出显示,您可以看到每日专栏错过了数据,此处丢失了9/12和9/13。

我有点困惑,因为据我所知LEFT JOIN会将所有数据保留在左侧,如果是这样,右侧 - STARTTIME列和PEPOPENAME列将有一些空行,这将很容易看到(你可以在第一张图片上看到它),这就是我使用它的原因。他们为什么现在不见了?

///// ******** UPDATE ****************** ** //////

交叉连接方式结果如下: cross join

/////////////// ************更新样本数据************** /// ////////////

这里只涉及2个表:TB_DAYWORKLEADER,我准备了样本表以供进一步探索。与原始表相同的数据,但所有带有中文字符的列都被罗马字母替换,以防万一出现字符问题。

LEADER非常简单,请在代码下面运行以构建示例:

create table leader(leadername varchar2(50), num number);

insert into leader values ('mazeping','1');
insert into leader values ('zhangyi','2');
insert into leader values ('taoshengfa','3');

TB_DAYWORK我得到了元数据:

CREATE TABLE "JSHXBJY"."TB_DAYWORK"
    (       "ROW_ID" VARCHAR2(32) NOT NULL ENABLE,
    "CREATETIME" DATE,
     "CREATEUSERID" VARCHAR2(32),
     "UPDATETIME" DATE,
     "UPDATEUSERID" VARCHAR2(32),
     "DELFLAG" NUMBER(10,0),
     "DEPTID" VARCHAR2(255),
     "STARTTIME" DATE,
     "STARTTIMESTR" VARCHAR2(255),
     "WORK_CONTENT" VARCHAR2(3000),
     "PEOPLENAME" VARCHAR2(255),
     "WORK_CONTENT_INFO" VARCHAR2(4000),
     "WORK_REMARK" VARCHAR2(255),
     "PEOPLE_SORT" NUMBER(10,0),
     "LOGINID" VARCHAR2(255),
     "HEAD_PHOTO_PATH" VARCHAR2(255)
     ) SEGMENT CREATION IMMEDIATE
    PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING
    STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
    PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
   TABLESPACE "JSHXBJY"

我也从原始表中导出了一些原始数据,请从以下网址下载: http://pan.baidu.com/s/1mgtRiBI(按下右上角的第二个按钮进行下载,如果无法访问此链接,请告诉我)。

和sqlloader的控制文件是:

LOAD DATA
INFILE 'c:\oracle\tb_daywork_data1.txt'
APPEND
into TABLE TB_DAYWORK

FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
trailing nullcols
(ROW_ID,CREATETIME date "yyyy-mm-dd hh24:mi:ss",CREATEUSERID,UPDATETIME date "yyyy-mm-dd",UPDATEUSERID,DELFLAG,DEPTID,STARTTIME date "yyyy-mm-dd",STARTTIMESTR,WORK_CONTENT,PEOPLENAME,WORK_CONTENT_INFO,WORK_REMARK,PEOPLE_SORT,LOGINID,HEAD_PHOTO_PATH)

,命令是:

C:\>sqlldr jshxbjy/jshxbjy control=c:\oracle\sqlldr.txt discard=c:\oracle\tb_daywork_dis.txt bad=c:\oracle\tb_daywork_bad.txt log=c:\oracle\tb_daywork_log.txt

我自己测试过,这些代码应该可以建立样本表,其中包括3月份的3人登记数据。

2 个答案:

答案 0 :(得分:1)

您不需要“循环”。 SELECT语句是一个循环,因为它为表中的每个记录返回一行。如果您想要LEADER表中的所有记录,只需删除NUM列上的过滤器,然后将该表连接到查询中。

所以你的查询应该是这样的:

select m.* , n.starttime, n.peoplename 
from ( 
        select 
            everyDay,
            to_char(everyday,'dy') as weekday,
            lpad(to_char(everyday,'w'),6) as weekinmonth,
            lpad(to_char(everyday,'ww'),6) as weekinyear 
        from 
          (select to_date('20150901','yyyymmdd') + level - 1 as everyDay 
           from dual 
           connect by level <= (last_day(to_date('20150901','yyyymmdd')) - to_date('20150901','yyyymmdd') +1))
       ) m 
    left join 
        (
        select t.STARTTIME,
                t.Peoplename,
                1.num
        from leader l
             join TB_DAYWORK t
                  on t.PEOPLENAME = leadername
        where t.CREATETIME between to_date('2015-09-01 00:00:00','yyyy-mm-dd hh24:mi:ss') and to_date('2015-09-30 23:59:59','yyyy-mm-dd hh24:mi:ss') 
        ) n 
on m.EVERYDAY=trunc(n.STARTTIME)
order by m.everyDay, n.starttime, n.num ;

我假设STARTTIME包含一个时间元素,所以你需要使用TRUNC()来加入数据元素。不确定为什么你的查询中有DISTINCT。

  

“STARTTIME列和PEPOPENAME列将有一些空行...为什么它们现在丢失?”

您的代码与我的版本略有不同。具体来说,您可以这样排序:order by n.num,m.everyDay ;

the documentation for ORDER BY中说:

  

NULLS LAST是升序的默认值

因为您按n.num排序,所以该列为空的所有行都将显示在结果集的底部。它们不会丢失,您只需要继续滚动

或者更改排序顺序。恢复到我使用的那个,或者order by n.num,m.everyDay NULLS FIRST ;

预测您的问题:如何在Leader不起作用的每个日期获取空的STARTTIME条目?

对于TB_DAYWORK中每个匹配日期,OUTER JOIN为生成的日历提供了一行,并且对于与TB_DAYWORK中的任何日期都不匹配的每个日期,我们为一行。但是,正如你所说,我们失去了每个领导者的空白STARTTIME的视觉提示。

要恢复它,我们需要从每个领导者的日历生成一组完整的行。为此,我们需要使用LEADER表CROSS JOIN日历子查询。将TB_DAYWORK左键加入到笛卡尔积中将提供您想要的视觉提示。

select 1.num, l.leadername, m.* , t.starttime 
from ( 
        select 
            everyDay,
            to_char(everyday,'dy') as weekday,
            lpad(to_char(everyday,'w'),6) as weekinmonth,
            lpad(to_char(everyday,'ww'),6) as weekinyear 
        from 
          (select to_date('20150901','yyyymmdd') + level - 1 as everyDay 
           from dual 
           connect by level <= (last_day(to_date('20150901','yyyymmdd')) - to_date('20150901','yyyymmdd') +1))
       ) m 
    cross join leader l
    left join tb_daywork t
      on t.peoplename = l.leadername
      and t.starttime = m.everyday
where t.createtime between to_date('2015-09-01 00:00:00','yyyy-mm-dd hh24:mi:ss') and to_date('2015-09-30 23:59:59','yyyy-mm-dd hh24:mi:ss') 
order by l.num, m.everyday ;

关于未经测试的代码的警告仍然适用,

答案 1 :(得分:0)

答案就在这里:

需要构建一个包含完整日期和人名的临时表,然后执行左连接以获取空白。

    select tmp.* , n.starttime, n.peoplename
from
( select m.*,l.*
    from (
        select
            everyDay,
            to_char(everyday,'dy') as weekday,
            lpad(to_char(everyday,'w'),6) as weekinmonth,
            lpad(to_char(everyday,'ww'),6) as weekinyear
        from
          (select to_date('20150901','yyyymmdd') + level - 1 as everyDay
           from dual
           connect by level <= (last_day(to_date('20150901','yyyymmdd')) - to_date('20150901','yyyymmdd') +1))
       ) m,
       leader l ) tmp
      left join
      (
        select  t.STARTTIME,
                t.Peoplename
        from  TB_DAYWORK t
        where t.STARTTIME between to_date('2015-09-01 00:00:00','yyyy-mm-dd hh24:mi:ss') and to_date('2015-09-30 23:59:59','yyyy-mm-dd hh24:mi:ss')
          ) n
on tmp.EVERYDAY=n.STARTTIME and tmp.leadername=n.peoplename
order by tmp.num,tmp.everyDay ;