下一章:C语言PHP扩展高性能数据库ORM框架ycdb(2) : 构建稳定的数据库/缓存连接池
源码github地址: https://github.com/caohao-php/ycdatabase ,各位帮忙点个赞
CREATE TABLE `user_info_test` (
`uid` int(11) NOT NULL COMMENT 'userid' AUTO_INCREMENT,
`username` varchar(64) NOT NULL COMMENT 'username',
`sexuality` varchar(8) DEFAULT 'male' COMMENT 'sexuality:male - 男性 female - 女性',
`age` int(11) DEFAULT 0 COMMENT 'age',
`height` double(11,2) DEFAULT 0 COMMENT 'height of a person, 身高',
`bool_flag` int(11) DEFAULT 1 COMMENT 'flag',
`remark` varchar(11) DEFAULT NULL,
PRIMARY KEY (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='userinfo';
//// /path/to 是你的 PHP7 安装路径
$ cd ~/ycdatabase/ycdatabase_extension
$ /path/to/phpize
$ chmod +x ./configure
$ ./configure --with-php-config=/path/to/php-config
$ make
$ make install
$db_conf = array("host" => "127.0.0.1",
"username" => "root",
"password" => "test123123",
"dbname" => "userinfo",
"port" => '3306',
"option" => array(
PDO::ATTR_CASE => PDO::CASE_NATURAL,
PDO::ATTR_TIMEOUT => 2));
$ycdb = new ycdb($db_conf);
我们通过上面代码创建ycdatabase对象(ycdb),db_conf是数据库配置,包含host,username,password,dbname,port等信息,还包含option参数,这个参数是pdo的设置参数,具体您可以参考网站 http://php.net/manual/zh/pdo.setattribute.php , 例如上面代码中的PDO::ATTR_TIMEOUT是连接超时时间(秒),PDO::ATTR_CASE是强制列名为指定的大小写。
try{
$ycdb->initialize();
} catch (PDOException $e) {
echo "find PDOException when initialize\n";
var_dump($e);
exit;
}
我们可以通过exec函数直接执行sql语句,如果语句为插入语句,会返回 insert_id,如果表中没有AUTO_INCREMENT 列,insert_id 为0, 其他返回值为执行结果影响行数,以及query函数执行select语句,如果 $ret = -1 则说明 sql 执行出错,我们可以通过 $ycdb->errorCode,$ycdb->errorInfo() 分别返回错误代码、错误描述。
$insert_id = $ycdb->exec("insert into user_info_test(username, sexuality, age, height)
values('smallhow', 'male', 29, 180)");
if($insert_id == -1) {
$code = $ycdb->errorCode();
$info = $ycdb->errorInfo();
echo "code:" . $code . "\n";
echo "info:" . $info[2] . "\n";
} else {
echo $insert_id;
}
$ret = $ycdb->exec("update user_info_test set remark='test' where height>=180");
echo $ret; //ret is 3
$ret = $ycdb->query("select * from user_info_test where bool_flag=1");
echo json_encode($ret);
可以通过 errorCode() 和errorInfo() 函数获取错误码和错误信息
$code = $ycdb->errorCode();
$info = $ycdb->errorInfo();
$ycdb->select("user_info_test", "username", ["sexuality" => "male"]);
// WHERE sexuality = 'male'
$ycdb->select("user_info_test", "username", ["age" => 29]); // WHERE age = 29
$ycdb->select("user_info_test", "username", ["age[>]" => 29]); // WHERE age > 29
$ycdb->select("user_info_test", "username", ["age[>=]" => 29]); // WHERE age >= 29
$ycdb->select("user_info_test", "username", ["age[!]" => 29]); // WHERE age != 29
$ycdb->select("user_info_test", "username", ["age[<>]" => [28, 29]]); // WHERE age BETWEEN 28 AND 29
$ycdb->select("user_info_test", "username", ["age[><]" => [28, 29]]); // WHERE age NOT BETWEEN 28 AND 29
//you can use array
$data = $ycdb->select("user_info_test", "*", [
"OR" =>[
"uid" => [2, 3, 4, 7, 9],
"username" => ["Tom", "Red", "carlo"]]
]);
// WHERE uid in (2, 3, 4, 7, 9) OR username in ('Tom', 'Red', 'carlo')
//Multiple conditional query
$data = $ycdb->select("user_info_test", "*", [
"uid[!]" => 10,
"username[!]" => "James",
"height[!]" => [165, 168, 172],
"bool_flag" => true,
"remark[!]" => null
]);
// WHERE
// uid != 10 AND
// username != "James" AND
// height NOT IN ( 165, 168, 172) AND
// bool_flag = 1 AND
// remark IS NOT NULL
You can use "AND" or "OR" to make up very complex SQL statements.
$data = $ycdb->select("user_info_test", "*", [
"OR" => [
"uid[>]" => 3,
"age[<>]" => [28, 29],
"sexuality" => "female"
]
]);
// WHERE uid > 3 OR age BETWEEN 29 AND 29 OR sexuality = 'female'
$data = $ycdb->select("user_info_test", "*", [
"AND" => [
"OR" => [
"age" => 29,
"sexuality" => "female"
],
"height" => 177
]
]);
// WHERE (age = 29 OR sexuality='female') AND height = 177
//注意: 第一个 OR 被覆盖了,所以这个写法是错误的
$data = $ycdb->select("user_info_test", "*", [
"AND" => [
"OR" => [
"age" => 29,
"sexuality" => "female"
],
"OR" => [
"uid[!]" => 3,
"height[>=]" => 170
],
]
]);
// [X] SELECT * FROM user_info_test WHERE (uid != 3 OR height >= 170)
//我们可以用 # + 注释 来区分两个不同的 OR
$data = $ycdb->select("user_info_test", "*", [
"AND" => [
"OR #1" => [
"age" => 29,
"sexuality" => "female"
],
"OR #2" => [
"uid[!]" => 3,
"height[>=]" => 170
],
]
]);
// [√] SELECT * FROM user_info_test WHERE (age = 29 OR sexuality = 'female') AND (uid != 3 OR height >= 170)
LIKE USAGE [~].
$data = $ycdb->select("user_info_test", "*", [ "username[~]" => "%ide%" ]);
// WHERE username LIKE '%ide%'
$data = $ycdb->select("user_info_test", "*", [
"username[~]" => ["%ide%", "Jam%", "%ace"]
]);
// WHERE username LIKE '%ide%' OR username LIKE 'Jam%' OR username LIKE '%ace'
$data = $ycdb->select("user_info_test", "*", [ "username[!~]" => "%ide%" ]);
// WHERE username NOT LIKE '%ide%'
$ycdb->select("user_info_test", "*", [ "username[~]" => "Londo_" ]); // London, Londox, Londos...
$ycdb->select("user_info_test", "id", [ "username[~]" => "[BCR]at" ]); // Bat, Cat, Rat
$ycdb->select("user_info_test", "id", [ "username[~]" => "[!BCR]at" ]); // Eat, Fat, Hat...
$data = $ycdb->select("user_info_test", "*", [
'sexuality' => 'male',
'ORDER' => [
"age",
"height" => "DESC",
"uid" => "ASC"
],
'LIMIT' => '100', //Get the first 100 of rows (overwritten by next LIMIT)
'LIMIT' => [20, 100] //Started from the top 20 rows, and get the next 100
]);
//SELECT * FROM `user_info_test` WHERE `sexuality` = 'male' ORDER BY `age`, `height` DESC, `uid` ASC LIMIT 100 OFFSET 20
$ycdb->select("user_info_test", "sexuality,age,height", [
'GROUP' => 'sexuality',
// GROUP by array of values
'GROUP' => [
'sexuality',
'age',
'height'
],
// Must have to use it with GROUP together
'HAVING' => [
'age[>]' => 30
]
]);
//SELECT uid FROM `user_info_test` GROUP BY sexuality,age,height HAVING `age` > 30
select($table, $columns, $where)
table [string]
table name
columns [string/array]
Columns to be queried.
where (optional) [array]
The conditions of the query.
select($table, $join, $columns, $where)
table [string]
table name
join [array]
Multi-table query, can be ignored if not used.
columns [string/array]
Columns to be queried.
where (optional) [array]
The conditions of the query.
return [array]
如果返回 -1 则失败,否则返回结果数组
你可以使用*来匹配所有字段, 但如果你指名字段名可以很好的提高性能.
$datas = $ycdb->select("user_info_test", [
"uid",
"username"
], [
"age[>]" => 31
]);
// $datas = array(
// [0] => array(
// "uid" => 6,
// "username" => "Aiden"
// ),
// [1] => array(
// "uid" => 11,
// "username" => "smallhow"
// )
// )
// Select all columns
$datas = $ycdb->select("user_info_test", "*");
// Select a column
$datas = $ycdb->select("user_info_test", "username");
// $datas = array(
// [0] => "lucky",
// [1] => "Tom",
// [2] => "Red"
// )
多表查询SQL较为复杂,使用ycdb可以轻松的解决它
// [>] == RIGH JOIN
// [<] == LEFT JOIN
// [<>] == FULL JOIN
// [><] == INNER JOIN
$ycdb->select("user_info_test",
[ // Table Join Info
"[>]account" => ["uid" => "userid"], // RIGHT JOIN `account` ON `user_info_test`.`uid`= `account`.`userid`
// This is a shortcut to declare the relativity if the row name are the same in both table.
"[>]album" => "uid", //RIGHT JOIN `album` USING (`uid`)
// Like above, there are two row or more are the same in both table.
"[<]detail" => ["uid", "age"], // LEFT JOIN `detail` USING (`uid`,`age`)
// You have to assign the table with alias.
"[<]address(addr_alias)" => ["uid" => "userid"], //LEFT JOIN `address` AS `addr_alias` ON `user_info_test`.`uid`=`addr_alias`.`userid`
// You can refer the previous joined table by adding the table name before the column.
"[<>]album" => ["account.userid" => "userid"], //FULL JOIN `album` ON `account`.`userid` = `album`.`userid`
// Multiple condition
"[><]account" => [
"uid" => "userid",
"album.userid" => "userid"
]
], [ // columns
"user_info_test.uid",
"user_info_test.age",
"addr_alias.country",
"addr_alias.city"
], [ // where condition
"user_info_test.uid[>]" => 3,
"ORDER" => ["user_info_test.uid" => "DESC"],
"LIMIT" => '50'
]);
// SELECT
// user_info_test.uid,
// user_info_test.age,
// addr_alias.country,
// addr_alias.city
// FROM `user_info_test`
// RIGHT JOIN `account` ON `user_info_test`.`uid`= `account`.`userid`
// RIGHT JOIN `album` USING (`uid`)
// LEFT JOIN `detail` USING (`uid`,`age`)
// LEFT JOIN `address` AS `addr_alias` ON `user_info_test`.`uid`=`addr_alias`.`userid`
// FULL JOIN `album` ON `account`.`userid` = `album`.`userid`
// INNER JOIN `account` ON `user_info_test`.`uid`= `account`.`userid`
// AND `album`.`userid` = `account`.`userid`
// WHERE `user_info_test`.`uid` > 3
// ORDER BY `user_info_test`.`uid` DESC
// LIMIT 50
你可以使用别名,以防止字段冲突
$data = $ycdb->select("user_info_test(uinfo)", [
"[<]account(A)" => "userid",
], [
"uinfo.uid(uid)",
"A.userid"
]);
// SELECT uinfo.uid AS `uid`, A.userid
// FROM `user_info_test` AS `uinfo`
// LEFT JOIN `account` AS `A` USING (`userid`)
insert($table, $data)
table [string]
table name
data [array]
insert data
return [int]
如果返回 -1 则失败,否则返回插入insert_id,如果表中没有AUTO_INCREMENT 列,insert_id 为0
replace($table, $data)
table [string]
table name
data [array]
replace data
return [int]
如果返回 -1 则失败,否则返回插入insert_id
$data = array('username' => 'smallhow','sexuality' => 'male','age' => 35, 'height' => '168');
$insert_id = $ycdb->replace("user_info_test", $data);
if($insert_id == -1) {
$code = $ycdb->errorCode();
$info = $ycdb->errorInfo();
echo "code:" . $code . "\n";
echo "info:" . $info[2] . "\n";
} else {
echo $insert_id;
}
update($table, $data, $where)
table [string]
table name
data [array]
update data
where (optional) [array]
where condition [可选]
return [int]
如果返回 -1 则失败,否则返回更新记录数
$data = array('height' => 182,'age' => 33);
$where = array('username' => 'smallhow');
$ret = $ycdb->update("user_info_test", $data, $where);
delete($table, $where)
table [string]
table name
where (optional) [array]
where condition [可选]
return [int]
如果返回 -1 则失败,否则返回删除记录数
$where = array('username' => 'smallhow');
$ret = $ycdb->delete("user_info_test", $where);
/************** the complete example **********************
SELECT `name` AS `a`, `avatar` AS `b`, `age`
FROM `table_a` AS `a`
RIGHT JOIN `AAAA` AS `a1` USING (`id`)
LEFT JOIN `BBBB` USING (`E1`,`E2`,`E3`)
RIGHT JOIN `CCCC` AS `c1` ON `a`.`GG`=`c1`.`HH` AND `II`.`KK` =`c1`.`LL`
WHERE `user`.`email` NOT IN ('[email protected]', '[email protected]', '[email protected]') AND
`user`.`uid` < 11111 AND `uid` >= 222 AND
`uid` IS NOT NULL AND
`count` NOT IN (36, 57, 89) AND
`id` != 1 AND `int_num` != 3 AND `double_num` != 3.76 AND
`AA` LIKE '%saa%' AND `BB` NOT LIKE '%sbb' AND
(`CC` LIKE '11%' OR `CC` LIKE '22_' OR `CC` LIKE '33%') AND
(`DD` NOT LIKE '%44%' OR `DD` NOT LIKE '55%' OR `DD` NOT LIKE '66%') AND
(`EE` LIKE '%E11' AND `EE` LIKE 'E22') AND
(`FF` LIKE '%F33' OR `FF` LIKE 'F44') AND
(`GG` NOT LIKE '%G55' AND `GG` NOT LIKE 'G66') AND
(`HH` NOT LIKE 'H77' OR `HH` NOT LIKE 'H88') AND
(`II` BETWEEN 1 AND 12) AND NOT
(`LL` BETWEEN 1 AND 12) AND (
(`user_name` IS NULL OR `email` = '[email protected]') AND
(`user_name` = 'bar' OR `email` = '[email protected]')
) AND (`user_name` != 'foo' OR `promoted` != 1)
GROUP BY type,age,gender
HAVING `uid`.`num` > 111 AND `type` > 'smart' AND
`id` != 0 AND `god3` != 9.86 AND `uid` IS NOT NULL
AND `AA` LIKE 'SSA%' AND (`CC` LIKE '11%' OR `CC` LIKE '22%' OR `CC` LIKE '33%')
ORDER BY `user`.`score` , `user`.`uid` ASC, `time` DESC
LIMIT 33
*/
$table = "table_a(a)";
$join = [
"[>]AAAA(a1)" => "id",
"[<]BBBB" => ["E1", "E2", "E3"],
"[>]CCCC(c1)" => [ "GG" => "HH", "II.KK" => "LL"]
];
$columns = ["name(a)", "avatar(b)", "age"];
$where = [
"user.email[!]" => ["[email protected]", "[email protected]", "[email protected]"],
"user.uid[<]" => 11111,
"uid[>=]" => 222,
"uid[!]" => null,
"count[!]" => [36, 57, 89],
"id[!]" => true,
"int_num[!]" => 3,
"double_num[!]" => 3.76,
"AA[~]" => "%saa%",
"BB[!~]" => "%sbb",
"CC[~]" => ["11%", "22_", "33%"],
"DD[!~]" => ["%44%", "55%", "66%"],
"EE[~]" => ["AND" => ["%E11", "E22"]],
"FF[~]" => ["OR" => ["%F33", "F44"]],
"GG[!~]" => ["AND" => ["%G55", "G66"]],
"HH[!~]" => ["OR" => ["H77", "H88"]],
"II[<>]" => ["1", "12"],
"LL[><]" => ["1", "12"],
"AND #1" => [
"OR #1" => [
"user_name" => null,
"email" => "[email protected]",
],
"OR #2" => [
"user_name" => "bar",
"email" => "[email protected]"
]
],
"OR" => [
"user_name[!]" => "foo",
"promoted[!]" => true
],
'GROUP' => 'userid',
'GROUP' => ['type', 'age', 'gender'],
'HAVING' => [
"uid.num[>]" => 111,
"type[>]" => "smart",
"id[!]" => false,
"god3[!]" => 9.86,
"uid[!]" => null,
"AA[~]" => "SSA%",
"CC[~]" => ["11%", "22%", "%33"],
],
'ORDER' => [
"user.score",
"user.uid" => "ASC",
"time" => "DESC",
],
"LIMIT" => '33',
];
$ycdb->select($table, $join, $columns, $where);
$ycdb->begin();
$ret1 = $ycdb->exec("insert into user_info_test(username, sexuality, age, height) values('smallhow', 'male', 29, 180)");
$ret2 = $ycdb->exec("insert into user_info_test(username, sexuality, age, height) values('jesson', 'female', 28, 175)");
if($ret1 == -1 || $ret2 == -1 ) {
$ycdb->rollback();
} else {
$ycdb->commit()
}
我们可以以 redis,或者其他任何支持 set/get/del/expire 这4种方法的缓存系统作为介质,存储 database 返回的数据,如果不指定到期时间,默认存储到期时间为5分钟,当我们指定了缓存,如果对数据有update/delete/insert等更新操作,我们最好是传入相同的缓存key,以便ycdb能够清理缓存来保持数据的一致性
//用 redis 作为数据缓存介质
$redis = new Redis();
$redis->connect('/home/redis/pid/redis.sock');
$option = array("host" => "127.0.0.1",
"username" => "test",
"password" => "test",
"dbname" => "test",
"port" => '3306',
"cache" => $redis, //cache instance
'option' => array(
PDO::ATTR_CASE => PDO::CASE_NATURAL,
PDO::ATTR_TIMEOUT => 2));
$ycdb = new ycdb($option);
try{
$ycdb->initialize();
} catch (PDOException $e) {
echo "find PDOException when initialize\n";
exit;
}
// 保存从数据库查询的29岁用户的数据到缓存
$age = 29;
$cache_key = 'pre_cache_key_' . $age;
$data = $ycdb->select("user_info_test", "*", [
'age' => $age,
'CACHE' => ['key' => $cache_key, 'expire' => 600] //cache key an expire time (seconds)
]);
echo $redis->get($cache_key) . "\n";
// 如果修改,甚至删除29岁相关的用户数据的时候,一定记得清理缓存,以保证数据一致性
// it's best to enter the cache key to clean up the cache to keep the data consistent.
$ycdb->update("user_info_test", ['remark' => '29-year-old'], [
'age' => $age,
'CACHE' => ['key' => $cache_key] //cache key
]);
echo $redis->get($cache_key) . "\n";
//删除相关数据的时候,也需要删除缓存
$ycdb->delete("user_info_test", [
'age' => $age,
'CACHE' => ['key' => $cache_key] //cache key
]);
echo $redis->get($cache_key) . "\n";
//插入跟缓存有关的数据的时候,比如下面,插入了一个29岁的新用户,也需要删除缓存
$insert_data = array();
$insert_data['username'] = 'test';
$insert_data['sexuality'] = 'male';
$insert_data['age'] = 29;
$insert_data['height'] = 176;
$insert_id = $ycdb->insert("user_info_test", $insert_data, ['key' => $cache_key]);
echo $redis->get($cache_key) . "\n";
下一章:开源轻量级PHP数据库ORM框架ycdb(2) : 构建稳定的数据库/缓存连接池