简体   繁体   English

如何在Oracle SQL中编写循环语句

[英]How to write a loop statement in Oracle SQL

I need a query for collecting information of check-in status for a group of staff in a particular department everyday. 我每天都需要一个查询来收集一组特定部门工作人员的签到状态信息。

People I need to collect are saved in a simple table named LEADER , like: 我需要收集的人保存在一个名为LEADER的简单表中,例如:

 - LEADERNAME    -NUM-

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

The check-in status of all staff in my company is saved in a table named TB_DAYWORK . 我公司所有员工的签到状态保存在名为TB_DAYWORK的表中。 We know that everyday, once you checked in, the table will update with a new line, including your name and the check-in time. 我们知道,每天您签入后,表格都会以新的一行进行更新,包括您的姓名和签到时间。

I've composed a SQL for ONE person's check-in information. 我已经为一个人的签到信息编写了一个SQL。 the SQL is like this: 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;

The query result is just like this: 查询结果如下:

sql预览

From the image you can see that for one person, all status has been found by a left join with a dynamic calendar. 从图像中可以看到,对于一个人来说,所有状态都是通过动态日历的左联接找到的。 If the data in PEOPLENAME and STARTTIME is not null that means they had checked-in for that day, or if the column is null, it means he was absent. 如果PEOPLENAMESTARTTIME的数据不为空,则表示他们当天已签入,或者该PEOPLENAME空,则表示他不在。

Since all people need to be selected here are in the table LEADER and sorted by the column NUM . 由于这里需要选择所有人员,因此它们在表LEADER并按NUM列排序。 I think there should be a way of using a loop statement to collect all people's status we need by using one SQL. 我认为应该有一种使用循环语句通过使用一个SQL来收集我们需要的所有人的状态的方法。 Is there a way to do this? 有没有办法做到这一点?

/**********************UPDATE********************************/ / **********************更新************************** ****** /

@APC thank you for your help, I've just tested your code and the last code I use is like this: @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 ;

here's the result I got: 这是我得到的结果:

[ [ 修改查询的结果

Perfectly ordered by everyday and leadername , but as highlighted, you can see that the everyday column has missed data, here it lost 9/12 and 9/13. everydayleadername完美排序,但是突出显示,您可以看到日常列缺少数据,此处丢失了9/12和9/13。

I am a little confused, because as far as I know the LEFT JOIN will keep all data at the left side, if so the right side -- STARTTIME column and PEPOPENAME column will have some null lines, which will be easy to see (you can see it on the first pic), and that is the reason I use it. 我有点困惑,因为据我所知LEFT JOIN将保留所有的数据在左侧,如果是右侧- STARTTIME柱和PEPOPENAME列将有一些空行,这将是很容易看到(你可以在第一张照片上看到它),这就是我使用它的原因。 Why are they missing now? 他们为什么现在不见了?

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

the cross join way results is like this: cross join 交叉联接的结果是这样的: 交叉联接

///////////////************UPDATE SAMPLE DATA**************/////////////// /////////////// ************更新样本数据************** /////// /////////

only 2 tables are involved here: TB_DAYWORK and LEADER , I prepared sample tables for further explore. 这里仅涉及2个表: TB_DAYWORKLEADER ,我准备了示例表以进行进一步的研究。 same data to the original tables, but all columns with chinese characters are replaced by roman letters, just in case for the character issue. 与原始表格的数据相同,但所有带有中文字符的列均替换为罗马字母,以防万一出现字符问题。

for table LEADER which is very simple, please run below code to build a sample: 对于非常简单的表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');

for table TB_DAYWORK I got its metadata: 对于表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"

also I exported some raw data from the original table, please download from: http://pan.baidu.com/s/1mgtRiBI (press the 2nd button on the top-right corner to download, please let me know if this link cannot be reached ). 我也从原始表格中导出了一些原始数据,请从以下网址下载: http : //pan.baidu.com/s/1mgtRiBI (按右上角的第2个按钮进行下载, 请告诉我此链接是否无法达到 )。

and the control file for sqlloader is: 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)

and the command is : 并且命令是:

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

I've tested by myself, these codes should be ok to build up sample tables, which including 3 people's check-in data in this September. 我已经对自己进行了测试,可以使用这些代码建立示例表,其中包括今年9月的3个人签入数据。

You don't need a "loop". 您不需要“循环”。 A SELECT statement is a loop, in that it returns one row for each record in a table. SELECT语句是一个循环,其中它为表中的每个记录返回一行。 If you want all the records in the LEADER table all you need do is remove the filter on the NUM column and join that table in the query instead. 如果要让LEADER表中的所有记录,您需要做的就是删除NUM列上的过滤器,然后将该表加入查询中。

So your query should look like this: 因此,您的查询应如下所示:

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 ;

I assume STARTTIME includes a time element, so you need to use TRUNC() to join by just the data element. 我假设STARTTIME包含一个时间元素,所以您需要使用TRUNC()仅通过数据元素进行联接。 Not sure why you have a DISTINCT in your query. 不知道为什么在查询中有DISTINCT。


"STARTTIME column and PEPOPENAME column will have some null lines ... Why are they missing now?" “ STARTTIME列和PEPOPENAME列将包含一些空行……为什么现在缺少它们?”

Your code is subtly different from my version. 您的代码与我的版本略有不同。 Specifically you sort it like this: order by n.num,m.everyDay ; 具体来说,您可以这样排序: order by n.num,m.everyDay ; .

In the documentation for ORDER BY it says: ORDER BY的文档中说:

NULLS LAST is the default for ascending order NULLS LAST是升序的默认值

Because you are sorting by n.num all the lines where that column is null will appear at the bottom of the result set. 因为您n.num排序, n.numn.num null的所有行将出现在结果集的底部。 They are not missing, you just need to keep scrolling . 它们并没有丢失,您只需要继续滚动即可

Alternatively change the sort order. 或者,更改排序顺序。 Revert to the one I used, or else order by n.num,m.everyDay NULLS FIRST ; 恢复为我使用order by n.num,m.everyDay NULLS FIRST ;否则order by n.num,m.everyDay NULLS FIRST ;


Anticipating your question: how to get a null STARTTIME entry for each date that a Leader doesn't work? 预料到您的问题:对于领导者无法工作的每个日期,如何获取空的STARTTIME条目?

The OUTER JOIN gives us one row from the generated calendar for every matching date in TB_DAYWORK plus one row for every date which doesn't match any date in TB_DAYWORK. 对于TB_DAYWORK中的每个匹配日期,OUTER JOIN会从生成的日历中为我们提供一行,对于与TB_DAYWORK中的任何日期都不匹配的每个日期,为我们提供一行。 But, as you say, we lose the visual cue of a blank STARTTIME for each Leader. 但是,正如您所说,我们失去了每个领导者空白的开始时间的视觉提示。

To get it back we need to generate a complete set of rows from the calendar for each Leader. 为了找回它,我们需要从日历中为每个领导者生成一整套行。 To do this we need to CROSS JOIN the calendar sub-query with the LEADER table. 为此,我们需要将日历子查询与LEADER表交叉联接。 Left-joining TB_DAYWORK to that Cartesian product will provide the visual cue you want. 将该笛卡尔积与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 ;

Caveat about untested code still applies, 关于未经测试的代码的警告仍然适用,

the answer is here: 答案在这里:

need to build a temp table with full date and peoplename, and then do the left join to get the empties. 需要建立一个包含完整日期和人名的临时表,然后执行左联接以获取空容器。

    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 ;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM