自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

關(guān)系代數(shù)、SQL語(yǔ)句和Go語(yǔ)言示例

數(shù)據(jù)庫(kù)
近些年,數(shù)據(jù)庫(kù)領(lǐng)域發(fā)展日新月異,除傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)外,還出現(xiàn)了許多新型的數(shù)據(jù)庫(kù)。

近些年,數(shù)據(jù)庫(kù)領(lǐng)域發(fā)展日新月異,除傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)外,還出現(xiàn)了許多新型的數(shù)據(jù)庫(kù),比如:以HBase、Cassandra、MongoDB為代表的NoSQL數(shù)據(jù)庫(kù),以InfluxDB、TDEngine為代表的時(shí)序數(shù)據(jù)庫(kù),以Neo4J、Dgraph為代表的圖數(shù)據(jù)庫(kù),以Redis、Memcached等為代表的內(nèi)存數(shù)據(jù)庫(kù),以Milvus為代表的向量數(shù)據(jù)庫(kù),以CockroachDB、TiDB為代表的HTAP融合數(shù)據(jù)庫(kù)以及云原生數(shù)據(jù)庫(kù)等。各類型數(shù)據(jù)庫(kù)都有自己的優(yōu)勢(shì),開(kāi)發(fā)者可以根據(jù)應(yīng)用場(chǎng)景選擇最合適的數(shù)據(jù)庫(kù)。

不過(guò),關(guān)系型數(shù)據(jù)庫(kù)依舊是當(dāng)今最流行的數(shù)據(jù)庫(kù)管理系統(tǒng),廣泛應(yīng)用于企業(yè)應(yīng)用,也是大多數(shù)數(shù)應(yīng)用開(kāi)發(fā)人員日常接觸最多的一種數(shù)據(jù)庫(kù)類型。關(guān)系型數(shù)據(jù)庫(kù)通過(guò)關(guān)系模型和關(guān)系代數(shù)的理論基礎(chǔ),實(shí)現(xiàn)了對(duì)關(guān)系數(shù)據(jù)的高效組織和操作。但許多開(kāi)發(fā)人員在使用SQL進(jìn)行數(shù)據(jù)庫(kù)開(kāi)發(fā)時(shí),往往感到關(guān)系代數(shù)晦澀難懂,對(duì)SQL語(yǔ)句的語(yǔ)義理解不透徹,這給數(shù)據(jù)庫(kù)應(yīng)用開(kāi)發(fā)帶來(lái)了困難。

在這篇文章中,我們就來(lái)研究一下關(guān)系模型和關(guān)系代數(shù),探究其與SQL語(yǔ)句的對(duì)應(yīng)關(guān)系,并用Go語(yǔ)言代碼示例實(shí)現(xiàn)相關(guān)查詢,期望能幫助讀者增進(jìn)對(duì)關(guān)系數(shù)據(jù)庫(kù)的理解,減輕數(shù)據(jù)庫(kù)開(kāi)發(fā)痛點(diǎn),提高數(shù)據(jù)庫(kù)應(yīng)用能力。

1. 關(guān)系模型(Relational Model)

20世紀(jì)70年代,IBM研究員E.F. Codd在“A Relational Model of Data for Large Shared Data Banks”這篇論文中提出了關(guān)系模型的概念。隨后,E.F.Codd又陸續(xù)發(fā)表了多篇文章,用數(shù)學(xué)理論奠定了關(guān)系數(shù)據(jù)庫(kù)的基礎(chǔ),為關(guān)系數(shù)據(jù)庫(kù)建立了一個(gè)數(shù)據(jù)模型 —— 關(guān)系數(shù)據(jù)模型。

關(guān)系模型基于謂詞邏輯和集合論,有嚴(yán)格的數(shù)學(xué)基礎(chǔ),提供了高級(jí)別的數(shù)據(jù)抽象層次,并不規(guī)定數(shù)據(jù)存取的具體過(guò)程,而是交由DBMS(數(shù)據(jù)庫(kù)管理系統(tǒng))自己實(shí)現(xiàn)。

關(guān)系模型之所以成為DBMS領(lǐng)域的主流模型,正是由于其非常簡(jiǎn)單(相較于更早的網(wǎng)絡(luò)模型(network model)和層次模型(hierarchical model)),下面是關(guān)系模型中定義的一些概念:

  • 關(guān)系(Relation)

E.F.Codd的論文對(duì)關(guān)系(Relation)的定義是這樣的:“這里的關(guān)系是指公認(rèn)的數(shù)學(xué)意義上的關(guān)系。給定集合S1, S2, ... ,Sn(不一定互不相關(guān)),如果 R是由n元組(n-tuples)組成的集合,其中每個(gè)元組的第一個(gè)元素來(lái)自S1,第二個(gè)元素來(lái)自S2,以此類推,那么R就是這n個(gè)集合(S1~Sn)上的一個(gè)關(guān)系”。

不用你說(shuō),我也知道這段文字太過(guò)抽象!下面我盡力用一個(gè)圖來(lái)呈現(xiàn)一下Relation的含義:

我們看到,關(guān)系(Relation)是一個(gè)集合,實(shí)質(zhì)上是一個(gè)“二維表格結(jié)構(gòu)”,把上圖中不屬于R中的元組去掉,看起來(lái)可能更清晰一些:

這個(gè)結(jié)構(gòu)中的每一行就是1個(gè)n元組(n-tuples),列則是S1到Sn,一共n個(gè)列。n元組中的數(shù)據(jù)依次分別來(lái)自S1、S2、...Sn。

  • 元組(Tuple)

關(guān)系(Relation)這個(gè)“二維表格結(jié)構(gòu)”中的每一個(gè)n元組,即每一行,被稱作元組(Tuple)。

  • 屬性(Attribute)

關(guān)系(Relation)這個(gè)“二維表格結(jié)構(gòu)”中的每一列(Sn)被稱作一個(gè)屬性(Attribute)。

  • 域(Domain)

屬性可能取值的范圍被稱為該屬性的域,以圖中屬性S3為例,S3-e1、S3-e2一直到S3-ek都在該屬性的域中,顯然{S3-e1, S3-e2, ..., S3-ek}這個(gè)集合是屬性S3的域的一個(gè)子集。有個(gè)特殊的值null是所有域的一個(gè)成員,它一般表示值為"unknown"。

論文在定義關(guān)系模型時(shí),還定義了一些模型的額外特征,比如:

  • 元組的順序是不重要的;
  • 所有的元組(行)是不同的;
  • ... ...

有了關(guān)系模型的定義,接下來(lái)就可以在模型基礎(chǔ)上定義以關(guān)系操作對(duì)象的運(yùn)算了,這種運(yùn)算的集合就構(gòu)成了關(guān)系代數(shù)。

2. 關(guān)系代數(shù)(Relational Algebra)

關(guān)系代數(shù)由一系列操作組成,這些操作將一個(gè)或兩個(gè)關(guān)系作為輸入,并產(chǎn)生一個(gè)新的關(guān)系作為結(jié)果。概括來(lái)說(shuō)就是關(guān)系代數(shù)的運(yùn)算通過(guò)輸入有限數(shù)量的關(guān)系進(jìn)行運(yùn)算,運(yùn)算結(jié)果仍為關(guān)系。

關(guān)系代數(shù)定義了一些基本關(guān)系運(yùn)算和擴(kuò)展關(guān)系運(yùn)算,其中基本關(guān)系運(yùn)算包括:

  • 選擇(Selection)
  • 投影(Projection)
  • 笛卡兒積(Cartesian Product)
  • 連接(Join)
  • 除(Division)
  • 關(guān)系并(Union)
  • 關(guān)系差(Difference)

擴(kuò)展運(yùn)算包括:

  • 關(guān)系交(Intersection)
  • 重命名(Rename)
  • ... ...

注:關(guān)于關(guān)系代數(shù)的基本關(guān)系運(yùn)算與擴(kuò)展關(guān)系運(yùn)算的定義在不同書(shū)籍里或資料里有所不同。比如在《數(shù)據(jù)庫(kù)查詢優(yōu)化器的藝術(shù)》一書(shū)中,作者認(rèn)為:關(guān)系代數(shù)(Relational Algebra)是在集合代數(shù)基礎(chǔ)上發(fā)展起來(lái)的,其數(shù)據(jù)的操作可分為傳統(tǒng)的集合運(yùn)算和專門(mén)的關(guān)系運(yùn)算兩類。傳統(tǒng)的集合運(yùn)算包括并(Union)、差(Difference)、交(Intersection)和笛卡兒積(Cartesion Product),專門(mén)的關(guān)系運(yùn)算包括選擇(Select)、投影(Project)、連接(Join)和除(Division)。關(guān)系代數(shù)中五個(gè)基本的操作并(Union)、差(Difference)、笛卡兒積(Cartesion Product)、選擇(Select)和投影(Project)組成了關(guān)系代數(shù)完備的操作集。

關(guān)系代數(shù)中的一些操作(如選擇、投影和重命名操作)被稱為一元操作(unary operation),因?yàn)樗鼈冎粚?duì)一個(gè)關(guān)系進(jìn)行操作。其他操作,如關(guān)系并、笛卡爾積和關(guān)系差,則是對(duì)一對(duì)關(guān)系進(jìn)行操作,因此稱為二元操作(binary operation):

到這里,我們知道了關(guān)系模型的概念定義以及基于關(guān)系的代數(shù)運(yùn)算都有哪些。那么關(guān)系模型、代數(shù)運(yùn)算與我們?nèi)粘5年P(guān)系數(shù)據(jù)庫(kù)以及我們使用的SQL語(yǔ)句的對(duì)應(yīng)關(guān)系是什么呢?接下來(lái)我們就逐一說(shuō)明一下。

3. 關(guān)系模型與關(guān)系數(shù)據(jù)庫(kù)實(shí)現(xiàn)的對(duì)應(yīng)關(guān)系

講到這里,其實(shí)大家心里或多或少都有個(gè)數(shù)了,關(guān)系模型與關(guān)系數(shù)據(jù)庫(kù)實(shí)現(xiàn)中概念的對(duì)應(yīng)關(guān)系十分明顯:

  • 關(guān)系型數(shù)據(jù)庫(kù)中的表(table)對(duì)應(yīng)關(guān)系模型中的關(guān)系(relation);
  • 關(guān)系型數(shù)據(jù)庫(kù)中的表的記錄行(row)對(duì)應(yīng)關(guān)系模型中的元組(triple);
  • 關(guān)系型數(shù)據(jù)庫(kù)中的表的列(column)對(duì)應(yīng)關(guān)系模型中的屬性(attribute);
  • 關(guān)系型數(shù)據(jù)庫(kù)中的表的列數(shù)據(jù)類型(column type)對(duì)應(yīng)關(guān)系模型中的屬性的域(domain)。

當(dāng)然關(guān)系型數(shù)據(jù)庫(kù)與關(guān)系模型還有一些對(duì)應(yīng)關(guān)系不是本文重點(diǎn),比如:

  • 關(guān)系模型中的關(guān)系完整性約束(如實(shí)體完整性、參照完整性等)對(duì)應(yīng)于關(guān)系數(shù)據(jù)庫(kù)中的約束(如主鍵約束、外鍵約束等)。
  • 關(guān)系模型中的范式理論(如第一范式、第二范式等)對(duì)應(yīng)于關(guān)系數(shù)據(jù)庫(kù)中的數(shù)據(jù)規(guī)范化過(guò)程。

我們下面要關(guān)注的一個(gè)最重要的對(duì)應(yīng)就是關(guān)系模型中的關(guān)系代數(shù)運(yùn)算對(duì)應(yīng)于關(guān)系數(shù)據(jù)庫(kù)中的查詢操作,我們可以使用SQL語(yǔ)句來(lái)實(shí)現(xiàn)關(guān)系模型中的運(yùn)算,這也是下面我們要重點(diǎn)說(shuō)明的內(nèi)容,通過(guò)了解SQL語(yǔ)句背后實(shí)現(xiàn)的關(guān)系代數(shù)運(yùn)算的本質(zhì),將可以幫助我們更好地理解關(guān)系模型,對(duì)后續(xù)數(shù)據(jù)庫(kù)設(shè)計(jì)以及數(shù)據(jù)操作的高效性都大有裨益。

4. 關(guān)系代數(shù)與SQL的對(duì)應(yīng)關(guān)系

終于來(lái)到最重要的內(nèi)容了,其實(shí)就是通過(guò)SQL如何實(shí)現(xiàn)關(guān)系代數(shù)的操作,這也是作為應(yīng)用開(kāi)發(fā)人員最最關(guān)心的內(nèi)容。

4.1 預(yù)先定義的關(guān)系

為了便于后續(xù)的說(shuō)明,這里我們預(yù)先定義一些關(guān)系(表),它們將用在后續(xù)說(shuō)明各個(gè)關(guān)系運(yùn)算符的示例中,這些表見(jiàn)下圖:

這里包含一個(gè)學(xué)生表(Students)、一個(gè)課程清單表(Courses)以及兩年年度的選課表:CourseSelection2022和CourseSelection2023(注:這里不討論表設(shè)計(jì)的合理性)。

