使用PL / SQL Cursor返回报告工具

时间:2017-04-14 17:06:35

标签: oracle plsql reporting toad toad-data-point

我是一名报告撰稿人。我的查询通常来自某些来源的SELECT语句(在本例中为Oracle Exadata),它从WHERE中的一个或多个表,连接,过滤器中选择,进一步过滤HAVING中的组等。我通常只拥有对任何来源的READ权限。 ; m连接到。所以,我无法创建sprocs,包或函数。

我需要在复杂查询上使用高级功能,使用FOR..LOOPs,IF THEN ELSE结构等来优化派生结果集,以便输出到报告工具(如Qlikview或Tableau)。

随着我的学习,我在复杂的逻辑结构中找到了许多使用游标的课程,但每节课都使用dbms_output.put_line作为每次迭代的结果。输出最终在缓冲区中,而不是结果集。我已经了解到SYS_REFCURSOR是指向结果集的指针。听起来很有希望,但所有例子都以创建或替换程序/包装开始。

我的理解存在差距,如果我解释了我期望的结果,我希望你能填补这些空白:

我有一个复杂的查询,它通过UNION合成多个SELECT语句,在Join和Where子句中使用子选择等。它返回一个几乎完整的结果集,但我需要在发送之前进一步完善它结果设置为报告工具。

我可以:

DECLARE
  CURSOR current_Schedule IS
  SELECT --insert complex query here-- ;

  row_Schedule  cur_Schedule%ROWTYPE;

BEGIN
  IF NOT current_Schedule%ISOPEN THEN
    OPEN current_Schedule;
  END IF;

  LOOP
    FETCH current_Schedule INTO row_Schedule;
    EXIT WHEN current_Schedule%NOTFOUND;
    --Here's where I think my question is....
    dbms_output.put_line('row_Schedule.Login ' || row_Schedule.Code)
  END LOOP;

EXCEPTION --put exception clause here

END;

这可以验证我可以迭代复杂查询,更新,过滤等结果的概念,但问题是......

它输出到缓冲区,而不是结果集。

我可能误解了一些PL / SQL概念。我在这里找到的所有研究实例和其他来源都假设用户能够创建函数,包等。

我仅限于将此作为脚本从具有源的只读权限的QlikView ODBC连接启动。

考虑到我的限制,是否有解决方案?

感谢您花时间阅读本文,并感谢您能否提供一致的答案。

MFC

编辑:附加信息

我正在使用劳动力管理模式,进行非规范化并将其存放在Exadata数据湖中。我有2个表DET_SEG(详细段)和GEN_SEG(通用段)。 DET_SEG包含员工和段代码表,NOM_DATE,START MOMENT和STOP MOMENT的fk&#39。代码有两种类型,Additive和Subtractive。添加剂是指示员工被分配到的业务单位的工作代码,减法代码是雇员从工作中获取的任何类型的代码,例如Br​​eak,Lunch,Sick等。所有代码都绑定到在30分钟生成时间间隔的表。由于代码可能占用一个完整或部分间隔,并且因为它们可以跨越多个间隔,我最终得到了6个独特的用例来解决:以前开始的代码,占用整个并且在后续Interval中结束是一个。之前开始,结束的代码是另一个。 你可以想象其余的,我不会迭代它们。最终输出生成一个标题为" Minutes"表示添加或减法代码在区间中占用的时间。

我最终创建了6个独特的SELECT块UNION,以输出倒数第二个解决方案。这是一个较短的SELECT的例子:

SELECT 
  cal.GlobalIntervals AS G_Intervals,
  mainstart.startmoment AS StartMoment,
  mainstart.stopmoment AS StopMoment,
  mainstart.code as Code,
  mainstart.ID AS Login,
  ((case mainstart.code
        when 'BRK1' then round(24*60*(mainstart.startmoment -mainstart.stopmoment),0)
        when 'BRK2' then round(24*60*(mainstart.startmoment - mainstart.stopmoment),0)
        when 'BRK3' then round(24*60*(mainstart.startmoment - mainstart.stopmoment),0)
        else round(24*60*(mainstart.startmoment - mainstart.stopmoment),0)
  end)) as Minutes  
