数据库中的计算字段 - Laravel

时间:2018-03-08 17:52:55

标签: laravel

在数据库中存储计算字段的最佳做法是什么。

例如,假设一个表有字段高度,重量,bmi

用户输入身高体重值并自动填充bmi字段。如何用表格实现这一点。

bmi的公式 $ bmi =体重/(身高*身高)

尝试以下方法 档案模型

protected $table = 'profiles';

protected $fillable = ['user_id', 'weight', 'height', 'dob', 'age', 'bmi'];

public function user(){
  return $this->belongsTo(User::class, 'user_id');
}

protected static function boot() {
  parent::boot();

  static::saving(function($model){
      $model->bmi = $model->weight / ($model->height * $model->height);
      $model->age = (date('Y') - date('Y',strtotime($model->dob)));
  });
}

配置文件控制器

public function store(Request $request)
  {
      $profile = new Profile();
      $profile->weight = $request->get('weight');
      $profile->height = $request->get('height');
      $profile->dob = $request->get('dob');
      $profile->age;
      $profile->bmi;
      $profile->save();
      return back()->with('success', 'Your profile has been updated.');
  }

但我收到错误

Illuminate \ Database \ QueryException (42S22)
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'weight' in 'field list' (SQL: insert into `users` (`weight`, `height`, `dob`, `bmi`, `age`, `created_by`, `updated_by`, `updated_at`, `created_at`) values (60, 175, 1988-04-03, 0.0019591836734694, 30, 2, 2, 2018-03-08 20:06:02, 2018-03-08 20:06:02))

5 个答案:

答案 0 :(得分:2)

您可以在模型的boot方法中执行此操作:

protected static function boot() {
    parent::boot();

    static::saving(function($model){
        $model->bmi = $model->weight / ($model->height * $model->height);
    }); 
}

答案 1 :(得分:1)

  

在数据库中存储计算字段的最佳做法是什么。

一般情况下,。它是多余的 - 您的数据库已经拥有计算它所需的所有信息,那么为什么要存储冗余数据呢?

相反,将an accessor放在模型上:

public function getBmiAttribute() {
    return ($this->weight / ($this->height * $this->height));
}

然后,您可以$this->bmi获取计算值。

如果您必须在数据库中拥有它,请使用an Eloquent event。您可能想要加入saving事件。

答案 2 :(得分:1)

如果您的DBMS支持计算列(也称为生成的列),则可以考虑使用它们。

要点:

  • 计算列不会将数据持久化到数据库中(或者如果从技术上讲它们将由DBMS管理),那么您就不会复制任何数据或使任何冗余
  • 然后可以在应用程序的代码库之外进行计算,以供使用。例如,如果需要使用原始SQL查询来开发报告,则可以在那里进行计算。
  • 口才(Laravel的默认ORM)会选择该列,而不必在任何地方定义它。

您可以执行的代码在迁移过程中创建它。例如,我有一个用例,我想简化确定当前是否已发布某件事情的过程,而我这样做是这样的:

Schema::table('article', function (Blueprint $table) {
    $table->dateTime('published_at')->nullable();
    $table->dateTime('unpublished_at')->nullable();
});

$sql = "ALTER TABLE article ADD is_published AS CAST(CASE WHEN GETUTCDATE() BETWEEN ISNULL(published_at, '2050-01-01') AND ISNULL(unpublished_at, '2050-01-01') THEN 1 ELSE 0 END AS BIT);";
DB::connection()->getPdo()->exec($sql);

然后从数据库中检索记录后,我可以通过简单地检查...来确定它是否已发布...

if ($article->is_published) ...

如果我想从数据库中查询它,我可以轻松地做...

SELECT * FROM article WHERE is_published = 1

请注意,此语法适用于T-SQL,因为我的项目的RDBMS是MS SQL Server。然而,其他流行的DBMS有相似的特征。

  • MS SQL Server:Computed Columns

    ALTER TABLE dbo.Products添加RetailValue AS(QtyAvailable * UnitPrice * 1.35);

  • MySQL:Generated Columns

    更改表t1,将存储的c2列始终按(c1 +1)存储;

  • Oracle:Virtual Columns

    ALTER TABLE emp2 ADD(收入AS(薪金+(薪金* commission_pct)));

PostgreSQL似乎尚未支持它,但是this这样的回答提供了一个不错的解决方法。

答案 3 :(得分:0)

  

在数据库中存储计算字段的最佳做法是什么。

这取决于您的使用案例。如果您使用的是关系数据库,并且您的用例不涉及大数据(就数量,种类或速度而言),最佳做法是不存储计算字段并在飞。

如果您使用 noSQL 数据库(例如Laravel支持的MongoDB)或 Hadoop ,最佳做法是存储计算字段以避免计算时间。

坚果壳

  

这是时间复杂度和空间/存储复杂性之间的权衡。   对于大数据/ noSQL系统,存储计算字段,尤其是如果   它们在计算上很复杂。对于关系数据库,请计算   他们在运行中并保持您的数据非冗余

适用于RDBMS的Laravel解决方案:

像这样使用accessors

public function getBmiAttribute($value)
{
    return ($this->weight / ($this->height * $this->height));
}

NoSql的Laravel解决方案

像这样使用model events

protected static function boot() {
    parent::boot();

    static::saving(function($model){
        $model->bmi = $model->weight / ($model->height * $model->height);
    }); 
}

答案 4 :(得分:0)

从Laravel 5.3和MySQL 5.7开始,您可以使用Column Modifiers virtualAsstoredAs创建VIRTUAL (在读取行时进行评估)STORED (在插入或更新行时进行评估和存储)为MySQL生成的列。

在下面的示例中,我使用了virtualAs ...

    Schema::create('results', function (Blueprint $table) {
        $table->bigIncrements('id');
        ...
        $table->time('predicted_time')->nullable()->default(NULL);
        $table->time('handicap')->nullable()->default(NULL);
        $table->time('finish_time')->nullable()->default(NULL);
        $table->time('actual_time')->virtualAs('TIMEDIFF(finish_time, handicap)');
        $table->time('offset')->virtualAs('TIMEDIFF(actual_time, predicted_time)');
        ...        
    });