将CTE应用于递归查询

use*_*531 5 sql recursion recursive-query common-table-expression mariadb

我正在尝试应用CTE和递归查询.数据库是MariaDB 10.2或更高版本.

业务规则如下:

  1. 账户可以是持股或投资组合.
  2. 控股包含一定数量的资金.
  3. 控股可以是活跃的和不活跃的.
  4. 投资组合包含零个或多个账户,这些账户可以属于多个投资组合.
  5. 在确定投资组合的价值时,每个账户的总价值乘以"权重"因子.

我的架构如下(注释char用于id类型仅用于说明目的,但我将真正使用int):

在此输入图像描述

CREATE TABLE IF NOT EXISTS accounts (
  id CHAR(4) NOT NULL,
  name VARCHAR(45) NOT NULL,
  type ENUM('holding', 'portfolio') NULL,
  PRIMARY KEY (id))
ENGINE = InnoDB;

CREATE TABLE IF NOT EXISTS holdings (
  accounts_id CHAR(4) NOT NULL,
  value DECIMAL(6,2) NOT NULL,
  active TINYINT NOT NULL,
  PRIMARY KEY (accounts_id),
  CONSTRAINT fk_holdings_accounts
    FOREIGN KEY (accounts_id)
    REFERENCES accounts (id)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

CREATE TABLE IF NOT EXISTS portfolios (
  accounts_id CHAR(4) NOT NULL,
  PRIMARY KEY (accounts_id),
  CONSTRAINT fk_portfolios_accounts1
    FOREIGN KEY (accounts_id)
    REFERENCES accounts (id)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

CREATE TABLE IF NOT EXISTS portfolios_has_accounts (
  portfolios_id CHAR(4) NOT NULL,
  accounts_id CHAR(4) NOT NULL,
  weight DECIMAL(4,2) NOT NULL,
  PRIMARY KEY (portfolios_id, accounts_id),
  INDEX fk_portfolios_has_accounts_accounts1_idx (accounts_id ASC),
  INDEX fk_portfolios_has_accounts_portfolios1_idx (portfolios_id ASC),
  CONSTRAINT fk_portfolios_has_accounts_portfolios1
    FOREIGN KEY (portfolios_id)
    REFERENCES portfolios (accounts_id)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT fk_portfolios_has_accounts_accounts1
    FOREIGN KEY (accounts_id)
    REFERENCES accounts (id)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;
Run Code Online (Sandbox Code Playgroud)

样本数据如下:

INSERT INTO accounts(id,name,type) VALUES ('p1','portfolio1','portfolio'),('p2','portfolio2','portfolio'),('p3','portfolio3','portfolio'),('h1','holding1','holding'),('h2','holding2','holding'),('h3','holding3','holding'),('h4','holding4','holding');
INSERT INTO holdings(accounts_id,value,active) VALUES ('h1','50','1'),('h2','40','0'),('h3','70','1'),('h4','40','1');
INSERT INTO portfolios(accounts_id) VALUES ('p1'),('p2'),('p3');
INSERT INTO portfolios_has_accounts(portfolios_id,accounts_id,weight) VALUES ('p1','h1','1'),('p1','p2','0.5'),('p2','h2','2'),('p2','p3','1'),('p3','h3','2'),('p3','h4','0.5');
Run Code Online (Sandbox Code Playgroud)

账户

id  name        type
p1  portfolio1  portfolio
p2  portfolio2  portfolio
p3  portfolio3  portfolio
h1  holding1    holding
h2  holding2    holding
h3  holding3    holding
h4  holding4    holding
Run Code Online (Sandbox Code Playgroud)

投资组合

portfolios_id
p1
p2
p3
Run Code Online (Sandbox Code Playgroud)

增持

id value active
h1  50   1
h2  40   0
h3  70   1
h4  40   1
Run Code Online (Sandbox Code Playgroud)

portfolios_has_accounts

portfolios_id   accounts_id weight
p1               h1         1
p1               p2         0.5
p2               h2         2
p2               p3         1
p3               h3         2
p3               h4         0.5
Run Code Online (Sandbox Code Playgroud)

我的目标是找到:

  1. 查找仅包含有效馆藏的所有帐户.给定样本数据,它是p3,h1,h3和h4.不包括p2,因为它包括不活跃的h2,并且因为它包括p2而不包括p1.

  2. 投资组合p1的总价值.给出样本数据,它是170:1*50 + 0.5*(2*40 + 1*(2*70 + 0.5*40))

  3. 持有量乘以的常数导致投资组合p1的总价值.给出样本数据,它们如下(注意1*h1 + 1*h2 + 1*h3 + 0.25*h4 = 170)

.

id  weight
h1  1
h2  1
h3  1
h4  .25
Run Code Online (Sandbox Code Playgroud)

我怎么能做到这一点?

use*_*531 0

请评论这些是否应该以不同的方式进行,或者从性能的角度来看,它们是否有任何重大问题?

目标#1

MariaDB [recursion]> WITH RECURSIVE t AS (
    ->     SELECT accounts_id FROM holdings WHERE active=0
    ->     UNION ALL
    ->     SELECT pha.portfolios_id
    ->     FROM portfolios_has_accounts pha
    ->     INNER JOIN t ON t.accounts_id=pha.accounts_id
    -> )
    -> SELECT a.* FROM accounts a
    -> LEFT OUTER JOIN t ON t.accounts_id=a.id
    -> WHERE t.accounts_id IS NULL;
+----+------------+-----------+
| id | name       | type      |
+----+------------+-----------+
| h1 | holding1   | holding   |
| h3 | holding3   | holding   |
| h4 | holding4   | holding   |
| p3 | portfolio3 | portfolio |
+----+------------+-----------+
4 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

目标#2

MariaDB [recursion]> WITH RECURSIVE t AS (
    -> SELECT pha.*, h.value
    -> FROM portfolios_has_accounts pha
    -> LEFT OUTER JOIN holdings h ON h.accounts_id=pha.accounts_id
    -> WHERE pha.portfolios_id="p1"
    -> UNION ALL
    -> SELECT pha.portfolios_id, pha.accounts_id, pha.weight*t.weight, h.value
    -> FROM t
    -> INNER JOIN portfolios_has_accounts pha ON pha.portfolios_id=t.accounts_id
    -> LEFT OUTER JOIN holdings h ON h.accounts_id=pha.accounts_id
    -> )
    -> SELECT SUM(weight*value) FROM t WHERE value IS NOT NULL;
+-------------------+
| SUM(weight*value) |
+-------------------+
| 170.0000          |
+-------------------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

目标#3

MariaDB [recursion]> WITH RECURSIVE t AS (
    -> SELECT pha.*, h.value
    -> FROM portfolios_has_accounts pha
    -> LEFT OUTER JOIN holdings h ON h.accounts_id=pha.accounts_id
    -> WHERE pha.portfolios_id="p1"
    -> UNION ALL
    -> SELECT pha.portfolios_id, pha.accounts_id, pha.weight*t.weight, h.value
    -> FROM t
    -> INNER JOIN portfolios_has_accounts pha ON pha.portfolios_id=t.accounts_id
    -> LEFT OUTER JOIN holdings h ON h.accounts_id=pha.accounts_id
    -> )
    -> SELECT accounts_id, weight FROM t WHERE value IS NOT NULL;
+-------------+--------+
| accounts_id | weight |
+-------------+--------+
| h1          | 1.00   |
| h2          | 1.00   |
| h3          | 1.00   |
| h4          | 0.25   |
+-------------+--------+
4 rows in set (0.01 sec)

MariaDB [recursion]>
Run Code Online (Sandbox Code Playgroud)