[英]Db2 for i - dynamic FROM clause
Environment: Db2 for i, version 7.3环境: Db2 for i,7.3 版
Library/table structure:库/表结构:
CORPORATE/TENANTS
LIB01/INVOICE
LIB02/INVOICE
LIB03/INVOICE
…
LIBxx/INVOICE
The CORPORATE/TENANTS
table contains a list of libraries where information about each tenant is stored. CORPORATE/TENANTS
表包含存储每个租户信息的库列表。 It has this structure and data:它具有以下结构和数据:
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 |
+--+------+
The LIBxx/INVOICE
tables are all identical to each other and have this structure: 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|
|..|......|......|
+--+------+------+
I want to generate a list of invoice amounts for all tenants for a given date:我想为给定日期的所有租户生成发票金额列表:
180701 LIB01 100.00
180701 LIB02 140.00
180701 LIB03 74.00
…
Conceptually what I want to do is this (yes, I know this is invalid SQL):从概念上讲,我想做的是这个(是的,我知道这是无效的 SQL):
SELECT PAYDAT, TENANT, AMOUNT
FROM $X.INVOICE
WHERE PAYDAT = 180701;
I want to pull data from the INVOICE
table for each TENANT
but I know the FROM
clause cannot be dynamic like this.我想从每个
TENANT
的INVOICE
表中提取数据,但我知道FROM
子句不能像这样是动态的。 I'm sure this kind of query has a name but I don't know what it is so I'm unable to effectively use a search engine to find what I need.我确信这种查询有一个名字,但我不知道它是什么,所以我无法有效地使用搜索引擎来找到我需要的东西。
This would be trivial to solve with an RPGLE program but I need a pure SQL solution.用 RPGLE 程序解决这个问题很简单,但我需要一个纯 SQL 解决方案。
Please note - the LIBxx values CANNOT be hardcoded in any way.请注意 - LIBxx 值不能以任何方式硬编码。 These values can change at any time.
这些值可以随时更改。
To do what you want, you can use a stored procedure with an EXECUTE IMMEDIATE
in a loop to build a result set.要执行您想要的操作,您可以在循环中使用带有
EXECUTE IMMEDIATE
的存储过程来构建结果集。 Something like this:像这样的东西:
Note: this is not a complete cut and paste solution, but you can modify it to do what you want.注意:这不是一个完整的剪切和粘贴解决方案,但您可以对其进行修改以执行您想要的操作。
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;
I gave you more than I planned to.我给你的比我计划的要多。 But, one specific modification you will invariably need is to parameterize the date that you want to retrieve.
但是,您始终需要的一项特定修改是参数化您要检索的日期。
This is how it works: A global temporary table named TMP
is used to collect the records to be returned in a result set.它是这样工作的:一个名为
TMP
全局临时表用于收集要在结果集中返回的记录。 Once all the records are collected, a cursor is opened over TMP
and the procedure ends.一旦收集了所有记录,就会在
TMP
上打开一个游标,过程结束。 This causes the values collected in TMP to be returned as a result set.这会导致在 TMP 中收集的值作为结果集返回。
To collect the values the CORPORATE/TENANT
file is read, and the column TENANT
is retrieved into the variable LIBRARY
.为了收集值,需要读取
CORPORATE/TENANT
文件,并将列TENANT
检索到变量LIBRARY
。 For each record a statement is built that concatenates LIBRARY
into an INSERT
statement.对于每条记录,都会构建一个将
LIBRARY
连接到INSERT
语句的语句。 This statement is executed which loads the record into TMP
.执行此语句将记录加载到
TMP
。 I am using EXECUTE IMMEDIATE
because I cannot use a parameter marker to replace the table reference in the INSERT
statement, so a prepared statement is just extra work.我使用
EXECUTE IMMEDIATE
因为我不能使用参数标记来替换INSERT
语句中的表引用,所以准备好的语句只是额外的工作。
You could use UNION ALL
:你可以使用
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;
It is an SELECT * FROM sales + @yymm template.它是一个SELECT * FROM sales + @yymm模板。
EDIT:编辑:
More secure way is to create a view:更安全的方法是创建一个视图:
CREATE VIEW combined_invoice
AS
SELECT * FROM LIB01.INVOICE
UNION ALL
SELECT * FROM LIB02/INVOICE
...
SELECT * FROM LIB0n/INVOICE;
And query:并查询:
SELECT sub.PAYDAT, t.TENANT, sub.AMOUNT
FROM combined_invoice sub
JOIN TENANTS t
ON sub.id = t.id
WHERE SUB.PAYDAT = 180701;
Of course view should be altered after adding/removing tables.当然,添加/删除表后应该更改视图。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.