Db2 для i - динамическое предложение FROM - PullRequest
0 голосов
/ 01 сентября 2018

Среда: Db2 для i, версия 7.3

Структура библиотеки / таблицы:

CORPORATE/TENANTS
LIB01/INVOICE
LIB02/INVOICE
LIB03/INVOICE
…
LIBxx/INVOICE

Таблица CORPORATE/TENANTS содержит список библиотек, в которых хранится информация о каждом арендаторе. Имеет такую ​​структуру и данные:

CREATE OR REPLACE TABLE TENANTS (
ID     BIGINT GENERATED ALWAYS AS IDENTITY (START WITH 1),
TENANT CHAR(10) NOT NULL,
PRIMARY KEY(ID)
) RCDFMT TENANTSR;

RUNSQLSTM SRCFILE(HILLB/QDDLSRC) SRCMBR(TENANTS) DFTRDBCOL(CORPORATE)

+--+------+
|ID|TENANT|
+--+------+
| 1|LIB01 |
| 2|LIB02 |
|..|......|
|99|LIB99 |
+--+------+

Все таблицы LIBxx/INVOICE идентичны друг другу и имеют такую ​​структуру:

CREATE OR REPLACE TABLE INVOICE (
ID     BIGINT GENERATED ALWAYS AS IDENTITY (START WITH 1),
PAYDAT INTEGER(6,0) NOT NULL,
AMOUNT DECIMAL(15,2) NOT NULL DEFAULT 0,
PRIMARY KEY(ID)
) RCDFMT INVOICER;

+--+------+------+
|ID|PAYDAT|AMOUNT|
+--+------+------+
| 1|180701|100.00|
| 2|180801| 35.00|
|..|......|......|
+--+------+------+

Я хочу сформировать список сумм счетов для всех арендаторов на указанную дату:

180701 LIB01 100.00
180701 LIB02 140.00
180701 LIB03  74.00
…

Концептуально, что я хочу сделать, это (да, я знаю, что это неверный SQL):

SELECT PAYDAT, TENANT, AMOUNT
FROM $X.INVOICE
WHERE PAYDAT = 180701;

Я хочу получить данные из таблицы INVOICE для каждого TENANT, но я знаю, что предложение FROM не может быть таким динамическим. Я уверен, что у такого запроса есть имя, но я не знаю, что это такое, поэтому я не могу эффективно использовать поисковую систему, чтобы найти то, что мне нужно.

Это было бы тривиально решить с помощью программы RPGLE, но мне нужно решение на чистом SQL.

Обратите внимание - значения LIBxx НЕ МОГУТ быть жестко закодированы. Эти значения могут измениться в любое время.

Ответы [ 2 ]

0 голосов
/ 05 сентября 2018

Чтобы сделать то, что вы хотите, вы можете использовать хранимую процедуру с EXECUTE IMMEDIATE в цикле для построения набора результатов. Как то так:

Примечание: это не полное решение для вырезки и вставки, но вы можете изменить его, чтобы сделать то, что вы хотите.

CREATE OR REPLACE PROCEDURE GETINVOICEAMOUNTS ( ) 
  DYNAMIC RESULT SETS 1 
  LANGUAGE SQL 
  NOT DETERMINISTIC 
  MODIFIES SQL DATA 
  CALLED ON NULL INPUT 
  SET OPTION COMMIT = *NONE
BEGIN
  DECLARE STMT VARCHAR(1024);
  DECLARE RECORD_FOUND INTEGER DEFAULT 1;
  DECLARE LIBRARY CHAR(10);
  DECLARE C1 CURSOR FOR
    SELECT TENANT FROM CORPORATE/TENANT;
  DECLARE C2 CURSOR WITH RETURN FOR 
    SELECT * FROM SESSION.TMP ; 
  DECLARE CONTINUE HANDLER FOR NOT FOUND
    SET RECORD_FOUND = 0;
  DECLARE GLOBAL TEMPORARY TABLE TMP 
    (PAYDAT INTEGER(6,0),
     TENANT CHAR(10),
     AMOUNT DECIMAL(15,2))
    WITH REPLACE;
  OPEN C1;
  LOOP
    FETCH C1 INTO LIBRARY;
    IF RECORD_FOUND = 0;
      LEAVE LOOP;
    END IF;        
    SET STMT = 'INSERT INTO SESSION.TMP SELECT PAYDAT, LIBRARY, AMOUNT FROM ' || RTRIM(LIBRARY) || '.INVOICE WHERE PAYDAT = 180701';
    EXECUTE IMMEDIATE STMT
  END LOOP;
  CLOSE C1;
  OPEN C2; 
END; 

Я дал тебе больше, чем планировал. Но одна конкретная модификация, в которой вы всегда нуждаетесь, заключается в параметризации даты, которую вы хотите получить.

Вот как это работает: Глобальная временная таблица с именем TMP используется для сбора записей, которые будут возвращены в наборе результатов. Как только все записи собраны, курсор открывается над TMP, и процедура заканчивается. Это приводит к тому, что значения, собранные в TMP, возвращаются как набор результатов.

Для сбора значений читается файл CORPORATE/TENANT, а столбец TENANT извлекается в переменную LIBRARY. Для каждой записи создается оператор, который объединяет LIBRARY в оператор INSERT. Этот оператор выполняется, который загружает запись в TMP. Я использую EXECUTE IMMEDIATE, потому что не могу использовать маркер параметра для замены ссылки на таблицу в операторе INSERT, поэтому подготовленный оператор - просто дополнительная работа.

0 голосов
/ 01 сентября 2018

Вы можете использовать UNION ALL:

SELECT sub.PAYDAT, t.TENANT, sub.AMOUNT
FROM (SELECT * FROM LIB01.INVOICE
      UNION ALL
      SELECT * FROM LIB02/INVOICE
      ...
      SELECT * FROM LIB0n/INVOICE) sub
JOIN TENANTS t
  ON sub.id = t.id
WHERE SUB.PAYDAT = 180701;

Это шаблон SELECT * FROM + + yymm .


EDIT:

Более безопасный способ - создать представление:

CREATE VIEW combined_invoice
AS
SELECT * FROM LIB01.INVOICE
UNION ALL
SELECT * FROM LIB02/INVOICE
...
SELECT * FROM LIB0n/INVOICE;

И запрос:

SELECT sub.PAYDAT, t.TENANT, sub.AMOUNT
FROM combined_invoice sub
JOIN TENANTS t
  ON sub.id = t.id
WHERE SUB.PAYDAT = 180701;

Конечно, вид должен быть изменен после добавления / удаления таблиц.

...