我需要一个查询,每天为特定部门的一组员工收集办理登机手续的状态信息。
我需要收集的人员保存在名为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;
查询结果如下:
从图像中可以看出,对于一个人来说,左边的连接和动态日历都找到了所有状态。如果PEOPLENAME
和STARTTIME
中的数据不为空,这意味着他们已在当天登记,或者如果该列为空,则表示他不在。
由于此处需要选择的所有人都在表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 ;
这是我得到的结果:
[
everyday
和leadername
完美排序,但突出显示,您可以看到每日专栏错过了数据,此处丢失了9/12和9/13。
我有点困惑,因为据我所知LEFT JOIN
会将所有数据保留在左侧,如果是这样,右侧 - STARTTIME
列和PEPOPENAME
列将有一些空行,这将很容易看到(你可以在第一张图片上看到它),这就是我使用它的原因。他们为什么现在不见了?
///// ******** UPDATE ****************** ** //////
交叉连接方式结果如下: cross join
/////////////// ************更新样本数据************** /// ////////////
这里只涉及2个表:TB_DAYWORK
和LEADER
,我准备了样本表以供进一步探索。与原始表相同的数据,但所有带有中文字符的列都被罗马字母替换,以防万一出现字符问题。
表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人登记数据。
答案 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 ;