文中使用sqlite做為數(shù)據(jù)庫(kù)管理系統(tǒng)(DBMS)的代表,主要是為了簡(jiǎn)單,SQL標(biāo)準(zhǔn)的兼容性也不錯(cuò)。下面的Go代碼用于創(chuàng)建上圖中的表并插入樣例數(shù)據(jù):

// relational-algebra-examples/create_database/main.go

package main

import (
 "database/sql"
 "fmt"

 _ "modernc.org/sqlite"
)

func createTable(db *sql.DB, sqlStmt string) error {
 stmt, err := db.Prepare(sqlStmt)
 if err != nil {
  fmt.Println("prepare statement error:", err)
  return err
 }

 _, err = stmt.Exec()
 if err != nil {
  fmt.Println("exec prepared statement error:", err)
  return err
 }

 return nil
}

func createTables(db *sql.DB) error {
 // 創(chuàng)建Students表
 err := createTable(db, `CREATE TABLE IF NOT EXISTS Students (
    Sno INTEGER PRIMARY KEY,
    Sname TEXT,
    Gender TEXT, 
    Age INTEGER
  )`)
 if err != nil {
  fmt.Println("create table Students error:", err)
  return err
 }

 // 創(chuàng)建Courses表
 err = createTable(db, `CREATE TABLE IF NOT EXISTS Courses (
    Cno INTEGER PRIMARY KEY,
    Cname TEXT,
    Credit INTEGER
  )`)
 if err != nil {
  fmt.Println("create table Courses error:", err)
  return err
 }

 // 2022選課表
 err = createTable(db, `CREATE TABLE CourseSelection2022 (
  Sno INTEGER,
  Cno INTEGER,
  Score INTEGER,

  PRIMARY KEY (Sno, Cno),
  FOREIGN KEY (Sno) REFERENCES Students(Sno),
  FOREIGN KEY (Cno) REFERENCES Courses(Cno)
)`)
 if err != nil {
  fmt.Println("create table CourseSelection2022 error:", err)
  return err
 }

 // 2023選課表
 err = createTable(db, `CREATE TABLE CourseSelection2023 (
  Sno INTEGER,
  Cno INTEGER, 
  Score INTEGER,
  
  PRIMARY KEY (Sno, Cno),
  FOREIGN KEY (Sno) REFERENCES Students(Sno),
  FOREIGN KEY (Cno) REFERENCES Courses(Cno)
)`)

 if err != nil {
  fmt.Println("create table CourseSelection2023 error:", err)
  return err
 }
 return nil
}

func checkErr(err error) {
 if err != nil {
  panic(err)
 }
}

func insertData(db *sql.DB) {
 // 向Students表插入數(shù)據(jù)
 stmt, err := db.Prepare("INSERT INTO Students VALUES (?, ?, ?, ?)")
 checkErr(err)

 _, err = stmt.Exec(1001, "張三", "M", 20)
 checkErr(err)
 _, err = stmt.Exec(1002, "李四", "F", 18)
 checkErr(err)
 _, err = stmt.Exec(1003, "王五", "M", 19)
 checkErr(err)

 // 向Courses表插入數(shù)據(jù)
 stmt, err = db.Prepare("INSERT INTO Courses VALUES (?, ?, ?)")
 checkErr(err)

 _, err = stmt.Exec(1, "數(shù)據(jù)庫(kù)", 4)
 checkErr(err)
 _, err = stmt.Exec(2, "數(shù)學(xué)", 2)
 checkErr(err)
 _, err = stmt.Exec(3, "英語(yǔ)", 3)
 checkErr(err)

 // 插入2022選課數(shù)據(jù)
 stmt, _ = db.Prepare("INSERT INTO CourseSelection2022 VALUES (?, ?, ?)")
 _, err = stmt.Exec(1001, 1, 85)
 checkErr(err)
 _, err = stmt.Exec(1001, 2, 80)
 checkErr(err)
 _, err = stmt.Exec(1002, 1, 83)
 checkErr(err)
 _, err = stmt.Exec(1003, 1, 76)
 checkErr(err)
 // ...

 // 插入2023選課數(shù)據(jù)
 stmt, _ = db.Prepare("INSERT INTO CourseSelection2023 VALUES (?, ?, ?)")
 stmt.Exec(1001, 3, 75)
 checkErr(err)
 stmt.Exec(1002, 2, 81)
 checkErr(err)
 stmt.Exec(1003, 3, 86)
 checkErr(err)
}

func main() {
 db, err := sql.Open("sqlite", "../test.db")
 defer db.Close()
 if err != nil {
  fmt.Println("open test.db error:", err)
  return
 }

 err = createTables(db)
 if err != nil {
  fmt.Println("create table error:", err)
  return
 }

 insertData(db)
}

這里我們使用了cznic大神[3]實(shí)現(xiàn)并開(kāi)源的modernc.org/sqlite,這是一個(gè)純Go的sqlite3數(shù)據(jù)庫(kù)driver。Go社區(qū)另一個(gè)廣泛使用的sqlite3的driver庫(kù)為go-sqlite3,只不過(guò)go-sqlite3是使用cgo對(duì)sqlite3 C庫(kù)的封裝。

執(zhí)行上面go代碼,便可以建立一個(gè)名為test.db的sqlite數(shù)據(jù)庫(kù),我們通過(guò)sqlite官方的命令行工具(cli)也可以與該數(shù)據(jù)庫(kù)文件交互(這里我們使用的是容器版cli),比如:

$docker pull  nouchka/sqlite3

// cd到test.db文件路徑下

$docker run -v {test.db文件所在目錄的絕對(duì)路徑}:/root/db -it nouchka/sqlite3
SQLite version 3.40.1 2022-12-28 14:03:47
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> .open ./test.db
sqlite> .databases
main: /root/db/test.db r/w
sqlite> .tables
CourseSelection2022  Courses            
CourseSelection2023  Students
sqlite>

接下來(lái),我們就先從關(guān)系代數(shù)運(yùn)算中最容易理解的一元運(yùn)算符開(kāi)始說(shuō)起。

4.2. 選擇(Selection)

“選擇”是一元關(guān)系運(yùn)算,它的運(yùn)算符為σ,語(yǔ)義如下:

R' = σ[p](R "p") = {t | t∈R ∩ p(t) = true } // 這里用[p]表示數(shù)學(xué)符號(hào)的下標(biāo)

其中R為關(guān)系,t為元組,p是謂詞(predicate)表達(dá)式的組合,可以由一個(gè)或多個(gè)謂詞表達(dá)式構(gòu)成。

