在数据库计算的字段 - Laravel

问题描述 投票:1回答:4

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

例如,可以说表中有场身高,体重,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))
laravel
4个回答
3
投票

你可以在模型的boot方法做到这一点:

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

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

1
投票

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

在一般情况下,没有。这是多余的 - 你的数据库已经计算它所需的所有信息,那么为什么店里的冗余数据?

相反,把an accessor型号:

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

然后,您可以做$this->bmi得到的计算值。

如果你必须在数据库中有它,使用an Eloquent event。你可能会想钩到saving事件。


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服务器。然而,其他流行的DBMS有类似的功能。

  • MS SQL服务器:Computed Columns ALTER TABLE dbo.Products ADD RetailValue AS(QtyAvailable *单价* 1.35);
  • MySQL的:Generated Columns ALTER TABLE T1 ADD始终产生AS(C1 + 1)存储的列C2 INT;
  • 甲骨文:Virtual Columns ALTER TABLE EMP2 ADD(收入(工资+(薪水*佣金百分比)));

PostgreSQL的似乎并不支持它(还),但this SO答案提供了一个不错的解决方法。


0
投票

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

这取决于你的使用情况。如果您使用的是关系型数据库,以及你的使用情况不涉及大数据(在数量,种类和速度方面),最好的做法是不存储计算字段并计算它们的飞行。

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

简而言之

它的时间复杂度和空间/存储复杂性之间的权衡。对于大数据/ NoSQL系统,存储计算领域,特别是如果他们是计算复杂。对于关系数据库,计算它们的飞行,并保持数据的非冗余

Laravel解决方案RDBMS:

使用accessors像这样:

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

Laravel解决方案的NoSql

使用model events像这样:

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

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

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)');
        ...        
    });
© www.soinside.com 2019 - 2024. All rights reserved.