【SQLite学习笔记】With语句

简述

With语句可以出现在SELECTINSERTDELETEUPDATE语句的开头,如下。
INSERT支持以With语句开头
其中,common-table-expression可以简写为cte
上面内容的原型就是With语句。它的命令图如下:
With语句命令图

cte-table-name

命令图中cte-table-name其实就是表名和其中部分列名的组合,命令图如下:
cte-table-name命令图
如果在INSERT语句中,不填写schema-namealias,则TO后面就跟着一个cte-table-name,如下面代码中,insert_study_table(integer_value, real_value, text_value)就是一个cte-table-name

INSERT INTO insert_study_table(integer_value, real_value, text_value) VALUES (1, 1.1, 'a');

简单用法

假设有数据表for_test_table,存有如下数据:

idinteger_valuereal_valuetext_valueblob_value
111.1aa
221.2bNULL
则执行下面命令
WITH sel AS
(
 SELECT integer_value, real_value FROM for_test_table
 UNION ALL VALUES (1, 2.1), (2, 2.2)
)
SELECT * FROM sel;

输出结果为

integer_valuereal_value
11.1
21.2
12.1
22.2

With语句可以查询出一些数据,并提供给后面的命令使用。

RECURSIVE

RECURSIVE的意思是递归,标记了RECURSIVE也可以跟随普通(非递归)表达式。
先给出一个简单的使用递归表达式的With语句示例:

WITH RECURSIVE
  ctn(integer_value) AS (VALUES(1) UNION ALL SELECT integer_value+1 FROM ctn WHERE integer_value<3)
SELECT integer_value FROM ctn;

结果为

integer_value
1
2
3

上面示例命令中,声明cte-table-namectn(integer_value)select-stmt是一个复合语句,先指定一个元素1,最为迭代起点,然后从ctn中迭代地查出元素并加一,得到新的元素,并通过UNION ALL添加到结果中。迭代在integer_value为3时结束。

再给出一个较为常见的用法:
假设有数据表with_clause_study_table,内有数据如下

idinteger_valuereal_valuetext_value
1NULLNULLCEO
21NULLCOO
31NULLCTO
42NULLSVP Finance
52NULLSVP HR
62NULLEVP Operations
73NULLVP Engineering
83NULLVP IT
93NULLVP Product Management
106NULLVP Product Development

integer_value列的数据代表了该行上级的id,则结构如下

  • CEO
    • COO
      • SVP Finance
      • SVP HR
      • EVP Operations
        • VP Product Development
        • VP Manufacturing
        • VP Logistics
    • CTO
      • VP Engineering
      • VP IT
      • VP Product Management
        • Product Manager A
        • Product Manager B
        • Product Manager C

如下命令可以查出COO为根节点的树元素。

WITH RECURSIVE ctn(id, integer_value, text_value) AS
    (
        SELECT id, integer_value, text_value FROM with_clause_study_table WHERE id=2
        UNION ALL
        SELECT with_clause_study_table.id, with_clause_study_table.integer_value, with_clause_study_table.text_value FROM ctn,with_clause_study_table WHERE with_clause_study_table.integer_value=ctn.id
    )
SELECT * FROM ctn ORDER BY id;

结果为:

idinteger_valuetext_value
21COO
42SVP Finance
52SVP HR
62EVP Operations
106VP Product Development
116VP Manufacturing
126VP Logistics

基本要求

递归Select语句为从cte-table中查询的语句;否则为非递归Select语句。
使用递归表达式有如下几个要求

  1. select-stmt必须是复合SELECT语句,即由不少于2个独立的SELECT连接组成,连接关键字有UNIONUNION ALLINTERSECTEXCEPT
  2. 至少有一个递归Select语句。
  3. 至少有一个非递归Select语句。
  4. 所有非递归Select语句都写在递归Select语句之前
  5. 递归Select语句前面的分隔符必须是UNION或者UNION ALL。且所有递归Select语句前面的分隔符必须一致。
  6. 不从cte-table中查询的语句不能使用Aggregate函数窗口函数

