h2 VIEW 的 SELECT 语句有问题

h2 VIEW has problems with correct SELECT statement

我正在开发一个由 h2 数据库支持的会计系统。我有很多观点可以将 ACCOUNTSTRANSACTIONSTEMPLATE_TRANSACTIONS table 中的数据汇集在一起​​。

问题,一言以蔽之

虽然我有一个 SELECT 语句可以准确地生成我需要在 VIEW_TEMPLATES_DATA 视图中看到的数据,但当我在 CREATE VIEW VIEW_TEMPLATES_DATA 中使用 SELECT 然后go SELECT * FROM VIEW_TEMPLATES_DATA, h2 告诉我找不到其中的一列。 h2 怎么可能对 'naked' SELECT 语句完全满意,但一旦打包成 CREATE VIEW 语句就不满意了?

对于这个 post 的篇幅,我深表歉意,但是 SELECT 声明必然相当冗长和复杂,并且有必要的背景信息。 post 包含构建小型测试数据库所需的所有 SQL,供有兴趣的人使用。

背景

模板交易是以'prototype'形式存储frequently-used交易的一种方式:模板交易的唯一要求是它有一个叙述。日期、金额、账户等都是可选的。

一个帐户可能与一个模板交易相关联,该交易可以在核对帐户时自动实例化。例如,这对于信用卡帐户来说非常方便。

自然地,模板交易的 'legs' 与存储 header 信息的 table 分开存储。

因此,我们有以下 table 和视图(自然地,从真实事物简化而来):

ACCOUNTS
ID   NAME         PAYER_TEMPLATE_ID   CURRENCY_ID
-------------------------------------------------
95   account0     null                1
122  account1     47                  0
178  foo bar      35                  0

TEMPLATE_TRANSACTIONS
ID   NARRATION
--------------
32   template0
35   template1
47   template2

TEMPLATE_TRANSACTION_LEGS
ID   HEAD_TABLE_ID   ACCOUNT_ID   AMOUNT
----------------------------------------
23   32              95           null
74   35              178          500
75   35              null         -500

VIEW_TEMPLATES_DATA
HEAD_ID   NARRATION   LEG_ID   ACCOUNT_ID   AMOUNT   CURRENCY_ID
----------------------------------------------------------------
23        template0   23       95           null     1
35        template1   74       178          500      0
35        template1   75       null         -500     null
47        template2   null     null         null     null

上面的代码如下:

CREATE TABLE TEMPLATE_TRANSACTIONS(
   ID BIGINT NOT NULL PRIMARY KEY,
   NARRATION VARCHAR NOT NULL DEFAULT ' '
);

CREATE TABLE CURRENCIES(
   ID BIGINT NOT NULL PRIMARY KEY,
   DESCRIPTION VARCHAR DEFAULT ''
);

CREATE TABLE ACCOUNTS(
   ID BIGINT NOT NULL PRIMARY KEY,
   NAME VARCHAR DEFAULT '',
   PAYER_TEMPLATE_ID BIGINT,
   CURRENCY_ID BIGINT NOT NULL
);
ALTER TABLE ACCOUNTS ADD CONSTRAINT ACCOUNTS_FK_2 FOREIGN KEY(CURRENCY_ID) REFERENCES CURRENCIES(ID);   
ALTER TABLE ACCOUNTS ADD CONSTRAINT ACCOUNTS_FK_3 FOREIGN KEY(PAYER_TEMPLATE_ID) REFERENCES TEMPLATE_TRANSACTIONS(ID);

CREATE TABLE TEMPLATE_TRANSACTION_LEGS(
   ID BIGINT NOT NULL PRIMARY KEY,
   HEAD_TABLE_ID BIGINT NOT NULL,
   ACCOUNT_ID BIGINT DEFAULT NULL,
   AMOUNT INT DEFAULT NULL
);
ALTER TABLE TEMPLATE_TRANSACTION_LEGS ADD CONSTRAINT TEMPLATE_TRANSACTION_LEGS_FK_1 FOREIGN KEY(HEAD_TABLE_ID) REFERENCES TEMPLATE_TRANSACTIONS(ID);
ALTER TABLE TEMPLATE_TRANSACTION_LEGS ADD CONSTRAINT TEMPLATE_TRANSACTION_LEGS_FK_2 FOREIGN KEY(ACCOUNT_ID) REFERENCES ACCOUNTS(ID);

