数据库设计太拉跨被喷了。

2021-04-23 14:19:14 +08:00
 Renco

发现都是些智障问题,总结一下。

目前统计下来应该都是类似的问题。我数据库设计确实拉跨,可能是因为之前做的东西过度松散,对于数据库设计基本都是能用就行,关联表的约束关系可能都没有做全,全靠代码来做。还有很多细节上的问题。其实每次开发都是很快很简单,但是表设计真的让我头大。

11822 次点击
所在节点    程序员
71 条回复
Renco
2021-04-23 16:49:40 +08:00
@jones2000 小公司没有 DBA,都是写业务的自己建表
CantSee
2021-04-23 16:58:40 +08:00
create_name 我可能会用 create_user
CantSee
2021-04-23 17:00:19 +08:00
更新人可能会用 up_user 或者 update_user ?
dayudayupao
2021-04-23 17:11:53 +08:00
目前在用的一套感觉比较好的命名规范
n_sol
n_ur
cd_sx
vu_ail
nu_pne
(手动狗头)
neptuno
2021-04-23 17:14:39 +08:00
五大金刚,无脑加上去就好了
status
created_at
update_at
created_user
update_user,关联直接存主键
iugo
2021-04-23 17:30:06 +08:00
created_by
Hallelu
2021-04-23 17:53:40 +08:00
今天还因为数据库设计,跟同事讨论了一下午,太拉跨了......
完全没有考虑到以后业务扩展,随便增加一个需求,就需要重新设计数据库和业务操作....
cco
2021-04-23 17:57:11 +08:00
@pckillers lst_nme,这种的都见多了。。。一度让我认为是人家手滑少打一个,原来是专门这么缩写的。
ErenJaeger
2021-04-23 18:11:50 +08:00
三范式嘛
Number13
2021-04-23 18:49:56 +08:00
我看没人说这个,但是遵守这个比较好
数据库的 ACID 四原则及:
事物的原子性(Atomic)、一致性(Consistent)、独立性(Isolated)及持久性(Durable)。
1.事务的原子性是指一个事务要么全部执行,要么不执行.也就是说一个事务不可能只执行了一半就停止了.比如你从取款机取钱,这个事务可以分成两个步骤:1 划卡,2 出钱.不可能划了卡,而钱却没出来.这两步必须同时完成.要么就不完成.
2.事务的一致性是指事务的运行并不改变数据库中数据的一致性.例如,完整性约束了 a+b=10,一个事务改变了 a,那么 b 也应该随之改变.
3.事务的独立性是指两个以上的事务不会出现交错执行的状态.因为这样可能会导致数据不一致.
4.事务的持久性是指事务运行成功以后,就系统的更新是永久的.不会无缘无故的回滚.
摘抄自[传送门-数据库的 ACID 四原则]( https://blog.csdn.net/tiercel2008/article/details/6871961)
mengdodo
2021-04-23 19:15:28 +08:00
写好字段描述,status 各种条件描述,不然,我原地爆炸给你看
xiaochong0302
2021-04-23 19:34:01 +08:00
参考一下我的模型文件:[app/Models/Course.php]( https://gitee.com/koogua/course-tencent-cloud/blob/master/app/Models/Course.php)

我个人觉得不用写注释都能看得一清二楚

```
<?php

namespace App\Models;

use App\Caches\MaxCourseId as MaxCourseIdCache;
use App\Services\Sync\CourseIndex as CourseIndexSync;
use App\Services\Sync\CourseScore as CourseScoreSync;
use Phalcon\Mvc\Model\Behavior\SoftDelete;
use Phalcon\Text;

class Course extends Model
{

/**
* 模型
*/
const MODEL_VOD = 1; // 点播
const MODEL_LIVE = 2; // 直播
const MODEL_READ = 3; // 图文
const MODEL_OFFLINE = 4; // 面授

/**
* 级别
*/
const LEVEL_ENTRY = 1; // 入门
const LEVEL_JUNIOR = 2; // 初级
const LEVEL_MEDIUM = 3; // 中级
const LEVEL_SENIOR = 4; // 高级

/**
* @var array
*
* 点播扩展属性
*/
protected $_vod_attrs = [
'duration' => 0,
];

/**
* @var array
*
* 直播扩展属性
*/
protected $_live_attrs = [
'start_date' => '',
'end_date' => '',
];

/**
* @var array
*
* 图文扩展属性
*/
protected $_read_attrs = [
'duration' => 0,
'word_count' => 0,
];

/**
* @var array
*
* 面授扩展属性
*/
protected $_offline_attrs = [
'start_date' => '',
'end_date' => '',
'user_limit' => 30,
'location' => '',
];

/**
* 主键编号
*
* @var int
*/
public $id = 0;

/**
* 标题
*
* @var string
*/
public $title = '';

/**
* 封面
*
* @var string
*/
public $cover = '';

/**
* 简介
*
* @var string
*/
public $summary = '';

/**
* 关键字
*
* @var string
*/
public $keywords = '';

/**
* 详情
*
* @var string
*/
public $details = '';

/**
* 主分类编号
*
* @var int
*/
public $category_id = 0;

/**
* 主教师编号
*
* @var int
*/
public $teacher_id = 0;

/**
* 原始价格
*
* @var float
*/
public $origin_price;

/**
* 优惠价格
*
* @var float
*/
public $market_price = 0.00;

/**
* 会员价格
*
* @var float
*/
public $vip_price = 0.00;

/**
* 学习期限(月)
*
* @var int
*/
public $study_expiry = 12;

/**
* 退款期限(天)
*
* @var int
*/
public $refund_expiry = 7;

/**
* 用户评价
*
* @var float
*/
public $rating = 5.00;

/**
* 综合得分
*
* @var float
*/
public $score = 0.00;

/**
* 模式类型
*
* @var int
*/
public $model = self::MODEL_VOD;

/**
* 难度级别
*
* @var int
*/
public $level = self::LEVEL_JUNIOR;

/**
* 扩展属性
*
* @var array|string
*/
public $attrs = [];

/**
* 推荐标识
*
* @var int
*/
public $featured = 0;

/**
* 发布标识
*
* @var int
*/
public $published = 0;

/**
* 删除标识
*
* @var int
*/
public $deleted = 0;

/**
* 资源数
*
* @var int
*/
public $resource_count = 0;

/**
* 学员数
*
* @var int
*/
public $user_count = 0;

/**
* 课时数
*
* @var int
*/
public $lesson_count = 0;

/**
* 套餐数
*
* @var int
*/
public $package_count = 0;

/**
* 咨询数
*
* @var int
*/
public $consult_count = 0;

/**
* 评价数
*
* @var int
*/
public $review_count = 0;

/**
* 收藏数
*
* @var int
*/
public $favorite_count = 0;

/**
* 创建时间
*
* @var int
*/
public $create_time = 0;

/**
* 更新时间
*
* @var int
*/
public $update_time = 0;

public function getSource(): string
{
return 'kg_course';
}

public function initialize()
{
parent::initialize();

$this->keepSnapshots(true);

$this->addBehavior(
new SoftDelete([
'field' => 'deleted',
'value' => 1,
])
);
}

public function beforeCreate()
{
if (empty($this->attrs)) {
if ($this->model == self::MODEL_VOD) {
$this->attrs = $this->_vod_attrs;
} elseif ($this->model == self::MODEL_LIVE) {
$this->attrs = $this->_live_attrs;
} elseif ($this->model == self::MODEL_READ) {
$this->attrs = $this->_read_attrs;
} elseif ($this->model == self::MODEL_OFFLINE) {
$this->attrs = $this->_offline_attrs;
}
}

if (is_array($this->attrs)) {
$this->attrs = kg_json_encode($this->attrs);
}

if (empty($this->cover)) {
$this->cover = kg_default_course_cover_path();
} elseif (Text::startsWith($this->cover, 'http')) {
$this->cover = self::getCoverPath($this->cover);
}

$this->create_time = time();
}

public function beforeUpdate()
{
if (time() - $this->update_time > 3 * 3600) {
$sync = new CourseIndexSync();
$sync->addItem($this->id);

$sync = new CourseScoreSync();
$sync->addItem($this->id);
}

if (Text::startsWith($this->cover, 'http')) {
$this->cover = self::getCoverPath($this->cover);
}

if (empty($this->summary)) {
$this->summary = kg_parse_summary($this->details);
}

if (is_array($this->attrs)) {
$this->attrs = kg_json_encode($this->attrs);
}

if (empty($this->origin_price)) {
$this->origin_price = 1.5 * $this->market_price;
}

if ($this->deleted == 1) {
$this->published = 0;
}

$this->update_time = time();
}

public function afterCreate()
{
$cache = new MaxCourseIdCache();

$cache->rebuild();
}

public function afterFetch()
{
$this->origin_price = (float)$this->origin_price;
$this->market_price = (float)$this->market_price;
$this->vip_price = (float)$this->vip_price;
$this->rating = (float)$this->rating;
$this->score = (float)$this->score;

if (!Text::startsWith($this->cover, 'http')) {
$this->cover = kg_cos_course_cover_url($this->cover);
}

if (is_string($this->attrs)) {
$this->attrs = json_decode($this->attrs, true);
}
}

public static function getCoverPath($url)
{
if (Text::startsWith($url, 'http')) {
return parse_url($url, PHP_URL_PATH);
}

return $url;
}

public static function modelTypes()
{
return [
self::MODEL_VOD => '点播',
self::MODEL_LIVE => '直播',
self::MODEL_READ => '图文',
self::MODEL_OFFLINE => '面授',
];
}

public static function levelTypes()
{
return [
self::LEVEL_ENTRY => '入门',
self::LEVEL_JUNIOR => '初级',
self::LEVEL_MEDIUM => '中级',
self::LEVEL_SENIOR => '高级',
];
}

public static function sortTypes()
{
return [
'score' => '综合',
'rating' => '好评',
'latest' => '最新',
'popular' => '最热',
'featured' => '推荐',
'free' => '免费',
];
}

public static function studyExpiryOptions()
{
return [
1 => '1 个月',
3 => '3 个月',
6 => '6 个月',
12 => '12 个月',
36 => '36 个月',
];
}

public static function refundExpiryOptions()
{
return [
0 => '0 天',
7 => '7 天',
14 => '14 天',
30 => '30 天',
90 => '90 天',
180 => '180 天',
];
}

}
```
icylogic
2021-04-23 19:41:35 +08:00
所以 lst name 是啥意思?
ljhrot
2021-04-23 19:52:49 +08:00
@icylogic lst -> 1st -> first name (逃
xuanbg
2021-04-23 19:52:52 +08:00
update_xxx 就没必要了,更新又不会是唯一的。我都是通过统一的操作日志进行审计的。
IvanLi127
2021-04-23 20:49:21 +08:00
上 orm 框架,学习下 orm 框架采用的命名规则
vivisidea
2021-04-23 23:01:28 +08:00
lst_name 节省了一个字符,结果引发各种猜

last_name?
list_name?
kylix
2021-04-23 23:10:35 +08:00
若是用拼音首字母作字段名,怕不是要被喷的体无完肤~



我干过 /狗头🐶
h82258652
2021-04-23 23:40:12 +08:00
codefirst 一把梭不香么 /狗头
iseki
2021-04-24 00:28:49 +08:00
存 name 不存 id 这完全不可取,虽然逻辑上正确的做法是存 id 不存 name,但是考虑到某些场景下某些数据库拉垮的性能存上 name 也不是不行,不过还是先不要过早优化比较好吧,感觉如果不想连表 join,那就是把 name 塞 Redis 都不是不可以…
看起来加 Redis 加了缓存好麻烦,实际上在数据表里事实上缓存了 name 更麻烦…完全和数据库的设计思想对着干不讨好

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://yangjunhui.monster/t/772712

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX