欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入理解MySQL公共表表達(dá)式

 更新時(shí)間:2024年09月03日 10:55:21   作者:V1ncent Chen  
公用表達(dá)式是MySQL8.0的新特性,它是一個(gè)命名的臨時(shí)結(jié)果集,作用范圍是當(dāng)前語句,本文主要介紹了MySQL公共表表達(dá)式使用,感興趣的可以了解一下

公共表表達(dá)式(Common Table Expressions, CTE)是MySQL在單一語句中執(zhí)行過程中,預(yù)先定義的臨時(shí)結(jié)果集。

有時(shí)我們需要在一個(gè)SQL中重復(fù)執(zhí)行同一個(gè)子查詢,而每次子查詢都會(huì)重新計(jì)算結(jié)果,帶來性能的浪費(fèi)。而采用CTE可以在查詢的一開始就定義好子查詢的結(jié)果集,MySQL只會(huì)計(jì)算一次結(jié)果,然后在查詢中使用CTE的名稱可以反復(fù)引用。

一、CTE定義及分類

CTE的定義方式是在with子句后跟一個(gè)子查詢,如果一個(gè)SQL中需要定義多個(gè)CTE,則用逗號(hào)分隔即可。

定義語法:

with_clause:
    WITH [RECURSIVE]
        cte_name [(col_name [, col_name] ...)] AS (subquery)
        [, cte_name [(col_name [, col_name] ...)] AS (subquery)] ...

CTE分為兩種:

  • 普通CTE:定義一個(gè)簡(jiǎn)單子查詢
  • 遞歸CTE:定義時(shí)可以引用自己,產(chǎn)生一個(gè)遞歸的結(jié)果集

普通CTE和遞歸CTE的區(qū)別在于,遞歸CTE多了一個(gè)recursive關(guān)鍵字,且需要引用自己。

二、普通CTE

2.1 普通CTE示例

以下的演示SQL可以在在MySQL的官方示例數(shù)據(jù)庫中執(zhí)行:

with
cte1 as (select emp_no,first_name,last_name from employees where emp_no=10012), -- 定義cte1
cte2 as (select emp_no,dept_no from dept_emp)    -- 定義cte2
select cte1.emp_no,cte2.dept_no,cte1.first_name
from cte1
join cte2 on cte1.emp_no=cte2.emp_no;

示例中在select子句前定義了cte1和cte2(以逗號(hào)分隔),隨后在select子句中可以直接引用cte1和cte2的名稱進(jìn)行查詢。

cte定義時(shí)也可以引用其他cte,例如在上面的定義中,cte2的定義可以引用cte1:

with
cte1 as (select emp_no,first_name,last_name from employees where emp_no=10012),
cte2 as (select emp_no,last_name from cte1)    -- cte2的定義引用了cte1
select cte1.emp_no,cte2.last_name
from cte1
join cte2 on cte1.emp_no=cte2.emp_no;

注意之只有后定義的cte可以引用前面的定義的cte,如果把cte2定義位置調(diào)到前面,則會(huì)報(bào)錯(cuò):cte1不存在.

cte定義的名稱后面可以添加括號(hào),顯式定義cte的列名,但要和后面子查詢返還的列數(shù)量相同:

with
cte1(col1, col2, col3) as (select emp_no,first_name,last_name from employees where emp_no=10012)
select col1, col2, col3    -- 引用定義的列名
from cte1;

此時(shí)后續(xù)cte則必須通過顯示定義的列名來引用(col1, col2, col3),定義中子查詢的列名不能再引用了。

2.2 CTE的使用場(chǎng)景

cte的定義不僅僅用在select中,也可以用在update/delete語句前,子查詢中,以及其他可以嵌套select語句的地方(例如 insert …select):

  • WITH ... SELECT ...
  • WITH ... UPDATE ...
  • WITH ... DELETE …
  • SELECT ... WHERE id IN (WITH ... SELECT ...) ...
  • SELECT * FROM (WITH ... SELECT ...) AS dt ...
  • INSERT ... WITH ... SELECT ...
  • REPLACE ... WITH ... SELECT ...
  • CREATE TABLE ... WITH ... SELECT ...
  • CREATE VIEW ... WITH ... SELECT ...
  • DECLARE CURSOR ... WITH ... SELECT ...
  • EXPLAIN ... WITH ... SELECT ...

三、遞歸CTE

3.1 遞歸CTE示例