CREATE VIEW VIEW_TEMPLATES_DATA AS (SELECT
   TEMPLATE_TRANSACTIONS.ID     AS HEAD_ID,
   TEMPLATE_TRANSACTIONS.NARRATION,

   TEMPLATE_TRANSACTION_LEGS.ID     AS LEG_ID,
   TEMPLATE_TRANSACTION_LEGS.ACCOUNT_ID,
   TEMPLATE_TRANSACTION_LEGS.AMOUNT,
   ACCOUNTS.CURRENCY_ID
   FROM (TEMPLATE_TRANSACTIONS LEFT OUTER JOIN TEMPLATE_TRANSACTION_LEGS ON TEMPLATE_TRANSACTION_LEGS.HEAD_TABLE_ID = TEMPLATE_TRANSACTIONS.ID)
      LEFT OUTER JOIN ACCOUNTS ON TEMPLATE_TRANSACTION_LEGS.ACCOUNT_ID = ACCOUNTS.ID
);

INSERT INTO CURRENCIES (ID, DESCRIPTION) VALUES (0, 'currency0');
INSERT INTO CURRENCIES (ID, DESCRIPTION) VALUES (1, 'currency1');
INSERT INTO TEMPLATE_TRANSACTIONS (ID, NARRATION) VALUES (32, 'template0');
INSERT INTO TEMPLATE_TRANSACTIONS (ID, NARRATION) VALUES (35, 'template1');
INSERT INTO TEMPLATE_TRANSACTIONS (ID, NARRATION) VALUES (47, 'template2');
INSERT INTO ACCOUNTS (ID, NAME, PAYER_TEMPLATE_ID, CURRENCY_ID) VALUES (95, 'account0', null, 1);
INSERT INTO ACCOUNTS (ID, NAME, PAYER_TEMPLATE_ID, CURRENCY_ID) VALUES (122, 'account1', 47, 0);
INSERT INTO ACCOUNTS (ID, NAME, PAYER_TEMPLATE_ID, CURRENCY_ID) VALUES (178, 'foo bar', 35, 0);
INSERT INTO TEMPLATE_TRANSACTION_LEGS (ID, HEAD_TABLE_ID, ACCOUNT_ID, AMOUNT) VALUES (23, 32, 95, null);
INSERT INTO TEMPLATE_TRANSACTION_LEGS (ID, HEAD_TABLE_ID, ACCOUNT_ID, AMOUNT) VALUES (74, 35, 178, 500);
INSERT INTO TEMPLATE_TRANSACTION_LEGS (ID, HEAD_TABLE_ID, ACCOUNT_ID, AMOUNT) VALUES (75, 35, null, -500);

这在生产数据库中运行良好已有一段时间了。现在我需要向视图添加一个布尔列 IS_PAYER,对于 ID 在 ACCOUNTS.PAYER_TEMPLATE_ID 中的任何模板交易,该列将是 TRUE。如上所述,我有一个 SELECT 语句,它完全符合我的要求:

HEAD_ID   NARRATION   LEG_ID   ACCOUNT_ID   AMOUNT   CURRENCY_ID   IS_PAYER
---------------------------------------------------------------------------
32        template0   23       95           null     1             FALSE
35        template1   74       178          500      0             TRUE
35        template1   75       null         -500     null          TRUE
47        template2   null     null         null     null          TRUE

SELECT TEMPLATE_TRANSACTIONS.ID     AS HEAD_ID,
   TEMPLATE_TRANSACTIONS.NARRATION,
   TEMPLATE_TRANSACTION_LEGS.ID     AS LEG_ID,
   TEMPLATE_TRANSACTION_LEGS.ACCOUNT_ID,
   TEMPLATE_TRANSACTION_LEGS.AMOUNT,
   ACCOUNTS.CURRENCY_ID,
   IS_PAYER
FROM (
   (TEMPLATE_TRANSACTIONS LEFT OUTER JOIN TEMPLATE_TRANSACTION_LEGS ON TEMPLATE_TRANSACTION_LEGS.HEAD_TABLE_ID = TEMPLATE_TRANSACTIONS.ID)
    LEFT OUTER JOIN ACCOUNTS ON TEMPLATE_TRANSACTION_LEGS.ACCOUNT_ID = ACCOUNTS.ID)
JOIN
   (SELECT TEMPLATE_ID, (CASE WHEN PAYER_TEMPLATE_ID IS NOT NULL THEN TRUE ELSE FALSE END) AS IS_PAYER FROM 
      (SELECT TEMPLATE_TRANSACTIONS.ID AS TEMPLATE_ID, ACCOUNTS.PAYER_TEMPLATE_ID AS PAYER_TEMPLATE_ID FROM 
          ACCOUNTS RIGHT JOIN TEMPLATE_TRANSACTIONS ON ACCOUNTS.PAYER_TEMPLATE_ID =TEMPLATE_TRANSACTIONS.ID))