递归CTE命令图的命令图如下
递归CTE命令图
由命令中带RECURSIVE时,将cte-table-name命名的数据表记为递归表(recursive table)
CTE命令图中命令的执行大致逻辑如下:

  1. 执行非递归Select语句,并将结果存入队列。
  2. 只要队列非空,则不断循环。
    1. 从队列中取出一个元素(一行数据)
    2. 将这个元素插入递归表中(这一步受到UNION,LIMIT和OFFSET的限制)
    3. 假设递归表中只有刚插入的这一行数据(实际没有插入也可以假设有),对递归表执行递归Select语句,并将结果存入队列。

查出COO为根节点的树元素的示例代码为例,队列和递归表中数据的变化顺序如下表所示

队列递归表说明
COO非递归Select命令查出COO,并入队
COO出队入表
SVP Finance
SVP HR
EVP Operations
COO假设只有COO一条数据(这里也是事实),用递归Select命令查询,查出三条结果,并入队
SVP HR
EVP Operations
COO
SVP Finance
SVP Finance出队。
假设只有SVP Finance一条数据,用递归Select命令查询,没有符合要求的结果
EVP OperationsCOO
SVP Finance
SVP HR
COO
SVP Finance
SVP HR
EVP Operations
EVP Operations出队
`VP Product Development
VP Manufacturing
VP Logistics
COO
SVP Finance
SVP HR
EVP Operations
假设只有EVP Operations一条数据,用递归Select命令查询,查到3条符合要求的数据
VP Manufacturing
VP Logistics
COO
SVP Finance
SVP HR
EVP Operations
VP Product Development
VP LogisticsCOO
SVP Finance
SVP HR
EVP Operations
VP Product Development
VP Manufacturing
COO
SVP Finance
SVP HR
EVP Operations
VP Product Development
VP Manufacturing
VP Logistics
VP Logistics出队。
假设只有VP Logistics一条数据,用递归Select命令查询,没有符合要求的结果。
此时队列为空而且没有新元素的补充,递归查询结束

Materialization

With语句中AS关键字后面,可以填写NOT MATERIALIZED或者MATERIALIZED或者两个都不填。一般推荐两个都不填,这样SQLite可以自己判断更优的选择。

MATERIALIZED表示将With中的查询结果存储在临时表中,后续多次用到时可以直接从临时包中获取数据;NOT MATERIALIZED表示将With语句的查询当做一次subquery,后续用到查询结果的地方都做一次Subquery。

九宫格数独问题

官网给出了一个用With语句解决数独问题的方法。假设有数独问题如下:

537
6195
986
863
4831
726
628
4195
879

执行如下命令

WITH RECURSIVE
  input(sud) AS (
    VALUES('53..7....6..195....98....6.8...6...34..8.3..17...2...6.6....28....419..5....8..79')
  ),
  digits(z, lp) AS (
    VALUES('1', 1)
    UNION ALL SELECT
    CAST(lp+1 AS TEXT), lp+1 FROM digits WHERE lp<9
  ),
  x(s, ind) AS (
    SELECT sud, instr(sud, '.') FROM input
    UNION ALL
    SELECT
      substr(s, 1, ind-1) || z || substr(s, ind+1),
      instr( substr(s, 1, ind-1) || z || substr(s, ind+1), '.' )
     FROM x, digits AS z
    WHERE ind>0
      AND NOT EXISTS (
            SELECT 1
              FROM digits AS lp
             WHERE z.z = substr(s, ((ind-1)/9)*9 + lp, 1)
                OR z.z = substr(s, ((ind-1)%9) + (lp-1)*9 + 1, 1)
                OR z.z = substr(s, (((ind-1)/3) % 3) * 3
                        + ((ind-1)/27) * 27 + lp
                        + ((lp-1) / 3) * 6, 1)
         )
  )
SELECT s FROM x WHERE ind=0;

得到结果:

s
534678912672195348198342567859761423426853791713924856961537284287419635345286179

534678912
672195348
198342567
859761423
426853791
713924856
961537284
287419635
345286179

这个语句的效率不一定高,比如数独问题为.........59.8..7......218...37.......5.79........3.18...5..2...81.....4...6.8.9.3时,耗时就会明显增加。

  • 题目:
5987
218
37
579
318
52
814
6893
  • 答案
682457391
591863724
374921856
237148569
158796432
469235187
945312678
813679245
726584913
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值