這個(gè)語(yǔ)義相對(duì)好理解一些:它對(duì)R的操作結(jié)果依然是關(guān)系R',即一個(gè)新元組集合,這個(gè)元組集合中的元組來(lái)自R,但必須滿足p(t) = true的條件。說(shuō)直白一些,就是選擇滿足給定條件的元組。下面是一個(gè)“選擇”操作的示意圖:

我們可以用下面最常見(jiàn)的SQL語(yǔ)句實(shí)現(xiàn)對(duì)單一關(guān)系(表)的選擇運(yùn)算:

SELECT * FROM R WHERE p(t) = true;

對(duì)應(yīng)Go示例的代碼片段如下:

// relational-algebra-examples/query/main.go

func doSelection(db *sql.DB) {
    rows, _ := db.Query("SELECT * FROM CourseSelection2022 where score >= 80") // p(t)為score >= 80
    var selections []CourseSelection
    for rows.Next() {
        var s CourseSelection
        rows.Scan(&s.Sno, &s.Cno, &s.Score)
        selections = append(selections, s)
    }
    fmt.Println(selections)
}

輸出結(jié)果為:

[{1001 1 85} {1001 2 80} {1002 1 83}]

4.3 投影(Projection)

“投影”也是一元關(guān)系運(yùn)算,它的運(yùn)算符為∏,語(yǔ)義如下:

R' = ∏[A1,A2,...,An](R "A1,A2,...,An") = {t[A1,A2,...,An]| t∈R } // 這里A1,A2,...,An表示從R中取出的列名

顯然和“選擇”通過(guò)謂詞表達(dá)式選元組不同,“投影”選擇一個(gè)關(guān)系中的指定列(A1,A2,...,An),即選擇需要的屬性。下面是其運(yùn)算過(guò)程的示意圖:

“投影”對(duì)應(yīng)的SQL語(yǔ)句也是我們最熟悉的語(yǔ)句:

SELECT A1, A2, ..., An FROM R;

對(duì)應(yīng)Go示例的代碼片段如下:

// relational-algebra-examples/query/main.go

func doProjection(db *sql.DB) {
    rows, _ := db.Query("SELECT Sno, Sname FROM Students") // A1 = Sno, A2 = Sname
    var students []Student
    for rows.Next() {
        var s Student
        rows.Scan(&s.Sno, &s.Sname)
        students = append(students, s)
    }
    fmt.Println(students)
}

輸出結(jié)果為:

[{1001 張三  0} {1002 李四  0} {1003 王五  0}]

不過(guò)要注意的是:取消某些關(guān)系列后可能出現(xiàn)重復(fù)行,違反了關(guān)系的定義(關(guān)系是一個(gè)元組的集合),因此必須檢查并去除結(jié)果關(guān)系中重復(fù)的元組。

4.4 運(yùn)算符的組合(Composition)

關(guān)系運(yùn)算的輸入是關(guān)系,結(jié)果也是一個(gè)關(guān)系,因此我們可以將關(guān)系運(yùn)算符組合成一個(gè)更復(fù)雜的關(guān)系運(yùn)算符表達(dá)式來(lái)實(shí)現(xiàn)更復(fù)雜的運(yùn)算。比如將上面的兩個(gè)一元關(guān)系運(yùn)算符組合在一起“先選元組,再選屬性”:

R' = ∏[A1,A2,...,An](σ[p](R "A1,A2,...,An"))

其運(yùn)算過(guò)程如下圖所示:

上述運(yùn)算符組合對(duì)應(yīng)的SQL語(yǔ)句如下:

SELECT A1, A2, ..., An FROM R where p(t) = true;

對(duì)應(yīng)Go示例的代碼片段如下:

// relational-algebra-examples/query/main.go

func doCompositionOperation(db *sql.DB) {
    rows, _ := db.Query("SELECT Sno, Sname FROM Students where age >= 20")
    var students []Student
    for rows.Next() {
        var s Student
        rows.Scan(&s.Sno, &s.Sname)
        students = append(students, s)
    }
    fmt.Println(students)
}

輸出結(jié)果為:

[{1001 張三  0}]

無(wú)論是選擇運(yùn)算還是投影運(yùn)算,亦或是組合之后的運(yùn)算,理解起來(lái)都相對(duì)容易,因?yàn)橹簧婕耙粋€(gè)“關(guān)系”。接下來(lái)我們就看一下涉及兩個(gè)關(guān)系的二元運(yùn)算符,我們先來(lái)看看集合運(yùn)算。

4.5 關(guān)系交(Intersection)

如果沒(méi)有記錯(cuò),我們是在高中學(xué)習(xí)的集合代數(shù)。那時(shí)定義兩個(gè)集合的交集運(yùn)算是這樣的:

對(duì)于集合A和B,其交運(yùn)算(Intersction)為:

A ∩ B = { x | x ∈ A且 x ∈ B}

用一個(gè)一維空間的數(shù)的集合的例子來(lái)說(shuō),就是當(dāng)A = {1, 2, 3, 4, 5},B = { 3, 5, 6, 9}時(shí),A ∩ B = {3, 5}。我們通常用維恩圖來(lái)示意集合運(yùn)算:

在關(guān)系模型中,元組是一維集合,關(guān)系是元組的集合,即是一個(gè)二維集合,那么基于關(guān)系的交運(yùn)算就要有一個(gè)前提:那就是參與運(yùn)算的兩個(gè)關(guān)系的屬性必須是兼容的。

兩個(gè)關(guān)系的屬性兼容需滿足以下條件:

  • 屬性數(shù)量相同

兩個(gè)關(guān)系中的屬性數(shù)量必須相同。

  • 屬性類型相同或可轉(zhuǎn)換

兩個(gè)關(guān)系中對(duì)應(yīng)位置的屬性類型必須相同或可以通過(guò)類型轉(zhuǎn)換進(jìn)行兼容。例如,一個(gè)關(guān)系中的屬性類型是整數(shù),而另一個(gè)關(guān)系中的屬性類型是浮點(diǎn)數(shù),這種情況下屬性類型是兼容的,因?yàn)檎麛?shù)可以隱式轉(zhuǎn)換為浮點(diǎn)數(shù)。

  • 屬性名稱可以不同

兩個(gè)關(guān)系中對(duì)應(yīng)位置的屬性名稱可以不同,只要它們的屬性類型兼容即可。屬性名稱的不同不會(huì)影響屬性兼容性。

在關(guān)系模型中,兩個(gè)關(guān)系的屬性兼容性是判斷兩個(gè)關(guān)系是否可以進(jìn)行某些操作(包括集合操作)的重要條件之一。

