用于分配的 Oracle 游标 [英] Oracle cursor for assignation

查看:39
本文介绍了用于分配的 Oracle 游标的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下 2 个表:

t1

<头>
VKAYANR
VK1A1、A21
VK22
VK3A1、A2、A3、A43
VK4A24
VK55
VK66
VK7A37
VK88
VK99
VK1010
VK1111
VK1212
VK13A313
VK1414
VK15A315
VK1616
VK1717
VK1818
VK1919
VK2020

t2

<头>
AC
A14
A210
A32
A410

我想创建一个 plsql 游标来添加一个具有以下逻辑的列 AF:

  1. 目标是将 t2 中的不同 As 分配给 t1 中的行.R 列决定了它们的分配顺序(如果可能).
  2. 如果填充了 AY 列,那么如果 t2 中的 C 仍然 > 0(在之前的分配之后),则应将A"分配给 AF.
  3. 如果填充了 AN 列,则此处列出的任何A"都不能分配给该行.
  4. 'A' 的分配必须与其在 t2 中的 C 成比例.

以下是所需输出的一个示例(尽管可以按任何其他顺序分配 AF).

<头>
VKAYANRAF解释
VK1A1、A21A4A3 和 A4 可用:选择 A4.
VK22A2所有可用:选择 A2.
VK3A1、A2、A3、A43无可用:不能选择任何.
VK4A24A4除 A2 外的所有可用:选择 A4
VK55A2所有可用:选择 A2.
VK66A1所有可用:选择 A1.
VK7A37A3必须分配A3:选择A3
VK88A2所有可用:选择 A2.
VK99A4所有可用:选择 A4.
VK1010A4所有可用:选择 A4.
VK1111A2所有可用:选择 A2.
VK1212A1所有可用:选择 A1.
VK13A313A3必须分配A3:选择A3
VK1414A2所有可用:选择 A2.
VK15A315必须分配A3但A3已用完:无法分配
VK1616A4所有可用:选择 A4.
VK1717A4所有可用:选择 A4.
VK1818A2所有可用:选择 A2.
VK1919A2所有可用:选择 A2.
VK2020A1所有可用:选择 A1.

请在下面找到表的示例查询:

