簡體   English   中英

如何在Oracle SQL中編寫循環語句

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

我每天都需要一個查詢來收集一組特定部門工作人員的簽到狀態信息。

我需要收集的人保存在一個名為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預覽

從圖像中可以看到,對於一個人來說,所有狀態都是通過動態日歷的左聯接找到的。 如果PEOPLENAMESTARTTIME的數據不為空,則表示他們當天已簽入,或者該PEOPLENAME空,則表示他不在。

由於這里需要選擇所有人員,因此它們在表LEADER並按NUM列排序。 我認為應該有一種使用循環語句通過使用一個SQL來收集我們需要的所有人的狀態的方法。 有沒有辦法做到這一點?

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

@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 ;

這是我得到的結果:

[ 修改查詢的結果

everydayleadername完美排序,但是突出顯示,您可以看到日常列缺少數據,此處丟失了9/12和9/13。

我有點困惑,因為據我所知LEFT JOIN將保留所有的數據在左側,如果是右側- STARTTIME柱和PEPOPENAME列將有一些空行,這將是很容易看到(你可以在第一張照片上看到它),這就是我使用它的原因。 他們為什么現在不見了?

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

交叉聯接的結果是這樣的: 交叉聯接

/////////////// ************更新樣本數據************** /////// /////////

這里僅涉及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 (按右上角的第2個按鈕進行下載, 請告訴我此鏈接是否無法達到 )。

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

我已經對自己進行了測試,可以使用這些代碼建立示例表,其中包括今年9月的3個人簽入數據。

您不需要“循環”。 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 ;

ORDER BY的文檔中說:

NULLS LAST是升序的默認值

因為您n.num排序, n.numn.num null的所有行將出現在結果集的底部。 它們並沒有丟失,您只需要繼續滾動即可

或者,更改排序順序。 恢復為我使用order by n.num,m.everyDay NULLS FIRST ;否則order by n.num,m.everyDay NULLS FIRST ;


預料到您的問題:對於領導者無法工作的每個日期,如何獲取空的STARTTIME條目?

對於TB_DAYWORK中的每個匹配日期,OUTER JOIN會從生成的日歷中為我們提供一行,對於與TB_DAYWORK中的任何日期都不匹配的每個日期,為我們提供一行。 但是,正如您所說,我們失去了每個領導者空白的開始時間的視覺提示。

為了找回它,我們需要從日歷中為每個領導者生成一整套行。 為此,我們需要將日歷子查詢與LEADER表交叉聯接。 將該笛卡爾積與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 ;

關於未經測試的代碼的警告仍然適用,

答案在這里:

需要建立一個包含完整日期和人名的臨時表,然后執行左聯接以獲取空容器。

    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