mysql存储过程中的select into与局部变量的问题

今天解决了存储过程中的select into与局部变量的问题,原来存储过程大概这样:

1
2
3
4
5
6
7
8
# 在存储过程内部,省略
DECLARE user_uid INT;
SET user_uid = 0;
SELECT `user_uid` INTO user_uid
FROM table
WHERE ...;

结果user_uid的值一直为0,确认数据表满足条件是有一条数据的。试了很多种方法才发现是into赋值的变量不能和select的字段名相同,甚至不能用declare来定义,所以最终解决是这样:

1
2
3
4
5
6
7
8
# 注意不能定义user_uid这个局部变量
DECLARE lc_user_uid INT;
SET lc_user_uid = 0;
SELECT `user_uid` INTO lc_user_uid
FROM table
WHERE ...;

mysql存储过程管理(laravel下)

直接在mysql这个层面(例如命令行或者phpmyadmin等)管理存储过程是一个非常麻烦的事情:

  • 变更很难管理
  • 无法进行版本控制

第一步:把存储过程文件都放到git下管理

例如放在项目的sql目录下:

1
2
3
4
5
6
$ ls sql/
proc_money_appeal_payout.sql proc_money_apply_cash_payout.sql
proc_money_apply_cash_income_close.sql proc_money_reward_income.sql
proc_money_apply_cash_income_fail.sql proc_money_reward_payout.sql
proc_money_apply_cash_income_finish.sql proc_money_security_deposit_income.sql
proc_money_apply_cash_income.sql proc_money_security_deposit_payout.sql

第二步:生成导入数据库的sql文件

如果项目的存储过程比较多,每次更新时都可能会变得很麻烦,所以需要统一的导入入口,例如sql目录下生成source.sql文件,内容如下:

1
2
3
4
5
6
7
8
9
10
// vim sql/source.sql
source /home/code/ibbd/yanjiuquan-php/sql/proc_money_appeal_payout.sql;
source /home/code/ibbd/yanjiuquan-php/sql/proc_money_apply_cash_income.sql;
source /home/code/ibbd/yanjiuquan-php/sql/proc_money_apply_cash_income_close.sql;
source /home/code/ibbd/yanjiuquan-php/sql/proc_money_apply_cash_income_fail.sql;
source /home/code/ibbd/yanjiuquan-php/sql/proc_money_apply_cash_income_finish.sql;
source /home/code/ibbd/yanjiuquan-php/sql/proc_money_apply_cash_payout.sql;
source /home/code/ibbd/yanjiuquan-php/sql/proc_money_apply_cash_payout_close.sql;
source /home/code/ibbd/yanjiuquan-php/sql/proc_money_apply_cash_payout_finish.sql;
source /home/code/ibbd/yanjiuquan-php/sql/proc_money_reward_income.sql;

说明:这个source文件不应该加入git版本控制中。

这样只需要在命令行中,source这个文件即可:

1
source /path/to/source.sql

第三步:自动生成source.sql

如果手动管理这个文件,也是挺麻烦的,可以结合laravel自动生成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use Illuminate\Database\Seeder;
class ProcdureInitTableSeeder extends Seeder {
public function run()
{
$path = 'sql/proc_*.sql';
$root = getcwd();
$files = glob($path);
$source_file = $root . '/sql/source.sql';
file_put_contents($source_file, "# @desc 导入及更新存储过程结构, 生成命令:php artisan db:seed\n");
file_put_contents($source_file, "# @author Alex\n", FILE_APPEND);
file_put_contents($source_file, "# @date " . date('Y-m-d') . "\n\n", FILE_APPEND);
foreach ($files as $file) {
$sql = "source {$root}/{$file};\n";
file_put_contents($source_file, $sql, FILE_APPEND);
}
}
}

The last

每次变化的时候,还是需要手动source一下,略麻烦。。。暂时没有更好的方式。。。

laravel项目中的数据表设计规范及使用方式

字段设计规范

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 主键
$table->increments('id');
// 加入 created_at 和 updated_at 字段
$table->timestamps();
// 加入 deleted_at 字段于软删除使用
$table->softDeletes();
// 加入整数 taggable_id 与字串 taggable_type
$table->morphs('taggable');
// 加入 remember_token 使用 VARCHAR(100) NULL
$table->rememberToken();

