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

groupbyを使用したSQLFIFOクエリ

    オプション1

    これはおそらくPL/SQLの仕事です。出力するデータ型から始めます:

    CREATE TYPE supply_details_obj AS OBJECT(
      product_id  NUMBER,
      quantity    NUMBER,
      supplier_id NUMBER,
      customer_id NUMBER
    );
    
    CREATE TYPE supply_details_tab AS TABLE OF supply_details_obj;
    

    次に、パイプライン関数を定義して、INVENTORY_INを読み取ることができます。 およびINVENTORY_OUT 一度に1行ずつテーブルを作成し、2つをマージして、残りの在庫または供給量の現在の合計を維持します。

    CREATE FUNCTION assign_suppliers_to_customers (
      i_product_id IN INVENTORY_IN.PRODUCT_ID%TYPE
    )
    RETURN supply_details_tab PIPELINED
    IS
      v_supplier_id  INVENTORY_IN.SUPPLIER_ID%TYPE;
      v_customer_id  INVENTORY_OUT.CUSTOMER_ID%TYPE;
      v_quantity_in  INVENTORY_IN.IN_QUANTITY%TYPE   := NULL;
      v_quantity_out INVENTORY_OUT.OUT_QUANTITY%TYPE := NULL;
      v_cur_in       SYS_REFCURSOR;
      v_cur_out      SYS_REFCURSOR;
    BEGIN
      OPEN v_cur_in FOR
        SELECT in_quantity, supplier_id
        FROM   INVENTORY_IN
        WHERE  product_id = i_product_id
        ORDER BY inv_timestamp;
    
      OPEN v_cur_out FOR
        SELECT out_quantity, customer_id
        FROM   INVENTORY_OUT
        WHERE  product_id = i_product_id
        ORDER BY inv_timestamp;
    
      LOOP
        IF v_quantity_in IS NULL THEN
          FETCH v_cur_in INTO v_quantity_in, v_supplier_id;
          IF v_cur_in%NOTFOUND THEN
            v_supplier_id := NULL;
          END IF;
        END IF;
        IF v_quantity_out IS NULL THEN
          FETCH v_cur_out INTO v_quantity_out, v_customer_id;
          IF v_cur_out%NOTFOUND THEN
            v_customer_id := NULL;
          END IF;
        END IF;
    
        EXIT WHEN v_cur_in%NOTFOUND AND v_cur_out%NOTFOUND;
    
        IF v_quantity_in > v_quantity_out THEN
          PIPE ROW(
            supply_details_obj(
              i_product_id,
              v_quantity_out,
              v_supplier_id,
              v_customer_id
            )
          );
          v_quantity_in  := v_quantity_in - v_quantity_out;
          v_quantity_out := NULL;
        ELSE
          PIPE ROW(
            supply_details_obj(
              i_product_id,
              v_quantity_in,
              v_supplier_id,
              v_customer_id
            )
          );
          v_quantity_out := v_quantity_out - v_quantity_in;
          v_quantity_in  := NULL;
        END IF;
      END LOOP;
    END;
    /
    

    次に、サンプルデータの場合:

    CREATE TABLE INVENTORY_IN ( ID, INV_TIMESTAMP, PRODUCT_ID, IN_QUANTITY, SUPPLIER_ID ) AS
    SELECT 0, TIMESTAMP '2021-03-09 00:00:00', 101,  20, 0 FROM DUAL UNION ALL
    SELECT 1, TIMESTAMP '2021-03-10 01:00:00', 101, 100, 4 FROM DUAL UNION ALL
    SELECT 2, TIMESTAMP '2021-03-11 02:00:00', 101,  50, 3 FROM DUAL UNION ALL
    SELECT 3, TIMESTAMP '2021-03-14 01:00:00', 101,  10, 2 FROM DUAL;
    
    CREATE TABLE INVENTORY_OUT ( ID, INV_TIMESTAMP, PRODUCT_ID, OUT_QUANTITY, CUSTOMER_ID ) AS
    SELECT 1, TIMESTAMP '2021-03-10 02:00:00', 101, 30, 1 FROM DUAL UNION ALL
    SELECT 2, TIMESTAMP '2021-03-11 01:00:00', 101, 40, 2 FROM DUAL UNION ALL
    SELECT 3, TIMESTAMP '2021-03-12 01:00:00', 101, 80, 1 FROM DUAL;
    

    クエリ:

    SELECT product_id,
           supplier_id,
           customer_id,
           SUM( quantity ) AS quantity
    FROM   TABLE( assign_suppliers_to_customers( 101 ) )
    GROUP BY
           product_id,
           supplier_id,
           customer_id
    ORDER BY
           MIN( inv_timestamp )
    

    出力:

    オプション2

    (非常に)複雑なSQLクエリ:

    WITH in_totals ( ID, INV_TIMESTAMP, PRODUCT_ID, IN_QUANTITY, SUPPLIER_ID, TOTAL_QUANTITY ) AS (
      SELECT i.*,
             SUM( in_quantity ) OVER ( PARTITION BY product_id ORDER BY inv_timestamp )
      FROM   inventory_in i
    ),
    out_totals ( ID, INV_TIMESTAMP, PRODUCT_ID, OUT_QUANTITY, CUSTOMER_ID, TOTAL_QUANTITY ) AS (
      SELECT o.*,
             SUM( out_quantity ) OVER ( PARTITION BY product_id ORDER BY inv_timestamp )
      FROM   inventory_out o
    ),
    split_totals ( product_id, inv_timestamp, supplier_id, customer_id, quantity ) AS (
      SELECT i.product_id,
             MIN( COALESCE( LEAST( i.inv_timestamp, o.inv_timestamp ), i.inv_timestamp ) )
               AS inv_timestamp,
             i.supplier_id,
             o.customer_id,
             SUM(
               COALESCE(
                 LEAST(
                   i.total_quantity - o.total_quantity + o.out_quantity,
                   o.total_quantity - i.total_quantity + i.in_quantity,
                   i.in_quantity,
                   o.out_quantity
                 ),
                 0
               )
             )
      FROM   in_totals i
             LEFT OUTER JOIN
             out_totals o
             ON (   i.product_id = o.product_id
                AND i.total_quantity - i.in_quantity <= o.total_quantity
                AND i.total_quantity >= o.total_quantity - o.out_quantity )
      GROUP BY
             i.product_id,
             i.supplier_id,
             o.customer_id
      ORDER BY
             inv_timestamp
    ),
    missing_totals ( product_id, inv_timestamp, supplier_id, customer_id, quantity ) AS (
      SELECT i.product_id,
             i.inv_timestamp,
             i.supplier_id,
             NULL,
             i.in_quantity - COALESCE( s.quantity, 0 )
      FROM   inventory_in i
             INNER JOIN (
               SELECT product_id,
                      supplier_id,
                      SUM( quantity ) AS quantity
               FROM   split_totals
               GROUP BY product_id, supplier_id
             ) s
             ON (   i.product_id = s.product_id
                AND i.supplier_id = s.supplier_id )
      ORDER BY i.inv_timestamp
    )
    SELECT product_id, supplier_id, customer_id, quantity
    FROM   (
      SELECT product_id, inv_timestamp, supplier_id, customer_id, quantity
      FROM   split_totals
      WHERE  quantity > 0
      UNION ALL
      SELECT product_id, inv_timestamp, supplier_id, customer_id, quantity
      FROM   missing_totals
      WHERE  quantity > 0
      ORDER BY inv_timestamp
    );
    

    上記のサンプルデータの場合、次のように出力されます。

    db <> fiddle こちら



    1. Java Class.forName、JDBC接続ローディングドライバー

    2. mybatisに最後に挿入されたレコードのIDを取得します

    3. MySQLギャップロック

    4. MySQL5.6で生成された列の代替手段は何ですか