Oracle
 sql >> Teknologi Basis Data >  >> RDS >> Oracle

Kueri SQL FIFO dengan grup menurut

Opsi 1

Ini mungkin pekerjaan untuk PL/SQL. Dimulai dengan tipe data yang akan dikeluarkan:

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;

Kemudian kita dapat mendefinisikan fungsi pipelined untuk membaca INVENTORY_IN dan INVENTORY_OUT tabel satu baris pada satu waktu dan gabungkan keduanya dengan menjaga total persediaan atau jumlah yang tersisa untuk dipasok:

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

Kemudian, untuk contoh datanya:

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;

Pertanyaannya:

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 )

Keluaran:

Opsi 2

Permintaan SQL (sangat) rumit:

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

Yang, untuk contoh data di atas, menghasilkan:

db<>fiddle di sini



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Bagaimana saya bisa menghitung hanya nilai NULL di Oracle/PLSQL?

  2. Menanyakan pada EAV SQL Design

  3. menampilkan hasil secara vertikal di oracle

  4. Bagaimana mendefinisikan Prosedur Paket Oracle di H2 untuk Pengujian

  5. Ganti REGEXP_SUBSTR di SQL Server