回到集合運(yùn)算,如果兩個(gè)關(guān)系的屬性不兼容,則這兩個(gè)關(guān)系無(wú)法進(jìn)行集合運(yùn)算,比如Students表和Courses表的屬性個(gè)數(shù)不同,如果對(duì)它們進(jìn)行關(guān)系交運(yùn)算,會(huì)導(dǎo)致報(bào)錯(cuò):

SELECT * FROM Students INTERSECT SELECT * FROM Courses; 
Parse error: SELECTs to the left and right of INTERSECT do not have the same number of result columns

介紹完集合運(yùn)算的前提后,我們?cè)賮?lái)看關(guān)系交運(yùn)算,其語(yǔ)義入下:

R' = R1 ∩ R2

即兩個(gè)關(guān)系R1和R2在屬性兼容的前提下進(jìn)行關(guān)系交運(yùn)算的結(jié)果為返回兩個(gè)關(guān)系中相同的元組。

關(guān)系交運(yùn)算對(duì)應(yīng)的SQL語(yǔ)句如下:

SELECT * FROM R1 INTERSECT SELECT * FROM R2;

對(duì)應(yīng)Go示例的代碼片段如下:

// relational-algebra-examples/query/main.go

func doIntersection(db *sql.DB) {
    rows, _ := db.Query("SELECT * FROM CourseSelection2022 INTERSECT SELECT * FROM CourseSelection2023")
    var selections []CourseSelection
    for rows.Next() {
        var s CourseSelection
        rows.Scan(&s.Sno, &s.Cno, &s.Score)
        selections = append(selections, s)
    }
    fmt.Println(selections)
}

由于CourseSelection2022和CourseSelection2023這兩個(gè)關(guān)系沒(méi)有相同元組,所以上述Go程序輸出的結(jié)果為空。

4.6 關(guān)系并(Union)

和關(guān)系交一樣,兩個(gè)關(guān)系進(jìn)行關(guān)系并運(yùn)算的前提也是屬性兼容。關(guān)系并運(yùn)算的語(yǔ)義如下:

R' = R1 ∪ R2

即兩個(gè)關(guān)系R1和R2在屬性兼容的前提下進(jìn)行關(guān)系并運(yùn)算的結(jié)果為返回兩個(gè)關(guān)系中的所有元組,但要去除重復(fù)元組。

關(guān)系并對(duì)應(yīng)的SQL語(yǔ)句如下:

SELECT * FROM R1 UNION SELECT * FROM R2;

對(duì)應(yīng)Go示例的代碼片段如下:

// relational-algebra-examples/query/main.go

func doUnion(db *sql.DB) {
    rows, _ := db.Query("SELECT * FROM CourseSelection2022 UNION SELECT * FROM CourseSelection2023")
    var selections []CourseSelection
    for rows.Next() {
        var s CourseSelection
        rows.Scan(&s.Sno, &s.Cno, &s.Score)
        selections = append(selections, s)
    }
    fmt.Println(selections)
}

CourseSelection2022和CourseSelection2023這兩個(gè)關(guān)系沒(méi)有重復(fù)元組,所有關(guān)系并運(yùn)算后得到的結(jié)果關(guān)系中包含了這兩個(gè)關(guān)系的全部元組,上述程序的輸出結(jié)果為:

[{1001 1 85} {1001 2 80} {1001 3 75} {1002 1 83} {1002 2 81} {1003 1 76} {1003 3 86}]

4.7 關(guān)系差(Difference)

在集合代數(shù)中,對(duì)于集合A和B,其差運(yùn)算為:

A - B = { x | x ∈ A且 x ? B}

即從A集合中排除掉B集合中的元素。

在關(guān)系模型中,關(guān)系差運(yùn)算即是從一個(gè)關(guān)系中排除另一個(gè)關(guān)系中的元組,其語(yǔ)義如下:

R' = R1-R2={t|t∈R1 ∩ t?R2} // t為關(guān)系中的元組

在SQL中,我們可以用NOT IN實(shí)現(xiàn):

SELECT * FROM R1 WHERE A1 NOT IN (SELECT A1 FROM R2 WHERE 條件)

下面是對(duì)應(yīng)的Go語(yǔ)言代碼片段:

// relational-algebra-examples/query/main.go

func doDifference(db *sql.DB) {
    rows, _ := db.Query("SELECT * FROM CourseSelection2022 WHERE Cno NOT IN (SELECT Cno FROM CourseSelection2023)")
    var selections []CourseSelection
    for rows.Next() {
        var s CourseSelection
        rows.Scan(&s.Sno, &s.Cno, &s.Score)
        selections = append(selections, s)
    }
    fmt.Println(selections)
}

這段示例的含義是選出CourseSelection2022的元組,但去掉Cno值在CourseSelection2023出現(xiàn)過(guò)的元組。下面是運(yùn)行結(jié)果:

[{1001 1 85} {1002 1 83} {1003 1 76}]

注意:關(guān)系差運(yùn)算的前提也是兩個(gè)關(guān)系的屬性兼容。

最后看看略復(fù)雜的二元運(yùn)算符:笛卡爾積和連接。

4.8 笛卡爾積(Cartesian-product)

在關(guān)系代數(shù)中,關(guān)系積,即笛卡爾積(Cartesian Product)這種運(yùn)算(也被稱為關(guān)系叉乘)用于取兩個(gè)關(guān)系的所有可能的組合。它的數(shù)學(xué)語(yǔ)義可以描述為:給定關(guān)系R1和R2,它們的笛卡爾積結(jié)果是一個(gè)新的關(guān)系,其中的元組由R1中的每個(gè)元組與R2中的每個(gè)元組的組合構(gòu)成。

在SQL中,笛卡爾積可以通過(guò)使用CROSS JOIN關(guān)鍵字來(lái)實(shí)現(xiàn):

SELECT * FROM R1 CROSS JOIN R2;

也可以通過(guò)下面SQL語(yǔ)句來(lái)實(shí)現(xiàn):

SELECT R1.*, R1.* FROM R1, R2;

對(duì)應(yīng)的Go代碼片段如下:

// relational-algebra-examples/query/main.go

// StudentCourse結(jié)果
type StudentCourse struct {
    Sno    int
    Sname  string
    Gender string
    Age    int
    Cno    int
    Cname  string
    Credit int
}

func doCartesianProduct(db *sql.DB) {
    rows, _ := db.Query("SELECT * FROM Students CROSS JOIN Courses")
 // rows, _ := db.Query("SELECT Students.*, Courses.* FROM Students, Courses")
    var selections []StudentCourse
    for rows.Next() {
        var s StudentCourse
        rows.Scan(&s.Sno, &s.Sname, &s.Gender, &s.Age, &s.Cno, &s.Cname, &s.Credit)
        selections = append(selections, s)
    }
    fmt.Println(len(selections))
    fmt.Println(selections)
}

