1

我是一名报告作家。我的查询通常是来自某个源(在本例中为 Oracle Exadata)的 SELECT 语句,这些语句从一个或多个表中进行选择、连接、WHERE 中的过滤器、HAVING 中的进一步过滤器组等。我通常对我的任何来源都有只读权限连接到。所以,我不能创建存储过程、包或函数。

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

在我学习的过程中,我发现了很多关于在复杂逻辑结构中使用游标的课程,但每节课都使用 dbms_output.put_line 作为每次迭代的结果。输出最终在缓冲区中,而不是在结果集中。我了解到 SYS_REFCURSOR 是指向结果集的指针。听起来很有希望,但所有示例都以 CREATE OR REPLACE PROCEDURE/PACKAGE 开头。

如果我解释我想要的结果是什么,我希望你能填补我的理解中的空白:

我有一个复杂的查询,它通过 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 包含 Employee 和 Segment Code 表、NOM_DATE、START MOMENT 和 STOP MOMENT 的 fk。代码有 2 种类型,加法和减法。加法是表示员工被分配到的业务单位的工作代码,减法代码是让员工下班的任何类型的代码,例如休息、午餐、生病等。所有代码都与一个表格相关联,该表格生成 30 分钟的时间间隔。由于代码可以占据一个完整或部分区间,并且它们可以跨越多个区间,我最终得到了 6 个独特的用例来解决:之前开始的代码,占据整个区间并在后续区间结束。之前开始的代码,内部是另一个。你可以想象其余的,我不会遍历它们。最终输出生成一个标题为“分钟”的计算列,指示加法或减法代码在间隔中占用的时间量。

我最终创建了 6 个独特的 SELECT 块 UNION'ed 一起输出倒数第二个解决方案。以下是较短 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 | 开始时刻 | 停止| 代码 | 登录 | 分钟
2017 年 4 月 17 日 12:00 | 2/17/17 12:00 | 2/17/17 12:15 | BRK1 | ABC123 | -15
17 年 4 月 17 日 12:00 | 2/17/17 12:00 | 2/17/17 08:00 | 移位 | XYZ321 | 30 17 年 4 月
17 日 12:30 | 2/17/17 12:45 | 2/17/17 01:45 | 午餐 | LK4567 | -15

每个用例的 Minutes 列的 Case 语句会发生变化,最终联接中的 ON 条件也会发生变化。虽然可能不是最有效的,但它生成的数据集几乎可以用于收缩报告

最后一个条件(以及这篇文章的原因)是 GEN_SEG 表。它包含 Day Long 事件的信息,例如 Planned Sick (SICKPL)、unplanned Sick (SICKUP) 等。它只有一个日期,没有 START 或 STOP 时刻。这是我寻求进一步改进的地方。使用游标遍历结果集和在 GEN_SEG 中找到的每个登录,对于每个 <0 的值,将其更改为 0 以将与减法代码关联的负值清零。

最终,我们希望发现劳动力中的收缩。员工时间表只是第一步。一旦包含计划内和计划外收缩的员工计划被细化,我将引入实际数据以进行进一步细化。

我怀疑我写了一部短篇小说,但我希望我已经包含了足够的细节来解释对二级细化的需要。如果您有任何想法或批评,我将不胜感激。

加油~!

4

1 回答 1

0

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

如果您使用的是 Oracle 12c(请参阅隐式结果集),则可以从匿名块返回游标,但随后您将返回到没有过程控制的 SQL。

您可能想要的是一个流水线函数,它实际上为您提供了一个程序视图。但是,您不能从只读帐户执行此操作,因为您需要创建函数及其返回的集合类型。

不过,您可以使用 SQL 做很多事情。我很想知道什么报告要求需要程序逻辑。

于 2017-04-15T10:22:34.287 回答