如果一個(gè)cte定義過程中引用了自己,則是遞歸cte,此時(shí)需要with recursive子句定義,其中recursive關(guān)鍵字是必須的。

遞歸cte包含2個(gè)部分,使用union all 或 union [distinct]連接:

with recursive
cte(n) as (
select 1
union all
select n+1 from cte where n<5)
select * from cte;

上述cte定義中第1部分生成了一條初始數(shù)據(jù),union all后面的第二部分引用了cte自己,且遞歸執(zhí)行,直到不再滿足條件(n<5)。

1個(gè)遞歸cte其實(shí)包含了非遞歸部分和遞歸部分,遞歸的第二部分每次都以上一次產(chǎn)生的結(jié)果集為基礎(chǔ)計(jì)算數(shù)據(jù)。但是大小是以非遞歸部分為準(zhǔn),如果遞歸產(chǎn)生列越來越長(zhǎng),可能會(huì)發(fā)生錯(cuò)誤。

例如下面的遞歸拼接:

with recursive
cte as (
select 1 as n, 'abc' as str    -- 非遞歸部分
union all
select n+1,concat(str,str) from cte where n<3)    -- 遞歸部分
select * from cte;

如上圖所示,在strict SQL模式下,因?yàn)榈诙幸苑沁f歸部分的長(zhǎng)度為準(zhǔn),遞歸后長(zhǎng)度列的長(zhǎng)度變長(zhǎng)導(dǎo)致SQL直接報(bào)錯(cuò)。

而在非strict SQL模式下,以上SQL可以執(zhí)行成功,但是第二列都被按非遞歸部分截?cái)嗔?,如下所示?/p>

在遇到此類cte定義時(shí),將非遞歸部分的列定義大一些,例如下面將'abc'的非遞歸部分加長(zhǎng),即可顯示正確的遞歸結(jié)果:

with recursive
cte as (
select 1 as n, cast('abc' as char(20)) as str    -- 定義長(zhǎng)度
union all
select n+1,concat(str,str) from cte where n<3)
select * from cte;

另外,對(duì)于遞歸cte的遞歸部分(即union后的SQL)還有部分使用限制:

  • 遞歸部分不能包含聚合函數(shù)、窗口函數(shù)、group by、order by、distinct
  • 遞歸部分引用自身只能引用一次且必須在from子句中,不能在子查詢中。

3.2 限制無限遞歸

對(duì)于遞歸cte,如果沒有加限制遞歸的條件,在邏輯上是可以無限遞歸的(死循環(huán))。為了限制這種情況,MySQL有4種解決方式:

  • 使用參數(shù)cte_max_recursion_depth來限制最大遞歸的次數(shù),超過遞歸深度強(qiáng)制終止。
  • 使用參數(shù)max_execution_time來限制最大的執(zhí)行時(shí)間。
  • 使用優(yōu)化器提示 MAX_EXECUTION_TIME來限制最大執(zhí)行時(shí)間。
  • MySQL 8.0.19后,可以用limit子句限制最大返還行數(shù)。

示例:通過cte_max_recursion_depth限制遞歸次數(shù),超過10次遞歸終止

set session cte_max_recursion_depth=10;  -- 全局默認(rèn)值是1000,我們這里修改會(huì)話級(jí)為10次
with recursive
cte(n) as (
select 1
union all
select n+1 from cte)
select * from cte;

示例:超過10毫秒終止遞歸

set session cte_max_recursion_depth=100000;   -- 將遞歸次數(shù)增大,防止先觸發(fā)
set session max_execution_time=10;    -- 將最大遞歸執(zhí)行時(shí)長(zhǎng)修改為10毫秒
with recursive
cte(n) as (
select 1
union all
select n+1 from cte)
select * from cte;

示例:使用優(yōu)化器提示限制遞歸執(zhí)行時(shí)間

with recursive
cte(n) as (
select 1
union all
select n+1 from cte)
select /*+ MAX_EXECUTION_TIME(10) */ * from cte;    -- 使用提示語法限制執(zhí)行時(shí)間

四、一個(gè)遞歸CTE應(yīng)用示例

假設(shè)我們有一張訂單表,

create table orders (dt date,price decimal(10,2));
insert into orders values
('2022-01-01',100),
('2022-01-01',200),
('2022-01-03',200),
('2022-01-03',200),
('2022-01-05',300),
('2022-01-07',200);