示例的運(yùn)行結(jié)果如下:

9
[{1001 張三 M 20 1 數(shù)據(jù)庫(kù) 4} {1001 張三 M 20 2 數(shù)學(xué) 2} {1001 張三 M 20 3 英語(yǔ) 3} {1002 李四 F 18 1 數(shù)據(jù)庫(kù) 4} {1002 李四 F 18 2 數(shù)學(xué) 2} {1002 李四 F 18 3 英語(yǔ) 3} {1003 王五 M 19 1 數(shù)據(jù)庫(kù) 4} {1003 王五 M 19 2 數(shù)學(xué) 2} {1003 王五 M 19 3 英語(yǔ) 3}]

我們看到對(duì)Students和Courses兩個(gè)關(guān)系(表)進(jìn)行笛卡爾積運(yùn)算后,結(jié)果包含了Students中的每個(gè)元組與Courses中的每個(gè)元組進(jìn)行組合的結(jié)果(3x3=9個(gè))。

需要注意的是,由于笛卡爾積可能導(dǎo)致非常大的結(jié)果集,因此在實(shí)際使用中應(yīng)謹(jǐn)慎使用,并且通常需要與其他運(yùn)算符和條件結(jié)合使用,以限制結(jié)果的大小和提高查詢效率。通常我們會(huì)用連接來(lái)達(dá)到這些目的。

4.9 連接(Join)

連接(Join)運(yùn)算(?)是從兩個(gè)關(guān)系的笛卡兒積中選取屬性間滿足一定條件的元組形成一個(gè)新的關(guān)系,即將笛卡爾積和選擇(selection)運(yùn)算合并達(dá)到一個(gè)操作中。從這個(gè)角度來(lái)看,笛卡爾積可以視為一種無(wú)條件的連接。

連接代數(shù)運(yùn)算符是關(guān)系代數(shù)中很有用的關(guān)系代數(shù)運(yùn)算符,也是日常經(jīng)常使用的運(yùn)算符,它有很多種不同的子類別,下面我們分別看看各種子類型的語(yǔ)義、SQL語(yǔ)句以及對(duì)應(yīng)的Go代碼示例。

4.9.1 等值連接(Equijoin)

等值連接是通過(guò)比較兩個(gè)關(guān)系(表)之間的屬性值是否相等來(lái)進(jìn)行連接的操作。連接條件使用等號(hào)(=)來(lái)比較屬性值的相等性。

我們直接看Go示例:

// relational-algebra-examples/query/main.go

func dumpOperationResult(operation string, rows *sql.Rows) {
    cols, _ := rows.Columns()

    w := tabwriter.NewWriter(os.Stdout, 0, 2, 1, ' ', 0)
    defer w.Flush()
    w.Write([]byte(strings.Join(cols, "\t")))
    w.Write([]byte("\n"))

    row := make([][]byte, len(cols))
    rowPtr := make([]any, len(cols))
    for i := range row {
        rowPtr[i] = &row[i]
    }

    fmt.Printf("\n%s operation:\n", operation)
    for rows.Next() {
        rows.Scan(rowPtr...)
        w.Write(bytes.Join(row, []byte("\t")))
        w.Write([]byte("\n"))
    }
}

func doEquijoin(db *sql.DB) {
    rows, _ := db.Query("SELECT * FROM CourseSelection2022 JOIN Students ON CourseSelection2022.Sno = Students.Sno")
    dumpOperationResult("Equijoin", rows)
}

這個(gè)示例使用等值連接將CourseSelection2022表和Students表連接起來(lái),連接條件是CourseSelection2022.Sno = Students.Sno,即學(xué)生編號(hào)相等,返回的結(jié)果將包含CourseSelection2022和Students兩個(gè)表中滿足連接條件的元組。

我們看看程序運(yùn)行的輸出結(jié)果:

Equijoin operation:
Sno  Cno Score Sno  Sname Gender Age
1001 1   85    1001 張三    M      20
1001 2   80    1001 張三    M      20
1002 1   83    1002 李四    F      18
1003 1   76    1003 王五    M      19

在這個(gè)結(jié)果中,我們看到一個(gè)“奇怪”的情況,那就是出現(xiàn)了兩個(gè)Sno屬性。在等值連接中,如果連接的兩個(gè)表中存在相同名稱的屬性(例如這里兩個(gè)表中都有名為"Sno"的屬性),那么在連接結(jié)果中會(huì)出現(xiàn)兩個(gè)相同名稱的屬性。

這是因?yàn)榈戎颠B接會(huì)將兩個(gè)表中具有相同連接條件的屬性進(jìn)行匹配,并將匹配成功的元組進(jìn)行組合。由于兩個(gè)表中都有名為"Sno"的屬性,因此連接結(jié)果中會(huì)保留這兩個(gè)屬性,以顯示連接操作前后的對(duì)應(yīng)關(guān)系。

為了區(qū)分來(lái)自不同表的相同屬性名,通常在連接結(jié)果中會(huì)使用表別名或表名作為前綴,以區(qū)分它們的來(lái)源。這樣可以確保結(jié)果中的屬性名稱是唯一的,避免歧義。 例如,如果在等值連接中連接了名為"CourseSelection2022"的表和名為"Students"的表,并且兩個(gè)表中都有名為"Sno"的屬性,那么連接結(jié)果中可能會(huì)出現(xiàn)類似于"CourseSelection2022.Sno"和"Students.Sno"的屬性名稱,以明確它們的來(lái)源。

需要注意的是,數(shù)據(jù)庫(kù)管理系統(tǒng)的具體實(shí)現(xiàn)和查詢工具的設(shè)置可能會(huì)影響連接結(jié)果中屬性的顯示方式,但通常會(huì)采用類似的方式來(lái)區(qū)分相同屬性名的來(lái)源。

4.9.2 自然連接(Natural Join)

自然連接是基于兩個(gè)表中具有相同屬性名的屬性進(jìn)行連接的操作,重點(diǎn)在于它會(huì)自動(dòng)匹配具有相同屬性名的屬性,并根據(jù)這些屬性的相等性進(jìn)行連接,而無(wú)需手工指定。

我們來(lái)看自然連接的Go示例:

// relational-algebra-examples/query/main.go

func doNaturaljoin(db *sql.DB) {
    rows, _ := db.Query("SELECT * FROM CourseSelection2022 NATURAL JOIN Students")
    dumpOperationResult("Naturaljoin", rows)
}

這個(gè)示例使用自然連接將CourseSelection2022表和Students表連接起來(lái),自然連接會(huì)自動(dòng)基于兩個(gè)表中所有具有相同屬性名的屬性進(jìn)行連接,返回的結(jié)果將包含CourseSelection2022和Students兩個(gè)表中所有滿足連接條件的元組,并自動(dòng)消除重復(fù)屬性,這是與等值連接的一個(gè)明顯的區(qū)別。

