Oracle
 sql >> база данни >  >> RDS >> Oracle

SQL FIFO заявка с групиране по

Вариант 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 таблици един ред наведнъж и обединява двата, запазвайки текуща обща сума на оставащия инвентар или количество за доставка:

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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да деактивирам пула на връзките?

  2. Защо имената на таблица/колона/индекс на Oracle са ограничени до 30 знака?

  3. PLS-00103 Грешка в съхранената процедура на Oracle

  4. множествен случай SQL заявка извлича един ред като множество колони

  5. ПО-МАЛКО ИЛИ РАВНО НА В Oracle SQL