現(xiàn)在要統(tǒng)計(jì)截止'2022-01-07'日的營(yíng)業(yè)額,正常我們使用group by按日期匯集訂單金額即可:

select dt, sum(price) sales from orders group by dt;

但是注意到由于2號(hào)/4號(hào)/6號(hào)沒有訂單,所以查詢出來的結(jié)果中不包含這些日期,而通過遞歸cte我們可以先按日期遞歸,將這些日期列出來然后與orders連接:

with recursive cte(dt) as (
select min(dt) from orders
union all
select dt + interval 1 day from cte where dt <(select max(dt) from orders))
select e.dt,ifnull(sum(o.price),0) turnover
from cte e
left join orders o on o.dt=e.dt
group by e.dt
order by e.dt;

可以看到?jīng)]有訂單的日期也顯示出來了,營(yíng)業(yè)額顯示為0,這個(gè)技巧在做報(bào)表類數(shù)據(jù)時(shí)很有用。

到此這篇關(guān)于深入理解MySQL公共表表達(dá)式的文章就介紹到這了,更多相關(guān)MySQL公共表表達(dá)式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MySQL權(quán)限USAGE和ALL PRIVILEGES的用法

    MySQL權(quán)限USAGE和ALL PRIVILEGES的用法

    本文主要介紹了MySQL權(quán)限USAGE和ALL PRIVILEGES的用法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-09-09
  • 一條 SQL 語句執(zhí)行過程

    一條 SQL 語句執(zhí)行過程

    這篇文章主要介紹了一條 SQL 語句執(zhí)行過程的相關(guān)資料,沒人詳細(xì)具有一的的參考價(jià)值,需要的小伙伴可以參考一下,希望對(duì)你的學(xué)習(xí)和工作有所幫助
    2022-03-03
  • centos 6.9安裝mysql的詳細(xì)教程

    centos 6.9安裝mysql的詳細(xì)教程

    這篇文章主要介紹了centos 6.9安裝mysql的詳細(xì)教程,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-07-07
  • MySQL切分查詢用法分析

    MySQL切分查詢用法分析

    這篇文章主要介紹了MySQL切分查詢用法,結(jié)合實(shí)例形式分析了通過do while語句進(jìn)行切分查詢的具體實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2016-04-04
  • MySQL 不等于的三種使用及區(qū)別

    MySQL 不等于的三種使用及區(qū)別

    MySQL中常用到判斷符號(hào),而不等于是比較常用的符號(hào),不等于主要是三種,本文主要介紹了三種的使用及區(qū)別,感興趣的同學(xué)可以了解一下
    2021-06-06
  • 了解MySQL查詢語句執(zhí)行過程(5大組件)

    了解MySQL查詢語句執(zhí)行過程(5大組件)

    這篇文章主要介紹了了解MySQL查詢語句執(zhí)行過程(5大組件),文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-08-08
  • 教你為MySQL數(shù)據(jù)庫換擋加速

    教你為MySQL數(shù)據(jù)庫換擋加速

    如果你是個(gè)賽車手,并且按一下按鈕就能夠立即更換引擎而不需要把車開到車庫里去換,那會(huì)是什么感覺呢?MySQL數(shù)據(jù)庫為開發(fā)人員所做的就好像是按按鈕換引擎;它讓你選擇數(shù)據(jù)庫引擎,并給你一條簡(jiǎn)單的途徑來切換它。
    2010-02-02
  • 深入了解SQL注入

    深入了解SQL注入

    本篇文章通過SQL和MYSQL的對(duì)比,以及SQL注入的原理等方面詳細(xì)分析了SQL注入相關(guān)知識(shí)點(diǎn),對(duì)此有興趣的朋友學(xué)習(xí)下。
    2018-02-02
  • MySQL root賬號(hào)遠(yuǎn)程新建數(shù)據(jù)庫報(bào)錯(cuò)1044問題及解決方法

    MySQL root賬號(hào)遠(yuǎn)程新建數(shù)據(jù)庫報(bào)錯(cuò)1044問題及解決方法

    這篇文章主要介紹了MySQL root賬號(hào)遠(yuǎn)程新建數(shù)據(jù)庫報(bào)錯(cuò)1044問題及解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-09-09
  • 如何用cmd連接Mysql數(shù)據(jù)庫

    如何用cmd連接Mysql數(shù)據(jù)庫

    如何用cmd連接Mysql數(shù)據(jù)庫,需要的朋友可以參考一下
    2013-03-03

最新評(píng)論