From 52d0c47375bffe2320c4a4c7eb1f0eec3204308f Mon Sep 17 00:00:00 2001 From: sunface Date: Mon, 4 Apr 2022 20:23:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=20[Cookbook=20-=20?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/cookbook/database/postgres.md | 122 +++++++++++++++++++++++++++ src/cookbook/database/sqlite.md | 135 ++++++++++++++++++++++++++++++ 2 files changed, 257 insertions(+) diff --git a/src/cookbook/database/postgres.md b/src/cookbook/database/postgres.md index a7be4c31..13bd37dd 100644 --- a/src/cookbook/database/postgres.md +++ b/src/cookbook/database/postgres.md @@ -1 +1,123 @@ # Postgres + +### 在数据库中创建表格 +我们通过 [postgres](https://docs.rs/postgres/0.17.2/postgres/) 来操作数据库。下面的例子有一个前提:数据库 `library` 已经存在,其中用户名和密码都是 `postgres`。 + +```rust,editable +use postgres::{Client, NoTls, Error}; + +fn main() -> Result<(), Error> { + // 连接到数据库 library + let mut client = Client::connect("postgresql://postgres:postgres@localhost/library", NoTls)?; + + client.batch_execute(" + CREATE TABLE IF NOT EXISTS author ( + id SERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + country VARCHAR NOT NULL + ) + ")?; + + client.batch_execute(" + CREATE TABLE IF NOT EXISTS book ( + id SERIAL PRIMARY KEY, + title VARCHAR NOT NULL, + author_id INTEGER NOT NULL REFERENCES author + ) + ")?; + + Ok(()) + +} +``` + +### 插入和查询 + +```rust,editable +use postgres::{Client, NoTls, Error}; +use std::collections::HashMap; + +struct Author { + _id: i32, + name: String, + country: String +} + +fn main() -> Result<(), Error> { + let mut client = Client::connect("postgresql://postgres:postgres@localhost/library", + NoTls)?; + + let mut authors = HashMap::new(); + authors.insert(String::from("Chinua Achebe"), "Nigeria"); + authors.insert(String::from("Rabindranath Tagore"), "India"); + authors.insert(String::from("Anita Nair"), "India"); + + for (key, value) in &authors { + let author = Author { + _id: 0, + name: key.to_string(), + country: value.to_string() + }; + + // 插入数据 + client.execute( + "INSERT INTO author (name, country) VALUES ($1, $2)", + &[&author.name, &author.country], + )?; + } + + // 查询数据 + for row in client.query("SELECT id, name, country FROM author", &[])? { + let author = Author { + _id: row.get(0), + name: row.get(1), + country: row.get(2), + }; + println!("Author {} is from {}", author.name, author.country); + } + + Ok(()) + +} +``` + +### 聚合数据 + +下面代码将使用降序的方式列出 [Museum of Modern Art]() 数据库中的前 7999 名艺术家的国籍分布. + +```rust,editable +use postgres::{Client, Error, NoTls}; + +struct Nation { + nationality: String, + count: i64, +} + +fn main() -> Result<(), Error> { + let mut client = Client::connect( + "postgresql://postgres:postgres@127.0.0.1/moma", + NoTls, + )?; + + for row in client.query + ("SELECT nationality, COUNT(nationality) AS count + FROM artists GROUP BY nationality ORDER BY count DESC", &[])? { + + let (nationality, count) : (Option, Option) + = (row.get (0), row.get (1)); + + if nationality.is_some () && count.is_some () { + + let nation = Nation{ + nationality: nationality.unwrap(), + count: count.unwrap(), + }; + println!("{} {}", nation.nationality, nation.count); + + } + } + + Ok(()) +} +``` + diff --git a/src/cookbook/database/sqlite.md b/src/cookbook/database/sqlite.md index 02122687..0872fbf2 100644 --- a/src/cookbook/database/sqlite.md +++ b/src/cookbook/database/sqlite.md @@ -1 +1,136 @@ # SQLite + +### 创建 SQLite 数据库 + +使用 `rusqlite` 可以创建 SQLite 数据库,[Connection::open](https://docs.rs/rusqlite/*/rusqlite/struct.Connection.html#method.open) 会尝试打开一个数据库,若不存在,则创建新的数据库。 + +> 这里创建的 `cats.db` 数据库将被后面的例子所使用 + + +```rust,editable +use rusqlite::{Connection, Result}; +use rusqlite::NO_PARAMS; + +fn main() -> Result<()> { + let conn = Connection::open("cats.db")?; + + conn.execute( + "create table if not exists cat_colors ( + id integer primary key, + name text not null unique + )", + NO_PARAMS, + )?; + conn.execute( + "create table if not exists cats ( + id integer primary key, + name text not null, + color_id integer not null references cat_colors(id) + )", + NO_PARAMS, + )?; + + Ok(()) +} +``` + +### 插入和查询 + +```rust,editable + +use rusqlite::NO_PARAMS; +use rusqlite::{Connection, Result}; +use std::collections::HashMap; + +#[derive(Debug)] +struct Cat { + name: String, + color: String, +} + +fn main() -> Result<()> { + // 打开第一个例子所创建的数据库 + let conn = Connection::open("cats.db")?; + + let mut cat_colors = HashMap::new(); + cat_colors.insert(String::from("Blue"), vec!["Tigger", "Sammy"]); + cat_colors.insert(String::from("Black"), vec!["Oreo", "Biscuit"]); + + for (color, catnames) in &cat_colors { + // 插入一条数据行 + conn.execute( + "INSERT INTO cat_colors (name) values (?1)", + &[&color.to_string()], + )?; + // 获取最近插入数据行的 id + let last_id: String = conn.last_insert_rowid().to_string(); + + for cat in catnames { + conn.execute( + "INSERT INTO cats (name, color_id) values (?1, ?2)", + &[&cat.to_string(), &last_id], + )?; + } + } + let mut stmt = conn.prepare( + "SELECT c.name, cc.name from cats c + INNER JOIN cat_colors cc + ON cc.id = c.color_id;", + )?; + + let cats = stmt.query_map(NO_PARAMS, |row| { + Ok(Cat { + name: row.get(0)?, + color: row.get(1)?, + }) + })?; + + for cat in cats { + println!("Found cat {:?}", cat); + } + + Ok(()) +} +``` + +### 使用事务 +使用 [Connection::transaction](https://docs.rs/rusqlite/*/rusqlite/struct.Connection.html#method.transaction) 可以开始新的事务,若没有对事务进行显式地提交 [Transaction::commit](https://docs.rs/rusqlite/0.27.0/rusqlite/struct.Transaction.html#method.commit),则会进行回滚。 + +下面的例子中,`rolled_back_tx` 插入了重复的颜色名称,会发生回滚。 + +```rust,editable +use rusqlite::{Connection, Result, NO_PARAMS}; + +fn main() -> Result<()> { + // 打开第一个例子所创建的数据库 + let mut conn = Connection::open("cats.db")?; + + successful_tx(&mut conn)?; + + let res = rolled_back_tx(&mut conn); + assert!(res.is_err()); + + Ok(()) +} + +fn successful_tx(conn: &mut Connection) -> Result<()> { + let tx = conn.transaction()?; + + tx.execute("delete from cat_colors", NO_PARAMS)?; + tx.execute("insert into cat_colors (name) values (?1)", &[&"lavender"])?; + tx.execute("insert into cat_colors (name) values (?1)", &[&"blue"])?; + + tx.commit() +} + +fn rolled_back_tx(conn: &mut Connection) -> Result<()> { + let tx = conn.transaction()?; + + tx.execute("delete from cat_colors", NO_PARAMS)?; + tx.execute("insert into cat_colors (name) values (?1)", &[&"lavender"])?; + tx.execute("insert into cat_colors (name) values (?1)", &[&"blue"])?; + tx.execute("insert into cat_colors (name) values (?1)", &[&"lavender"])?; + + tx.commit() +} +``` \ No newline at end of file