软删除

通过软删除方式删除了一个模型后,模型中的数据并不是真的从数据库被移除。而是会设定 deleted_at时间戳。要让模型使用软删除功能,只要在模型类里加入 SoftDeletingTrait 即可:

1
2
3
4
5
6
7
8
9
use Illuminate\Database\Eloquent\SoftDeletes;
class User extends Model {
use SoftDeletes;
protected $dates = ['deleted_at'];
}

现在当您使用模型调用 delete 方法时, deleted_at字段会被更新成现在的时间戳。在查询使用软删除功能的模型时,被「删除」的模型数据不会出现在查询结果里。

1
2
3
4
5
// 强制查询软删除数据(使用 withTrashed 方法)
$users = User::withTrashed()->where('account_id', 1)->get();
// 只想查询被软删除的模型数据,可以使用 onlyTrashed 方法
$users = User::onlyTrashed()->where('account_id', 1)->get();

时间戳

默认 Eloquent 会自动维护数据库表的 created_at 和 updated_at 字段。只要把这两个「时间戳」字段加到数据库表, Eloquent 就会处理剩下的工作。

范围查询

定义范围查询

范围查询可以让您轻松的重复利用模型的查询逻辑。要设定范围查询,只要定义有 scope 前缀的模型方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
class User extends Model {
public function scopePopular($query)
{
return $query->where('votes', '>', 100);
}
public function scopeWomen($query)
{
return $query->whereGender('W');
}
}

使用范围查询

1
$users = User::popular()->women()->orderBy('created_at')->get();

动态范围查询

有时您可能想要定义可接受参数的范围查询方法。只要把参数加到方法里:

1
2
3
4
5
6
7
class User extends Model {
public function scopeOfType($query, $type)
{
return $query->whereType($type);
}
}

使用:

1
$users = User::ofType('member')->get();

使用枢纽表

如您所知,要操作多对多关联需要一个中间的数据库表。 Eloquent 提供了一些有用的方法可以和这张表互动。例如,假设 User 对象关联到很多 Role 对象。取出这些关联对象时,我们可以在关联模型上取得 pivot 数据库表的数据:

1
2
3
4
5
6
$user = User::find(1);
foreach ($user->roles as $role)
{
echo $role->pivot->created_at;
}

注意我们取出的每个 Role 模型对象会自动给一个 pivot 属性。这属性包含了枢纽表的模型数据,可以像一般的 Eloquent 模型一样使用。

默认 pivot 对象只会有关联键的属性。如果您想让 pivot 可以包含其他枢纽表的字段,可以在定义关联方法时指定那些字段:

1
return $this->belongsToMany('App\Role')->withPivot('foo', 'bar');

其他操作:

1
2
3
4
5
// 删除枢纽表的关联数据
User::find(1)->roles()->detach();
// 更新枢纽表的数据
User::find(1)->roles()->updateExistingPivot($roleId, $attributes);

集合

所有 Eloquent 查询返回的数据,如果结果多于一条,不管是经由 get 方法或是 relationship,都会转换成集合对象返回。这个对象实现了 IteratorAggregate PHP 接口,所以可以像数组一般进行遍历。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 确认集合中里是否包含特定键值
$roles = User::find(1)->roles;
if ($roles->contains(2))
{
//
}
// 集合遍历
$roles = $user->roles->each(function($role)
{
//
});
// 集合过滤
$users = $users->filter(function($user)
{
return $user->isAdmin();
});

待续。。。

提升接口可扩展性的一点小技巧

现实情况

接口涉及到三个方面:

  • 服务器端,简称s;
  • 自己封装的浏览器,简称b;
  • 前端js,简称j。

b启动之后,会定时轮询s,获取相应的信息,并把其中的一部分反馈给j。开始时对s的返回值设计如下:

1
2
3
4
5
6
7
{
status: 0, // 标识成功还是失败(0表示成功,非0表示失败)
data: { // 打印数据
photo: 123, // 【整型】照片ID
preview: 'http://...', // 【字符串】预览图地址:给前端预览使用
}
}

其中,preview字段是缩略图的地址,b在接收到之后,需要传递给前端的js去展示。理想情况下,这可以运行得很好,但是b端一旦固定下来,是很难改的,如果我们需要传更多的数据给j端怎么办?

READ MORE

vim实用技巧之快速移动与跳转

动作命令是vim操作中最重要的一些命令。不仅可以实现快速移动,还能与操作符进行配合使用。

READ MORE

封装redis基础类库

开发的时候,需要用到缓存,经常需要封装各种缓存类,有时为了所谓的缓存通用了,我们对redis做了统一性的封装,结果只能用到redis最基本的kv结构,大大浪费了。

所以封装redis的时候,应该恰到好处就好了,phpredis本身的api就已经挺好的了。

下面是一个封装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?php
/**
* redis初始化类库
* @author Alex <cyy0523xc@gmail.com>
* @copyright IBBD.net
* @see
* @todo
* @version 20141104
*/
namespace Common\Model;
class RedisModel
{
// redis对象
private $handler = null;
/**
* 获取Redis缓存对象
* @param array $options redis连接所需要的参数
* @return Redis
*/
public static function getInstance(array $options = null)
{
static $instance = array();
if(empty($options)) {
// 加载配置文件
$options = C('CACHE.REDIS_CONFIG');
}
$guid = to_guid_string($options);
if (!isset($instance[$guid])) {
$obj = new self($options);
$instance[$guid] = $obj->getRedis();
}
return $instance[$guid];
}
/**
* 构造函数
* @param array $options redis需要的参数
*/
protected function __construct(array $options)
{
if ( !extension_loaded('redis') ) {
error_log('NOT_SUPPORT:redis');
}
$func = $options['persistent'] ? 'pconnect' : 'connect';
$this->handler = new \Redis;
$options['timeout'] === false ?
$this->handler->$func($options['host'], $options['port']) :
$this->handler->$func($options['host'], $options['port'], $options['timeout']);
// select db
isset($options['db']) && $this->select($options['db']);
}
/**
* 获取redis处理对象
* @return Redis
*/
protected function getRedis()
{
return $this->handler;
}
}

vim实用技巧-模式2

第三章:可视模式

  • 深入理解可视模式

:h Select-mode :选择模式(特殊的可视模式)

<C-g> 在可视模式和选择模式之间切换(提示栏不同)

  • 选择高亮选区

v, V, <C-v>, gv(重选上次的高亮选区),o(激活高亮选区的活动端)

  • 重复执行面向行的可视命令 .
  • 尽量使用操作符命令,而不是可视命令

可视模式下,. 号命令可能会有异常表现。如 vitUgUit (后者是普通模式的命令)

  • 操作表格数据
  1. 列:<C-v>3jr|
  2. 行:yypVr-
  • 修改列文本

<C-v>jjeC{new text}<Esc>

  • 在行尾添加文本

<C-v>jj$A;:可视模式下,a与i无法进入插入模式

READ MORE

书籍:vim实用技巧-模式

第二章:普通模式

  • 停顿时请离开画笔
  • 构造可撤销块

u命令:撤销最新修改

一次修改:

  1. 普通模式,可视模式和命令行模式中所触发的修改命令
  2. 插入模式: i{some text}<Esc>

段落完时,可用:<Esc>o

  • 构造可重复修改块

例如在单词结尾处怎么删除单词:dbx,bdx,daw(最好)

  • 用次数做简单的运算

<C-a><C-x> :加与减

  • 能够重复就别用次数

连续删除两个单词:d2w, 2dw, dw.(可重复,避免算错数)

  • 操作 = 操作符 + 动作命令

操作符:c(修改),d,y,g~(反转大小写),gU,gu(小写),>(增加缩进),<(减少缩进),=(自动缩进)

READ MORE

书籍:vim实用技巧

以思维的速度来编辑文本!

《vim实用技巧》看了几天,今天终于看完了第一部分 模式 ,感觉收益良多,很多以前困惑的或者不懂的,有种豁然开朗的感觉。

READ MORE

周鸿祎:我的互联网方法论

昨晚读完了这本书,作为一个浸在互联网多年的老兵,本书没有太多新鲜的观点,很多在以前都看过。不过老周把他们都总结到了一本书里,浅显易懂,可以比较系统深入的做一次复习与碰撞。对于要转型的传统企业,应该有很大的用处。

READ MORE