【问题标题】:Oracle converting a working query to a subqueryOracle 将工作查询转换为子查询
【发布时间】:2021-08-06 13:09:12
【问题描述】:

我在下面有一个测试用例,它工作正常。在第一个场景中,我重复使用 TRUC(access_date) 以便分组正常工作。

我想只使用一次子查询来执行 TRUNC(access_date) 以使代码更清晰,并且只在一个地方执行 TRUNC 命令,但我很难让它工作。

谁能告诉我如何解决这个问题。我提供了工作版本和我的尝试,但我无法工作。

 ALTER SESSION SET NLS_DATE_FORMAT = 'MMDDYYYY HH24:MI:SS';

    CREATE OR REPLACE TYPE nt_date IS TABLE OF DATE;
    /

    CREATE OR REPLACE FUNCTION generate_dates_pipelined(
     p_from IN DATE,
     p_to   IN DATE
   )
   RETURN nt_date  PIPELINED     DETERMINISTIC
   IS
      v_start DATE := TRUNC(LEAST(p_from, p_to));
     v_end   DATE := TRUNC(GREATEST(p_from, p_to));
    BEGIN
    LOOP
        PIPE ROW (v_start);
        EXIT WHEN v_start >= v_end;
        v_start := v_start + INTERVAL '1' DAY;
      END LOOP;
      RETURN;
    END  generate_dates_pipelined;
    /

    create table employees(
      employee_id NUMBER(6), 
      first_name VARCHAR2(20),
     last_name VARCHAR2(20),
     card_num VARCHAR2(10),
     work_days VARCHAR2(7)
    );

   ALTER TABLE employees
  ADD (
     CONSTRAINT employees_pk PRIMARY KEY (employee_id)
  );

   INSERT INTO employees (
     EMPLOYEE_ID,
     first_name, 
     last_name,
     card_num,
    work_days
   )
   WITH names AS ( 
     SELECT 1, 'Jane',     'Doe',      'F123456', 'NYYYYYN'   FROM dual UNION ALL 
      SELECT 2, 'Madison', 'Smith', 'R33432','NYYYYYN' FROM dual UNION ALL 
      SELECT 3, 'Justin',   'Case',     'C765341','NYYYYYN' FROM dual UNION ALL 
     SELECT 4, 'Mike',     'Jones',      'D564311','NYYYYYN' FROM dual  )
   SELECT * FROM names;  

   CREATE TABLE locations AS
     SELECT level AS location_id,
      'Door ' || level AS location_name,
      CASE round(dbms_random.value(1,3)) 
          WHEN 1 THEN 'A' 
          WHEN 2 THEN 'T' 
          WHEN 3 THEN 'T' 
      END AS location_type
    FROM   dual
    CONNECT BY level <= 3;


   ALTER TABLE locations 
     ADD ( CONSTRAINT locations_pk
     PRIMARY KEY (location_id));

    create table access_history(     
       seq_num integer  GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
      employee_id NUMBER(6), 
      card_num varchar2(10),
      location_id number(4),
       access_date date,
       processed NUMBER(1) default 0
   );

   create or replace procedure create_access_history(p_start_date date, p_end_date date)
    IS
    
    BEGIN
    INSERT into    access_history
     (
      employee_id,
      card_num,
       location_id, 
       access_date
     )
             WITH  cntr  AS
    (
 SELECT LEVEL - 1 AS n
  FROM     dual
  CONNECT BY LEVEL <= 15 -- Max number of rows per employee per date
    )
    ,            got_location_num  AS
    (
SELECT  location_id
,    ROW_NUMBER () OVER (ORDER BY location_id) AS location_num
,    COUNT (*)   OVER ()            AS max_location_num
FROM     locations
    )
   ,    employee_days  AS
    (
    SELECT         e.employee_id, e.card_num
,      d.column_value         AS access_date
,          dbms_random.value (0, 15) AS rn    -- 0 to max number of rows per employee per date
    FROM     employees e
      CROSS JOIN TABLE (generate_dates_pipelined (p_start_date, p_end_date)) d
       )
   ,               employee_n_days  AS
   (
       SELECT        ed.employee_id, ed.card_num, ed.access_date
,     dbms_random.value (0, 1) AS lrn
    FROM     employee_days        ed
     JOIN    cntr            c  ON  c.n <= ed.rn
  )
      SELECT       n.employee_id,       n.card_num, l.location_id, 
      n.access_date +                            NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(0,86399)), 'SECOND') 
      FROM     employee_n_days  n
    JOIN         got_location_num  l  ON l.location_num = CEIL (n.lrn * l.max_location_num); 
   END;

      EXEC  create_access_history (DATE '2021-08-06',  DATE '2021-08-11');

    -- Works 
    select TRUNC(a. access_date) access_date,
   e.employee_id, 
   count(*) cnt         
    FROM  employees    e
    JOIN  access_history a ON a.employee_id = e.employee_id
    JOIN  locations     l ON l.location_id = a.location_id
    GROUP BY  GROUPING SETS (
    (TRUNC(a.access_date), e.employee_id),       
   (trunc(a.access_date)),
     ()
   )
   ORDER BY trunc(a. access_date),e.employee_id;

固定

选择
访问日期, 员工ID, 数数(*) 从 (
选择 TRUNC(a.access_date) 作为 access_date, e.employee_id 来自员工 e JOIN access_history a ON a.employee_id = e.employee_id

加入位置 l ON l.location_id = a.location_id ) ah_trunc 按分组集分组( (access_date, employee_id),
(访问日期), () ) ORDER BY access_date,employee_id;

【问题讨论】:

  • 我并没有完全理解,但是在您的最终查询中,ah_trunc 有两列名为"TRUNC(a.access_date)"employee_id,这就是为什么您不能从中选择access_date 的原因(如果这就是“不起作用”的意思)。尝试给第一个别名。
  • 好的,所以如果您按照我的建议修复错字(JOIN locations l. 之后的杂散点)和别名 TRUNC(a.access_date),则您的原始示例有效。我不确定你需要 150 行代码来演示它。

标签: sql oracle grouping


【解决方案1】:

如果我没看错的话,问题似乎可以简化为以下内容。

为什么这个查询会因为“invalid identifier”而失败?

select dummy
from   ( select lower(dummy) from dual );
ERROR at line 1:  
ORA-00904: "DUMMY": invalid identifier

这是因为内联视图只有一个名为"LOWER(DUMMY)" 的列。你要么需要引用它(用双引号完成),要么给它一个名字:

select dummy
from   ( select lower(dummy) as dummy from dual );
DUMMY
------
x

1 row selected.

【讨论】:

  • 我在 subqery 中尝试了以下操作,但没有成功,选择 TRUNC(a.access_date) 作为 access_date, ... 似乎是其他问题,或者我误解了您的回复。还有其他建议吗?感谢回复
  • 当我尝试使用别名时,我收到以下错误 ORA-00905:缺少关键字。如果你运行我的测试用例,你可以看到问题
猜你喜欢
  • 1970-01-01
  • 2019-07-29
  • 2018-11-01
  • 2013-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-12
  • 2016-07-29
相关资源
最近更新 更多