新增章 [Cookbook - 数据库]

pull/737/head
sunface 3 years ago
parent 2d43a8e4d4
commit 52d0c47375

@ -1 +1,123 @@
# Postgres # 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<String>, Option<i64>)
= (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(())
}
```

@ -1 +1,136 @@
# SQLite # 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()
}
```
Loading…
Cancel
Save