sql >> データベース >  >> RDS >> Oracle

割り当て用のOracleカーソル

    データ型とヘルパー関数を設定します:

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

    次に、次の関数を使用できます:

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

    サンプルデータの場合:

    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;
    

    次に:

    SELECT * FROM PICK_VALUES();
    

    出力される可能性があります:

    VK AY AN R C VK1A1、A21A4VK22A2VK3A1、A2、A3、A43VK4A24A1VK55A2VK66A2VK7A37A3VK88A1VK99A2VK1010A4VK1111A2VK1212A1VK13A313A3VK1414A4VK15A315

    db<>ここでフィドル




    1. PL / PGSQLからのセッション変数(\ set var ='value')の参照

    2. SQLServerで同じクエリを複数回実行する最も簡単な方法

    3. 行あたり8060バイト、(varchar、nvarchar)値あたり8000バイトの制限にどのように到達しますか?

    4. SQLでビューを作成する方法