我們看看程序運(yùn)行的輸出結(jié)果:

Naturaljoin operation:
Sno  Cno Score Sname Gender Age
1001 1   85    張三    M      20
1001 2   80    張三    M      20
1002 1   83    李四    F      18
1003 1   76    王五    M      19

如果兩個(gè)表(比如R1和R2)有一個(gè)以上的屬性名相同,比如2個(gè)(比如:A1和A2),那就會(huì)自動(dòng)針對(duì)這兩個(gè)屬性名(一起)在兩個(gè)表中進(jìn)行等值連接:只有R2.A1 = R1.A1且R2.A2 = R1.A2時(shí),才將元組連接并放入結(jié)果關(guān)系中。

4.9.3 θ連接(Theta Join)

θ連接是一種通用的連接操作,它使用比等號(hào)更一般化的連接條件進(jìn)行連接。連接條件可以使用除了等號(hào)之外的比較運(yùn)算符(如大于、小于、不等于等)來(lái)比較兩個(gè)表之間的屬性。

我們來(lái)看θ連接的Go示例:

// relational-algebra-examples/query/main.go

func doThetajoin(db *sql.DB) {
    rows, _ := db.Query(`SELECT *
FROM CourseSelection2022
JOIN Students ON CourseSelection2022.Sno > Students.Sno`)
    dumpOperationResult("Thetajoin", rows)
}

這個(gè)示例使用Join將CourseSelection2022表和Students表連接起來(lái),連接條件是CourseSelection2022.Sno > Students.Sno,即學(xué)生編號(hào)大于學(xué)生表中的學(xué)生編號(hào),返回的結(jié)果將包含CourseSelection2022和`Students兩個(gè)表中滿足連接條件的元組。

Thetajoin operation:
Sno  Cno Score Sno  Sname Gender Age
1002 1   83    1001 張三    M      20
1003 1   76    1001 張三    M      20
1003 1   76    1002 李四    F      18

這個(gè)結(jié)果的生成過(guò)程大致如下:

  • 先看CourseSelection2022表的第一個(gè)元組,其Sno為1001,該Sno不大于Students表中的任一個(gè)Sno;
  • 再看CourseSelection2022表的第二個(gè)元組,其Sno為1002,該Sno僅大于Students表中的Sno為1001的那一個(gè)元組,于是將CourseSelection2022表的第二個(gè)元組和Students表中第一個(gè)元組連接起來(lái)作為結(jié)果表中的第一個(gè)元組;
  • 最后看CourseSelection2022表的第三個(gè)元組,其Sno為1003,該Sno大于Students表中的Sno為1001和1002的元組,于是將CourseSelection2022表的第三個(gè)元組分別和Students表中第一個(gè)和第二個(gè)元組連接起來(lái)作為結(jié)果表中的第二個(gè)和第三個(gè)元組。

4.9.4 半連接(Semi Join)

半連接是一種特殊的連接操作,它返回滿足連接條件的左側(cè)關(guān)系中的元組,并且只返回右側(cè)關(guān)系中與之匹配的屬性。半連接通常用于判斷兩個(gè)關(guān)系中是否存在匹配的元組,而不需要返回右側(cè)關(guān)系的詳細(xì)信息。

我們來(lái)看半連接的Go示例:

// relational-algebra-examples/query/main.go

func doSemijoin(db *sql.DB) {
    rows, _ := db.Query(`SELECT *
FROM Students
WHERE EXISTS (
    SELECT *
    FROM CourseSelection2022
    WHERE Students.Sno = CourseSelection2022.Sno
)`)
    dumpOperationResult("Semijoin", rows)
}

這個(gè)示例使用半連接操作,以Students表為左側(cè)關(guān)系,CourseSelection2022表為右側(cè)關(guān)系。它使用子查詢來(lái)判斷左側(cè)關(guān)系中是否存在滿足連接條件的元組,即Students.Sno = CourseSelection2022.Sno。它返回的結(jié)果將只包含滿足連接條件的Students表中的元組。

下面是程序輸出的結(jié)果:

Semijoin operation:
Sno  Sname Gender Age
1001 張三    M      20
1002 李四    F      18
1003 王五    M      19

半連接返回的結(jié)果關(guān)系中只包含左關(guān)系中的行,其中每一行只返回一次,即使在右關(guān)系中有多個(gè)匹配項(xiàng)。

4.9.5 反連接(Anti Join)

反連接是半連接的補(bǔ)集操作,它返回左側(cè)關(guān)系中不存在滿足連接條件的元組。反連接通常用于查找在左側(cè)關(guān)系中存在而在右側(cè)關(guān)系中不存在的元組。

我們來(lái)看反連接的Go示例:

// relational-algebra-examples/query/main.go

func doAntijoin(db *sql.DB) {
    rows, _ := db.Query(`SELECT *
FROM Students
WHERE NOT EXISTS (
    SELECT *
    FROM CourseSelection2022
    WHERE Students.Sno = CourseSelection2022.Sno
)`)
    dumpOperationResult("Antijoin", rows)
}

這個(gè)示例使用反連接操作,以Students表為左側(cè)關(guān)系,CourseSelection2022表為右側(cè)關(guān)系,并使用NOT EXISTS子查詢來(lái)判斷左側(cè)關(guān)系中不存在滿足連接條件的元組,即Students.Sno = CourseSelection2022.Sno。返回的結(jié)果將只包含左側(cè)關(guān)系Students表中不存在連接條件的元組。

Antijoin operation:
Sno Sname Gender Age

我們看到輸出的元組集合為空。

4.9.6 左(外)連接(Left Outer Join)

左外連接是將左側(cè)關(guān)系中的所有元組與滿足連接條件的右側(cè)關(guān)系中的元組進(jìn)行連接,并返回所有左側(cè)關(guān)系的元組。如果右側(cè)關(guān)系中沒(méi)有與左側(cè)關(guān)系匹配的元組,對(duì)應(yīng)的屬性值將為NULL。

我們來(lái)看左(外)連接的Go示例:

// relational-algebra-examples/query/main.go

func doLeftjoin(db *sql.DB) {
    rows, _ := db.Query(`SELECT *
FROM Students
LEFT JOIN CourseSelection2022 ON Students.Sno = CourseSelection2022.Sno`)
    dumpOperationResult("Leftjoin", rows)
}

這個(gè)示例使用左外連接將Students表和CourseSelection2022表連接起來(lái),其連接條件是Students.Sno = CourseSelection2022.Sno,即學(xué)生編號(hào)相等。示例的返回結(jié)果將包含Students表中的所有元組,并將滿足連接條件的CourseSelection2022表中的元組加入結(jié)果中。如果沒(méi)有匹配的元組,右側(cè)關(guān)系中的屬性值將為NULL。 ` 下面是程序輸出的結(jié)果:

Leftjoin operation:
Sno  Sname Gender Age Sno  Cno Score
1001 張三    M      20  1001 1   85
1001 張三    M      20  1001 2   80
1002 李四    F      18  1002 1   83
1003 王五    M      19  1003 1   76

4.9.7 右(外)連接(Right Outer Join)

右外連接是將右側(cè)關(guān)系中的所有元組與滿足連接條件的左側(cè)關(guān)系中的元組進(jìn)行連接,并返回所有右側(cè)關(guān)系的元組。如果左側(cè)關(guān)系中沒(méi)有與右側(cè)關(guān)系匹配的元組,對(duì)應(yīng)的屬性值將為NULL。

我們來(lái)看右(外)連接的Go示例:

// relational-algebra-examples/query/main.go

func doRightjoin(db *sql.DB) {
    rows, _ := db.Query(`SELECT *
FROM Students
RIGHT JOIN CourseSelection2022 ON Students.Sno = CourseSelection2022.Sno`)
    dumpOperationResult("Rightjoin", rows)
}

這個(gè)示例使用右外連接將Students表和CourseSelection2022表連接起來(lái),它的連接條件是Students.Sno = CourseSelection2022.Sno,即學(xué)生編號(hào)相等。返回的結(jié)果將包含CourseSelection2022表中的所有元組,并將滿足連接條件的Students表中的元組加入結(jié)果中。如果沒(méi)有匹配的元組,左側(cè)關(guān)系中的屬性值將為NULL。

下面是程序輸出的結(jié)果:

Rightjoin operation:
Sno  Sname Gender Age Sno  Cno Score
1001 張三    M      20  1001 1   85
1001 張三    M      20  1001 2   80
1002 李四    F      18  1002 1   83
1003 王五    M      19  1003 1   76

4.9.8 全連接(Full Outer Join)

全連接是將左側(cè)關(guān)系和右側(cè)關(guān)系中的所有元組進(jìn)行連接,并返回所有滿足連接條件的元組。如果左側(cè)關(guān)系或右側(cè)關(guān)系中沒(méi)有與對(duì)方匹配的元組,對(duì)應(yīng)的屬性值將為NULL。

我們來(lái)看全連接的Go示例:

// relational-algebra-examples/query/main.go

func doFulljoin(db *sql.DB) {
    rows, _ := db.Query(`SELECT *
FROM Students
FULL JOIN CourseSelection2022 ON Students.Sno = CourseSelection2022.Sno`)
    dumpOperationResult("Fulljoin", rows)
}

這個(gè)示例使用全連接將Students表和CourseSelection2022表連接起來(lái),連接條件是Students.Sno = CourseSelection2022.Sno,即學(xué)生編號(hào)相等。示例返回的結(jié)果將包含Students表和CourseSelection2022表中的所有元組,并將滿足連接條件的元組進(jìn)行組合。如果沒(méi)有匹配的元組,對(duì)應(yīng)關(guān)系中的屬性值將為NULL。

下面是程序輸出的結(jié)果:

Fulljoin operation:
Sno  Sname Gender Age Sno  Cno Score
1001 張三    M      20  1001 1   85
1001 張三    M      20  1001 2   80
1002 李四    F      18  1002 1   83
1003 王五    M      19  1003 1   76

以上就是本文要介紹的連接類型,這些連接類型提供了在關(guān)系數(shù)據(jù)庫(kù)中操作和組合表數(shù)據(jù)的靈活性,可以根據(jù)特定的需求選擇合適的連接方式來(lái)獲取所需的結(jié)果。

5. 小結(jié)

本文系統(tǒng)地介紹和講解了關(guān)系數(shù)據(jù)庫(kù)中的關(guān)系代數(shù)運(yùn)算,包括選擇、投影、連接、交、并、積等,以及關(guān)系代數(shù)的SQL實(shí)現(xiàn),并給出了Go語(yǔ)言示例。

關(guān)系模型是關(guān)系數(shù)據(jù)庫(kù)的理論基礎(chǔ),關(guān)系代數(shù)通過(guò)對(duì)關(guān)系的運(yùn)算來(lái)表達(dá)查詢,因此關(guān)系代數(shù)也構(gòu)成了SQL查詢語(yǔ)言的理論基礎(chǔ)。理解關(guān)系代數(shù)與SQL的對(duì)應(yīng)關(guān)系,可以更好地使用SQL語(yǔ)言操作關(guān)系型數(shù)據(jù)庫(kù)。

本文算是關(guān)系數(shù)據(jù)庫(kù)的入門(mén)文章,既能讓數(shù)據(jù)庫(kù)初學(xué)者快速掌握關(guān)系代數(shù),也能讓有基礎(chǔ)的讀者回顧并深入理解概念內(nèi)涵。通過(guò)閱讀學(xué)習(xí),能幫助讀者把關(guān)系代數(shù)運(yùn)用到實(shí)際數(shù)據(jù)庫(kù)應(yīng)用中,解決查詢優(yōu)化等問(wèn)題。


責(zé)任編輯:華軒 來(lái)源: TonyBai
相關(guān)推薦

2010-09-08 16:17:37

SQL循環(huán)語(yǔ)句

2010-09-25 14:59:54

SQL語(yǔ)句

2017-08-02 17:00:51

SQL關(guān)系代數(shù)數(shù)據(jù)

2010-09-17 09:35:51

SQL中if語(yǔ)句

2024-08-07 09:51:51

2010-09-07 14:45:34

sql語(yǔ)句

2011-06-02 10:20:09

SQL主從關(guān)系

2023-03-30 09:10:06

SQLSELECTFROM

2021-10-18 10:17:07

Go Golang語(yǔ)言

2010-09-07 14:36:24

SQL語(yǔ)句

2023-05-24 09:31:51

CGo

2022-02-09 16:02:26

Go 語(yǔ)言ArraySlice

2010-09-09 09:49:18

SQL函數(shù)存儲(chǔ)過(guò)程

2018-03-12 22:13:46

GO語(yǔ)言編程軟件

2010-04-19 13:50:27

Oracle調(diào)整

2018-04-19 14:54:12

2023-03-29 08:03:53

2010-09-08 17:10:24

SQL循環(huán)語(yǔ)句

2010-11-18 12:58:25

Oracle條件分支語(yǔ)

2023-11-21 15:46:13

Go內(nèi)存泄漏
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)