with t1 (vk, ay, an, r) as(select 'VK1', null, 'A1, A2', 1 from dual union allselect 'VK2', null, null, 2 from dual union allselect 'VK3', null, 'A1, A2, A3, A4', 3 from dual union allselect 'VK4', null, 'A2', 4 from dual union allselect 'VK5', null, null, 5 from dual union allselect 'VK6', null, null, 6 from dual union allselect 'VK7', 'A3', null, 7 from dual union allselect 'VK8', null, null, 8 from dual union allselect 'VK9', null, null, 9 from dual union allselect 'VK10', null, null, 10 from dual union allselect 'VK11', null, null, 11 from dual union allselect 'VK12', null, null, 12 from dual union allselect 'VK13', 'A3', null, 13 from dual union allselect 'VK14', null, null, 14 from dual union allselect 'VK15', 'A3', null, 15 from dual union allselect 'VK16', null, null, 16 from dual union allselect 'VK17', null, null, 17 from dual union allselect 'VK18', null, null, 18 from dual union allselect 'VK19', null, null, 19 from dual union allselect 'VK20', null, null, 20 from dual),t2 (a, c) 为 (选择 'A1', 4 from dual union all选择 'A2', 10 from dual union all选择 'A3', 2 from dual union all选择A4",双选 10)从 t1 中选择 *;

解决方案

设置数据类型和辅助函数:

CREATE TYPE string_list IS TABLE OF VARCHAR2(2);/创建类型 int_list 是 INT 表;/创建类型 t1_data 作为对象(vk VARCHAR2(4),是 VARCHAR2(2),一个 VARCHAR2(14),INT,c VARCHAR2(2));/创建类型 t1_table 是 t1_data 表;/创建或替换函数 split_String(i_str IN VARCHAR2,i_delim IN VARCHAR2 DEFAULT ',') 返回 string_list DETERMINISTIC作为p_result string_list := string_list();p_start NUMBER(5) := 1;p_end NUMBER(5);c_len 常数 (5) := LENGTH( i_str );c_ld 常数 (5) := LENGTH( i_delim );开始-- https://stackoverflow.com/a/35577315/1509264-- 许可证:CC BY-SA 4.0如果 c_len >0 那么p_end := INSTR( i_str, i_delim, p_start );而 p_end >0 循环p_result.EXTEND;p_result( p_result.COUNT ) := SUBSTR( i_str, p_start, p_end - p_start );p_start := p_end + c_ld;p_end := INSTR( i_str, i_delim, p_start );结束循环;如果 p_start <= c_len + 1 THENp_result.EXTEND;p_result( p_result.COUNT ) := SUBSTR( i_str, p_start, c_len - p_start + 1 );万一;万一;返回 p_result;结尾;/

然后就可以使用函数了:

CREATE FUNCTION pick_values RETURN t1_table PIPELINED是TYPE t2_type 是 t2%ROWTYPE 表;t2_data t2_type;开始-- https://stackoverflow.com/a/67398434/1509264-- 许可证:CC BY-SA 4.0选择 *批量收集到 t2_data从 t2;FOR cur IN ( SELECT * FROM t1 ORDER BY r )环形宣布a_freqs INT_LIST := INT_LIST();cum_freq INT := 0;采用 STRING_LIST := SPLIT_STRING( cur.an, ', ' );idx INT;c T2.A%TYPE;开始a_freqs.EXTEND(t2_data.COUNT);FOR i IN 1 .. t2_data.COUNT 循环IF ( t2_data(i).a = cur.ay AND t2_data(i).c > 0 )或( cur.ay 为 NULL AND t2_data(i).a NOT MEMBER OF Taked AND t2_data(i).c > 0 )然后a_freqs(i) := cum_freq + t2_data(i).c;cum_freq := cum_freq + t2_data(i).c;别的a_freqs(i) := cum_freq;万一;结束循环;如果 cum_freq >0 那么idx := FLOOR(DBMS_RANDOM.VALUE(0, cum_freq));FOR i IN 1 .. t2_data.COUNT 循环如果idx<a_freqs(i) THENc := t2_data(i).a;t2_data(i).c := t2_data(i).c - 1;出口;万一;结束循环;万一;管排(t1_data(cur.vk、cur.ay、cur.an、cur.r、c));结尾;结束循环;结尾;/

对于您的示例数据:

CREATE TABLE t1 (vk, ay, an, r) asselect 'VK1', null, 'A1, A2', 1 from dual union allselect 'VK2', null, null, 2 from dual union allselect 'VK3', null, 'A1, A2, A3, A4', 3 from dual union allselect 'VK4', null, 'A2', 4 from dual union allselect 'VK5', null, null, 5 from dual union allselect 'VK6', null, null, 6 from dual union allselect 'VK7', 'A3', null, 7 from dual union allselect 'VK8', null, null, 8 from dual union allselect 'VK9', null, null, 9 from dual union allselect 'VK10', null, null, 10 from dual union allselect 'VK11', null, null, 11 from dual union allselect 'VK12', null, null, 12 from dual union allselect 'VK13', 'A3', null, 13 from dual union allselect 'VK14', null, null, 14 from dual union allselect 'VK15', 'A3', null, 15 from dual union allselect 'VK16', null, null, 16 from dual union allselect 'VK17', null, null, 17 from dual union allselect 'VK18', null, null, 18 from dual union allselect 'VK19', null, null, 19 from dual union all选择 'VK20', null, null, 20 from dual;创建表 t2 (a, c) 作为选择 'A1', 4 from dual union all选择 'A2', 10 from dual union all选择 'A3', 2 from dual union all选择'A4', 10 from dual;

那么:

SELECT * FROM PICK_VALUES();

可能输出:

<块引用>

<头>
VKAYANRC
VK1A1、A21A4
VK22A2
VK3A1、A2、A3、A43
VK4A24A1
VK55A2
VK66A2
VK7A37A3
VK88A1
VK99A2
VK1010A4
VK1111A2
VK1212A1
VK13A313A3
VK1414A4
VK15A315
VK1616A4
VK1717A2
VK1818A4
VK1919A4
VK2020A4

db<>fiddle 这里

I have the below 2 tables:

t1

VK AY AN R
VK1 A1, A2 1
VK2 2
VK3 A1, A2, A3, A4 3
VK4 A2 4
VK5 5
VK6 6
VK7 A3 7
VK8 8
VK9 9
VK10 10
VK11 11
VK12 12
VK13 A3 13
VK14 14
VK15 A3 15
VK16 16
VK17 17
VK18 18
VK19 19
VK20 20

t2

A C
A1 4
A2 10
A3 2
A4 10

I would like to create a plsql cursor to add a column AF with the logic below:

  1. The objective is to assign the different As in t2 to the rows in t1. It's column R which determines the order in which, if possible, they should be assigned.
  2. If column AY is populated, then that 'A' should be assigned to AF if C in t2 is still >0 (after the previous assignations).
  3. If column AN is populated, then none of the 'A's listed there can be assigned to that row.
  4. The assignation of 'A's must be done proportionally to their C in t2.

The below would be one example of desired output (AF could be assigned in any other order though).

VK AY AN R AF EXPLANATION
VK1 A1, A2 1 A4 A3 and A4 available: A4 chosen.
VK2 2 A2 All available: A2 chosen.
VK3 A1, A2, A3, A4 3 null None are available: cannot pick any.
VK4 A2 4 A4 All but A2 available: A4 chosen
VK5 5 A2 All available: A2 chosen.
VK6 6 A1 All available: A1 chosen.
VK7 A3 7 A3 A3 must be assigned: A3 chosen
VK8 8 A2 All available: A2 chosen.
VK9 9 A4 All available: A4 chosen.
VK10 10 A4 All available: A4 chosen.
VK11 11 A2 All available: A2 chosen.
VK12 12 A1 All available: A1 chosen.
VK13 A3 13 A3 A3 must be assigned: A3 chosen
VK14 14 A2 All available: A2 chosen.
VK15 A3 15 null A3 must be assigned but A3 already exhausted: cannot be assigned
VK16 16 A4 All available: A4 chosen.
VK17 17 A4 All available: A4 chosen.
VK18 18 A2 All available: A2 chosen.
VK19 19 A2 All available: A2 chosen.
VK20 20 A1 All available: A1 chosen.

Please find below the sample query for the tables:

with t1 (vk, ay, an, r) as(
    select 'VK1', null, 'A1, A2', 1 from dual union all
    select 'VK2', null, null, 2 from dual union all
    select 'VK3', null, 'A1, A2, A3, A4', 3 from dual union all
    select 'VK4', null, 'A2', 4 from dual union all
    select 'VK5', null, null, 5 from dual union all
    select 'VK6', null, null, 6 from dual union all
    select 'VK7', 'A3', null, 7 from dual union all
    select 'VK8', null, null, 8 from dual union all
    select 'VK9', null, null, 9 from dual union all
    select 'VK10', null, null, 10 from dual union all
    select 'VK11', null, null, 11 from dual union all
    select 'VK12', null, null, 12 from dual union all
    select 'VK13', 'A3', null, 13 from dual union all
    select 'VK14', null, null, 14 from dual union all
    select 'VK15', 'A3', null, 15 from dual union all
    select 'VK16', null, null, 16 from dual union all
    select 'VK17', null, null, 17 from dual union all
    select 'VK18', null, null, 18 from dual union all
    select 'VK19', null, null, 19 from dual union all
    select 'VK20', null, null, 20 from dual),
t2 (a, c) as (
    select 'A1', 4 from dual union all
    select 'A2', 10 from dual union all
    select 'A3', 2 from dual union all
    select 'A4', 10 from dual)
select * from t1;

解决方案

Set up the data types and helper functions:

CREATE TYPE string_list IS TABLE OF VARCHAR2(2);
/

CREATE TYPE int_list IS TABLE OF INT;
/

CREATE TYPE t1_data AS OBJECT(
  vk VARCHAR2(4),
  ay VARCHAR2(2),
  an VARCHAR2(14),
  r  INT,
  c  VARCHAR2(2)
);
/

CREATE TYPE t1_table IS TABLE OF t1_data;
/

CREATE OR REPLACE FUNCTION split_String(
  i_str    IN  VARCHAR2,
  i_delim  IN  VARCHAR2 DEFAULT ','
) RETURN string_list DETERMINISTIC
AS
  p_result       string_list := string_list();
  p_start        NUMBER(5) := 1;
  p_end          NUMBER(5);
  c_len CONSTANT NUMBER(5) := LENGTH( i_str );
  c_ld  CONSTANT NUMBER(5) := LENGTH( i_delim );
BEGIN
  -- https://stackoverflow.com/a/35577315/1509264
  -- License: CC BY-SA 4.0
  IF c_len > 0 THEN
    p_end := INSTR( i_str, i_delim, p_start );
    WHILE p_end > 0 LOOP
      p_result.EXTEND;
      p_result( p_result.COUNT ) := SUBSTR( i_str, p_start, p_end - p_start );
      p_start := p_end + c_ld;
      p_end := INSTR( i_str, i_delim, p_start );
    END LOOP;
    IF p_start <= c_len + 1 THEN
      p_result.EXTEND;
      p_result( p_result.COUNT ) := SUBSTR( i_str, p_start, c_len - p_start + 1 );
    END IF;
  END IF;
  RETURN p_result;
END;
/

Then you can use the function:

CREATE FUNCTION pick_values RETURN t1_table PIPELINED
IS
  TYPE t2_type IS TABLE OF t2%ROWTYPE;
  
  t2_data t2_type;
BEGIN
  -- https://stackoverflow.com/a/67398434/1509264
  -- License: CC BY-SA 4.0
  SELECT *
  BULK COLLECT INTO t2_data
  FROM  t2;
  
  FOR cur IN ( SELECT * FROM t1 ORDER BY r )
  LOOP
    DECLARE
      a_freqs  INT_LIST := INT_LIST();
      cum_freq INT := 0;
      taken    STRING_LIST := SPLIT_STRING( cur.an, ', ' );
      idx      INT;
      c        T2.A%TYPE;
    BEGIN
      a_freqs.EXTEND(t2_data.COUNT);
      FOR i IN 1 .. t2_data.COUNT LOOP
        IF ( t2_data(i).a = cur.ay AND t2_data(i).c > 0 )
           OR ( cur.ay IS NULL AND t2_data(i).a NOT MEMBER OF taken AND t2_data(i).c > 0 )
        THEN
          a_freqs(i) := cum_freq + t2_data(i).c;
          cum_freq := cum_freq + t2_data(i).c;
        ELSE
          a_freqs(i) := cum_freq;
        END IF;
      END LOOP;
      IF cum_freq > 0 THEN
        idx := FLOOR(DBMS_RANDOM.VALUE(0, cum_freq));
        FOR i IN 1 .. t2_data.COUNT LOOP
          IF idx < a_freqs(i) THEN
            c := t2_data(i).a;
            t2_data(i).c := t2_data(i).c - 1;
            EXIT;
          END IF;
        END LOOP;
      END IF;
      PIPE ROW(
        t1_data(
          cur.vk, cur.ay, cur.an, cur.r, c
        )
      );
    END;
  END LOOP;
END;
/

Which, for your sample data:

CREATE TABLE t1 (vk, ay, an, r) as
    select 'VK1', null, 'A1, A2', 1 from dual union all
    select 'VK2', null, null, 2 from dual union all
    select 'VK3', null, 'A1, A2, A3, A4', 3 from dual union all
    select 'VK4', null, 'A2', 4 from dual union all
    select 'VK5', null, null, 5 from dual union all
    select 'VK6', null, null, 6 from dual union all
    select 'VK7', 'A3', null, 7 from dual union all
    select 'VK8', null, null, 8 from dual union all
    select 'VK9', null, null, 9 from dual union all
    select 'VK10', null, null, 10 from dual union all
    select 'VK11', null, null, 11 from dual union all
    select 'VK12', null, null, 12 from dual union all
    select 'VK13', 'A3', null, 13 from dual union all
    select 'VK14', null, null, 14 from dual union all
    select 'VK15', 'A3', null, 15 from dual union all
    select 'VK16', null, null, 16 from dual union all
    select 'VK17', null, null, 17 from dual union all
    select 'VK18', null, null, 18 from dual union all
    select 'VK19', null, null, 19 from dual union all
    select 'VK20', null, null, 20 from dual;

CREATE TABLE t2 (a, c) as
    select 'A1', 4 from dual union all
    select 'A2', 10 from dual union all
    select 'A3', 2 from dual union all
    select 'A4', 10 from dual;

Then:

SELECT * FROM PICK_VALUES();

May output:

VK AY AN R C
VK1 A1, A2 1 A4
VK2 2 A2
VK3 A1, A2, A3, A4 3
VK4 A2 4 A1
VK5 5 A2
VK6 6 A2
VK7 A3 7 A3
VK8 8 A1
VK9 9 A2
VK10 10 A4
VK11 11 A2
VK12 12 A1
VK13 A3 13 A3
VK14 14 A4
VK15 A3 15
VK16 16 A4
VK17 17 A2
VK18 18 A4
VK19 19 A4
VK20 20 A4

db<>fiddle here

这篇关于用于分配的 Oracle 游标的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