成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

自己動手寫數據庫:解析 Select 語句并生成查詢樹

開發 前端
前面我們在實現 sql 解析器后,在解析完一條查詢語句后會創建一個 QueryData 對象,本節我們看看如何根據這個對象構建出合適的查詢規劃器(Plan)。我們將采取由簡單到負責的原則,首先我們直接構建 QueryData 的信息去構建查詢規劃對象,此時我們不考慮它所構造的查詢樹是否足夠優化,后面我們再慢慢改進構造算法,直到算法能構建出足夠優化的查詢樹。

首先,我們需要給原來代碼打個補丁,在SelectScan 結構體初始化時需要傳入 UpdateScan 接口對象,但很多時候我們需要傳入的是 Scan 對象,因此我們需要做一個轉換,也就是當初始化 SelectScan 時,如果傳入的是 Scan 對象,那么我們就將其封裝成 UpdateScan 接口對象,因此在 query 目錄下增加一個名為 updatescan_wrapper.go 的文件,在其中輸入內容如下:

package query

import (
    "record_manager"
)

type UpdateScanWrapper struct {
    scan Scan
}

func NewUpdateScanWrapper(s Scan) *UpdateScanWrapper {
    return &UpdateScanWrapper{
        scan: s,
    }
}

func (u *UpdateScanWrapper) GetScan() Scan {
    return u.scan
}

func (u *UpdateScanWrapper) SetInt(fldName string, val int) {
    //DO NOTHING
}

func (u *UpdateScanWrapper) SetString(fldName string, val string) {
    //DO NOTHING
}

func (u *UpdateScanWrapper) SetVal(fldName string, val *Constant) {
    //DO NOTHING
}

func (u *UpdateScanWrapper) Insert() {
    //DO NOTHING
}

func (u *UpdateScanWrapper) Delete() {
    //DO NOTHING
}

func (u *UpdateScanWrapper) GetRid() *record_manager.RID {
    return nil
}

func (u *UpdateScanWrapper) MoveToRid(rid *record_manager.RID) {
    // DO NOTHING
}

上面代碼邏輯簡單,如果調用 Scan 對象接口時,他直接調用其 Scan 內部對象的接口,如果調用到 UpdateScan 的接口,那么它什么都不做。完成上面代碼后,我們在select_plan.go 中進行一些修改:

func (s *SelectPlan) Open() interface{} {
    scan := s.p.Open()
    updateScan, ok := scan.(query.UpdateScan)
    if !ok {
        updateScanWrapper := query.NewUpdateScanWrapper(scan.(query.Scan))
        return query.NewSelectionScan(updateScanWrapper, s.pred)
    }
    return query.NewSelectionScan(updateScan, s.pred)
}

上面代碼在創建 SelectScan 對象時,先判斷傳進來的對象是否能類型轉換為 UpdateScan,如果不能,那意味著s.p.Open 獲取的是 Scan 對象,因此我們使用前面的代碼封裝一下再用來創建 SelectScan 對象。完成這里的修改后,我們進入正題。

前面我們在實現 sql 解析器后,在解析完一條查詢語句后會創建一個 QueryData 對象,本節我們看看如何根據這個對象構建出合適的查詢規劃器(Plan)。我們將采取由簡單到負責的原則,首先我們直接構建 QueryData 的信息去構建查詢規劃對象,此時我們不考慮它所構造的查詢樹是否足夠優化,后面我們再慢慢改進構造算法,直到算法能構建出足夠優化的查詢樹。

我們先看一個具體例子,假設我們現在有兩個表 STUDENT, EXAM,第一個表包含兩個字段分別是學生 id 和姓名:
id | name
———— | ——-
1 | Tom
2 | Jim
3 | John

第二個表包含的是學生 id,科目名稱,考試乘機:
stuid | exam|grad
———— | ——-|——-|
1 | math| A|
1 | algorithm| B
2 | writing| C |
2| physics| C|
3|chemical|B|
3|english| C|

現在我們使用 sql 語句查詢所有考試成績得過 A 的學生:

select name from STUDENT, EXAM where id = student_id and grad='A'

當 sql 解釋器讀取上面語句后,他就會創建一個 QueryData 結構,里面 Tables 對了就包含兩個表的名字,也就是 STUDENT, EXAM。由于這兩個表不是視圖,因此上面代碼中判斷 if viewDef != nil 不成立,于是進入 else 部分,也就是代碼會為這兩個表創建對應的 TablePlan 對象,接下來直接對這兩個表執行 Product 操作,也就是將左邊表的一行跟右邊表的每一行合起來形成新表的一行,Product 操作在 STUDENT 和 EXAM 表后所得結果如下:
id|name|student_id | exam|grad
————|——-|———— | ——-|——-|
1|Tom|1|math|A|
1|Tom|1|algorithm|B|
1|Tom|2|writing|A|
1|Tom|2|physics|C|
1|Tom|3|chemical|B|
1|Tom|3|english|A|
…..|….|…..|…|…|