from
(
SELECT
    (
    to_date(to_char((
    SELECT CLNDR_DT
    FROM COMMOBJ.DIM_DATE
    where CLNDR_DT = trunc(sysdate)
    ), 'dd-mon-yyyy') || HOUR_24_LABEL_CD, 'dd-mon-yyyy hh24:mi:ss')
    ) as GlobalIntervals
FROM    COMMOBJ.DIM_TIME t
WHERE   second_nbr = 0
AND     mod(minute_nbr, 30) = 0 
) cal
left outer join
(
  SELECT DISTINCT
        New_time(to_date(30121899,'ddMMyyyy')+EWFM.DET_SEG.START_MOMENT/1440,'GMT','PST') as StartMoment,
    New_time(to_date(30121899,'ddMMyyyy')+EWFM.DET_SEG.STOP_MOMENT/1440,'GMT','PST') as StopMoment,
    SEG_CODE.CODE,
    SEG_CODE.DESCR,
    EMP.ID,
    EMP.LAST_NAME,
    EMP.FIRST_NAME 
FROM EWFM.DET_SEG DET_SEG
    INNER JOIN EWFM.SEG_CODE SEG_CODE ON (DET_SEG.SEG_CODE_SK = SEG_CODE.SEG_CODE_SK)
    INNER JOIN EWFM.EMP EMP ON (DET_SEG.EMP_SK = EMP.EMP_SK)
    INNER JOIN SBCG_ADHOC.SBCG_STAFF SBCG_STAFF ON (EMP.ID = SBCG_STAFF.AGENT_LOGIN)
    WHERE trunc (New_time(to_date(30121899,'ddMMyyyy')+EWFM.DET_SEG.START_MOMENT/1440,'GMT','PST')) =  trunc(sysdate)  
    and EWFM.SEG_CODE.CRNT_REC_IND = 'Y'
    and SEG_CODE.CODE NOT IN ('SHIFT', 'CCBOUT', 'HOLIDA', 'LIND', 'PCHT', 'PIND', 'PXTR', 'LCHT', 'LXTR', 'PSVC', 'HV Saves Inbound' )
  )mainstart
  --StartMoment occurs AFTER Current Interval
  on  cal.GlobalIntervals < mainstart.StartMoment 
  --StopMoment occurs on Next Interval 
  and (cal.GlobalIntervals + (1/24/60 * 30)) = mainstart.StopMoment 
  --StartMoment occurs prior to Next Interval
  and mainstart.StartMoment < (cal.GlobalIntervals + (1/24/60 * 30))

输出如下:

G_INTERVALS | STARTMOMENT | STOPMOMENT |代码|登录| MINUTES
4/17/17 12:00 | 2/17/17 12:00 | 2/17/17 12:15 | BRK1 | ABC123 | -15
4/17/17 12:00 | 2/17/17 12:00 | 2/17/17 08:00 | SHIFT | XYZ321 | 30个
4/17/17 12:30 | 2/17/17 12:45 | 2/17/17 01:45 |午餐| LK4567 | -15

Minutes列的Case语句随每个Use Case的变化以及最终Join中的ON条件而变化。虽然可能不是最有效的,但它会生成一个几乎可以用于收缩报告的数据集。

最终条件(以及此帖子的原因)是GEN_SEG表。它包含Day Long事件的信息,例如Planned Sick(SICKPL),unplanned Sick(SICKUP)等。它只有一个日期,没有START或STOP时刻。这是我正在寻找进一步改进的地方。使用Cursor迭代Result-Set和GEN_SEG中找到的每个Login,并且对于每个值&lt; 0,将其更改为0,以便将与减法代码关联的负值清零。

最终,我们希望在劳动力中发现收缩。员工安排只是第一步。一旦计划内和计划外缩减的员工时间表得到改进,我将引入实际值进一步细化。

我怀疑我写过一本简短的小说,但我希望我已经包含了足够的细节来解释是否需要进行二级细化。如果您有任何想法或批评,我很感激。

干杯〜!

1 个答案:

答案 0 :(得分:0)

在PL / SQL块中,您正在使用光标,因此没有任何内容可以返回给客户端。

如果您使用的是Oracle 12c,则可以从匿名块返回游标(请参阅implicit result sets),但是您将返回SQL而无需程序控制。

你可能想要的是pipelined function,它实际上为你提供了程序性视图。但是,您无法从只读帐户执行此操作,因为您需要创建函数及其返回的集合类型。

你可以用SQL做很多事情。我有兴趣知道哪些报告要求需要程序逻辑。