ON TEMPLATE_ID = TEMPLATE_TRANSACTIONS.ID

但是当我这样做时:

DROP VIEW VIEW_TEMPLATES_DATA;
CREATE VIEW VIEW_TEMPLATES_DATA AS (SELECT
   TEMPLATE_TRANSACTIONS.ID     AS HEAD_ID,
   TEMPLATE_TRANSACTIONS.NARRATION,
   TEMPLATE_TRANSACTION_LEGS.ID     AS LEG_ID,
   TEMPLATE_TRANSACTION_LEGS.ACCOUNT_ID,
   TEMPLATE_TRANSACTION_LEGS.AMOUNT,
   ACCOUNTS.CURRENCY_ID,
   IS_PAYER
FROM (
   (TEMPLATE_TRANSACTIONS LEFT OUTER JOIN TEMPLATE_TRANSACTION_LEGS ON TEMPLATE_TRANSACTION_LEGS.HEAD_TABLE_ID = TEMPLATE_TRANSACTIONS.ID)
    LEFT OUTER JOIN ACCOUNTS ON TEMPLATE_TRANSACTION_LEGS.ACCOUNT_ID = ACCOUNTS.ID)
JOIN
   (SELECT TEMPLATE_ID, (CASE WHEN PAYER_TEMPLATE_ID IS NOT NULL THEN TRUE ELSE FALSE END) AS IS_PAYER FROM 
      (SELECT TEMPLATE_TRANSACTIONS.ID AS TEMPLATE_ID, ACCOUNTS.PAYER_TEMPLATE_ID AS PAYER_TEMPLATE_ID FROM 
          ACCOUNTS RIGHT JOIN TEMPLATE_TRANSACTIONS ON ACCOUNTS.PAYER_TEMPLATE_ID =TEMPLATE_TRANSACTIONS.ID))
ON TEMPLATE_ID = TEMPLATE_TRANSACTIONS.ID
);

然后去 SELECT * FROM VIEW_TEMPLATES_DATA 它告诉我 TEMPLATE_TRANSACTION_LEGS.ACCOUNT_ID not found.

真不明白为什么我原来的观点是acceptable,我的新观点SELECT也是,而我的新观点CREATE VIEW却不是!我在 SQL 中做的不多,错误消息也不是很有帮助,我真的不知道该去哪里。

任何好心的人都可以指出解决方案的方向吗?

问题是您在 JOIN 子句中使用的括号。视图上的 SELECT 将在您删除它们后立即生效:

CREATE VIEW VIEW_TEMPLATES_DATA AS (SELECT
   TEMPLATE_TRANSACTIONS.ID     AS HEAD_ID,
   TEMPLATE_TRANSACTIONS.NARRATION,
   TEMPLATE_TRANSACTION_LEGS.ID     AS LEG_ID,
   TEMPLATE_TRANSACTION_LEGS.ACCOUNT_ID,
   TEMPLATE_TRANSACTION_LEGS.AMOUNT,
   ACCOUNTS.CURRENCY_ID,
   IS_PAYER
FROM  TEMPLATE_TRANSACTIONS
      LEFT OUTER JOIN TEMPLATE_TRANSACTION_LEGS ON TEMPLATE_TRANSACTION_LEGS.HEAD_TABLE_ID = TEMPLATE_TRANSACTIONS.ID
      LEFT OUTER JOIN ACCOUNTS ON TEMPLATE_TRANSACTION_LEGS.ACCOUNT_ID = ACCOUNTS.ID
      JOIN (SELECT TEMPLATE_ID, (CASE WHEN PAYER_TEMPLATE_ID IS NOT NULL THEN TRUE ELSE FALSE END) AS IS_PAYER
              FROM (SELECT TEMPLATE_TRANSACTIONS.ID AS TEMPLATE_ID, ACCOUNTS.PAYER_TEMPLATE_ID AS PAYER_TEMPLATE_ID
                      FROM ACCOUNTS
                           RIGHT JOIN TEMPLATE_TRANSACTIONS ON ACCOUNTS.PAYER_TEMPLATE_ID =TEMPLATE_TRANSACTIONS.ID))
      ON TEMPLATE_ID = TEMPLATE_TRANSACTIONS.ID
);

当您将第一个 LEFT OUTER JOIN 放在括号中时,加入的 table TEMPLATE_TRANSACTION_LEGS 在第二个 LEFT OUTER JOIN.

中不可见