接下來代碼創建 ScanSelect 對象在上面的表上,接著獲取該表的每一行,然后檢測該行的 id 字段是否跟 student_id 字段一樣,如果相同,那么查看其 grad 字段,如果該字段是’A’,就將該行的 name 字段顯示出來。

下面我們看看如何使用代碼把上面描述的流程實現出來。首先我們先對接口進行定義,在 Planner 目錄下的 interface.go 文件中增加如下內容:

type QueryPlanner interface {
    CreatePlan(data *query.QueryData, tx tx.Transaction) Plan
}

接著在 Planner 目錄下創建文件 query_planner.go,同時輸入以下代碼,代碼的實現邏輯將接下來的文章中進行說明:

package planner

import (
    "metadata_management"
    "parser"
    "tx"
)

type BasicQueryPlanner struct {
    mdm *metadata_management.MetaDataManager
}

func CreateBasicQueryPlanner(mdm *metadata_management.MetaDataManager) QueryPlanner {
    return &BasicQueryPlanner{
        mdm: mdm,
    }
}

func (b *BasicQueryPlanner) CreatePlan(data *parser.QueryData, tx *tx.Transaction) Plan {
    //1,直接創建 QueryData 對象中的表
    plans := make([]Plan, 0)
    tables := data.Tables()
    for _, tblname := range tables {
        //獲取該表對應視圖的 sql 代碼
        viewDef := b.mdm.GetViewDef(tblname, tx)
        if viewDef != nil {
            //直接創建表對應的視圖
            parser := parser.NewSQLParser(viewDef)
            viewData := parser.Query()
            //遞歸的創建對應表的規劃器
            plans = append(plans, b.CreatePlan(viewData, tx))
        } else {
            plans = append(plans, NewTablePlan(tx, tblname, b.mdm))
        }
    }

    //將所有表執行 Product 操作,注意表的次序會對后續查詢效率有重大影響,但這里我們不考慮表的次序,只是按照
    //給定表依次執行 Product 操作,后續我們會在這里進行優化
    p := plans[0]
    plans = plans[1:]

    for _, nextPlan := range plans {
        p = NewProductPlan(p, nextPlan)
    }

    p = NewSelectPlan(p, data.Pred())

    return NewProjectPlan(p, data.Fields())
}

上面代碼中 QueryData就是解析器在解析 select 語句后生成的對象,它的 Tables 數組包含了 select 語句要查詢的表,所以上面代碼的 CreatePlan 函數先從 QueryData 對象獲得 select 語句要查詢的表,然后使用遍歷這些表,使用 NewProductPlan 創建這些表對應的 Product 操作,最后在 Product 的基礎上我們再創建 SelectPlan,這里我們就相當于使用 where 語句中的條件,在 Product 操作基礎上將滿足條件的行選出來,最后再創建 ProjectPlan,將在選出的行基礎上,將需要的字段選擇出來。

下面我們測試一下上面代碼的效果,首先在 main.go 中,我們先把 student, exam 兩個表構造出來,代碼如下:

func createStudentTable() (*tx.Transation, *metadata_manager.MetaDataManager) {
    file_manager, _ := fm.NewFileManager("student", 2048)
    log_manager, _ := lm.NewLogManager(file_manager, "logfile.log")
    buffer_manager := bmg.NewBufferManager(file_manager, log_manager, 3)
    tx := tx.NewTransation(file_manager, log_manager, buffer_manager)
    sch := record_manager.NewSchema()
    mdm := metadata_manager.NewMetaDataManager(false, tx)

    sch.AddStringField("name", 16)
    sch.AddIntField("id")
    layout := record_manager.NewLayoutWithSchema(sch)

    ts := query.NewTableScan(tx, "student", layout)
    ts.BeforeFirst()
    for i := 1; i <= 3; i++ {
        ts.Insert() //指向一個可用插槽
        ts.SetInt("id", i)
        if i == 1 {
            ts.SetString("name", "Tom")
        }
        if i == 2 {
            ts.SetString("name", "Jim")
        }
        if i == 3 {
            ts.SetString("name", "John")
        }
    }

    mdm.CreateTable("student", sch, tx)

    exam_sch := record_manager.NewSchema()

    exam_sch.AddIntField("stuid")
    exam_sch.AddStringField("exam", 16)
    exam_sch.AddStringField("grad", 16)
    exam_layout := record_manager.NewLayoutWithSchema(exam_sch)

    ts = query.NewTableScan(tx, "exam", exam_layout)
    ts.BeforeFirst()

    ts.Insert() //指向一個可用插槽
    ts.SetInt("stuid", 1)
    ts.SetString("exam", "math")
    ts.SetString("grad", "A")

    ts.Insert() //指向一個可用插槽
    ts.SetInt("stuid", 1)
    ts.SetString("exam", "algorithm")
    ts.SetString("grad", "B")

    ts.Insert() //指向一個可用插槽
    ts.SetInt("stuid", 2)
    ts.SetString("exam", "writing")
    ts.SetString("grad", "C")

    ts.Insert() //指向一個可用插槽
    ts.SetInt("stuid", 2)
    ts.SetString("exam", "physics")
    ts.SetString("grad", "C")

    ts.Insert() //指向一個可用插槽
    ts.SetInt("stuid", 3)
    ts.SetString("exam", "chemical")
    ts.SetString("grad", "B")

    ts.Insert() //指向一個可用插槽
    ts.SetInt("stuid", 3)
    ts.SetString("exam", "english")
    ts.SetString("grad", "C")

    mdm.CreateTable("exam", exam_sch, tx)

    return tx, mdm
}

然后我們用解析器解析select查詢語句生成 QueryData 對象,最后使用BasicQueryPlanner創建好執行樹和對應的 Scan 接口對象,最后我們調用 Scan 對象的 Next 接口來獲取給定字段,代碼如下:

func main() {
    //構造 student 表
    tx, mdm := createStudentTable()
    queryStr := "select name from student, exam where id = stuid and grad=\"A\""
    p := parser.NewSQLParser(queryStr)
    queryData := p.Query()
    test_planner := planner.CreateBasicQueryPlanner(mdm)
    test_plan := test_planner.CreatePlan(queryData, tx)
    test_interface := (test_plan.Open())
    test_scan, _ := test_interface.(query.Scan)
    for test_scan.Next() {
        fmt.Printf("name: %s\n", test_scan.GetString("name"))
    }

}

上面代碼運行后所得結果如下:

責任編輯:武曉燕 來源: Coding迪斯尼
相關推薦

2015-10-28 09:55:39

Swift解析生產庫

2020-09-29 12:13:46

SQL引擎底層

2018-09-12 10:58:11

NBA數據存儲

2019-04-08 14:58:36

數據庫SQL數據類型

2022-04-05 13:46:21

日志數據庫系統

2015-06-02 10:24:43

iOS網絡請求降低耦合

2015-06-02 09:51:40

iOS網絡請求封裝接口

2014-11-26 10:54:20

C#

2015-07-23 14:53:50

貝葉斯分類器

2017-03-02 13:31:02

監控系統

2019-03-01 18:50:09

SQL Server數據庫備份并壓縮

2011-07-22 16:59:30

MySQL數據庫嵌套查詢

2018-02-07 10:46:20

數據存儲

2011-08-15 16:58:34

SQL Server遠程查詢批量導入數據

2021-05-07 09:25:34

數據庫工具技術

2021-05-13 14:34:34

數據庫PolarDB

2021-12-01 06:40:32

Bind原理實現

2015-06-02 09:41:00

iOS網絡請求NSURLSessio

2011-03-07 15:54:30

2010-09-07 10:47:42

DB2數據庫
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 99re6在线 | 日韩精品区 | 91久久精品一区二区二区 | 欧美日韩国产一区二区三区 | 伊人热久久 | 欧美日韩亚洲一区 | 亚洲一区二区三区在线视频 | 国产激情视频在线观看 | 精品亚洲一区二区 | 久草新视频 | 国产一区二区精品 | 国产九九九九 | 日韩中文在线视频 | 午夜一区| 色接久久| 亚洲永久| 99精品视频在线 | 瑟瑟视频在线看 | 欧洲一区二区三区 | 精品一区二区三区电影 | 成人国产精品入口免费视频 | 浮生影院免费观看中文版 | 一级毛片视频 | 成人影院在线 | 亚洲国产欧美一区二区三区久久 | 成人动慢| 日日摸日日爽 | 人人草天天草 | 成人三级在线观看 | 亚洲精品一区二区三区中文字幕 | 拍戏被cao翻了h承欢 | 亚洲精品乱码久久久久久蜜桃91 | 五月综合久久 | 国产探花 | 日本欧美在线视频 | 日本成人片在线观看 | 91在线视频网址 | 一区二区三区视频 | 91成人免费电影 | 亚洲欧洲精品在线 | 亚洲一级黄色 |