【问题标题】:Laravel Eloquent retrive all latest itemsLaravel Eloquent 检索所有最新项目
【发布时间】:2021-02-24 22:47:42
【问题描述】:

我正在处理库存数据库。我有一些仓库和一些产品。示例数据库可以是:

请看下面的代码:

public function scopeOf($query, $warehouse, $product, $date = '')
{
    $date = ($date == '' ? Carbon::today() : $date);
    return $query
        ->where(
            [
                ['warehouse', $warehouse],
                ['product', $product],
            ])
        ->whereDate('updated_at', '<=', $date)
        ->orderByDesc('updated_at')->take(1);
}

使用此功能,我可以通过以下方式获取一个仓库中一种产品的最新数据: $data = Inventory::Of('w1', 'INJR')-&gt;get();

我需要列出所有仓库的所有最新数据。

我阅读了this,但它仅根据日期列出。就我而言,对于我需要的每个仓库和产品 获取最新的行。

我在 laravel 8 的开发模式下使用 SqlLite。

更新:

也许我的问题不清楚。 这是我的问题的工作功能

public function scopeOfList($query, $warehouseName = [], $productName = [], $date = '')
{
    $date = ($date == '' ? Carbon::today() : $date);
    $db = $query->select('product', 'warehouse')->get()->toArray();
    $products = array_unique(
        array_filter(
            array_map(function ($n) use ($productName) {
                return ($productName == [] || in_array($n['product'] ,$productName)? $n['product'] : null );
                },
                $db),
            'strlen')
    );
    $warehouses = array_unique(array_map(function ($n) use ($warehouseName)
    {
        return ($warehouseName == '' || in_array($n['warehouse'] ,$warehouseName)? $n['warehouse'] : null );

    }, $db));
    $data = [];
    foreach ($warehouses as $warehouse){
        foreach ($products as $product){
            $data[] = Inventory::
                where(
                    [
                        ['warehouse', $warehouse],
                        ['product', $product],
                    ])
                ->whereDate('updated_at', '<=', $date)
                ->orderByDesc('updated_at')->get()->first();
        }
    }
    $inventory = array_filter($data, 'strlen');
    sort($inventory);
    return $inventory;
}

用途:

    //get one/more product from one/more warehouse
    //returns latest data only
    $data = Inventory::OfList(['w1', 'w2'], ['INJR']);
    //returns latest data available before 3 days
    $data = Inventory::OfList(['w1', 'w2'], ['INJR', 'TP'], Carbon::now()->subDays(3));

    //get one product from all warehouse
    //returns latest data only
    $data = Inventory::OfList([], ['INJR']);
    //returns latest data available before 3 days
    $data = Inventory::OfList([], ['INJR'], Carbon::now()->subDays(3));

    //get all product from one warehouse
    //returns latest data only
    $data = Inventory::OfList(['w1']);
    //returns latest data available before 3 days
    $data = Inventory::OfList(['w1'], [], Carbon::now()->subDays(3));

    //get all product from all warehouse
    //returns latest data only
    $data = Inventory::OfList();
    //returns latest data available before 3 days
    $data = Inventory::OfList([], [], Carbon::now()->subDays(3));

我只需要简化scopeOfList 函数以使其灵活处理inventories 表中的大量数据。如果我在每个仓库中有 500 个仓库 500 种产品,这些产品每天都会更新,那么 1 年的数据我的 php 内存将被释放,并且执行需要相当长的时间。

【问题讨论】:

  • 你应该按仓库和产品分组
  • 我试过了,没用
  • 就我而言,对于每个仓库和产品,我需要获取最新的行。你能举个例子吗?您目前得到了什么,您的预期结果是什么?
  • @Anurat Chapanond 我已经更新了我的问题。请看一看。

标签: php database laravel eloquent


【解决方案1】:

在 SQL 中,您可以使用基于提供的$date 过滤器的exists 获取每个仓库和产品的最新记录

select a.*
from products a
where updated_at <= :date
and exists (
    select 1 
    from products b
    where b.warehouse = a.warehouse
      and b.product = a.product 
      and b.updated_at <= :date
      group by 1
      having max(b.updated_at) = a.updated_at
)

使用上面的查询生成器可以将SQL转换为

DB::table('products as a')
    ->whereDate('a.updated_at', '<=', $date)
    ->whereExists(function ($query) use ($date){
       $query->select(DB::raw(1))
             ->from('products as b')
             ->whereDate('b.updated_at', '<=', $date)
             ->whereRaw(DB::raw('b.warehouse = a.warehouse'))
             ->whereRaw(DB::raw('b.product = a.product'))
             ->groupByRaw('1')
             ->havingRaw('max(b.updated_at) = a.updated_at')
             ;
    });

如果您需要每个仓库和产品的最新记录而不指定任何 $date,则从两个子句中删除 -&gt;whereDate() 过滤器

更新:不确定您在其中应用数组过滤和映射函数产品和仓库集合的其余代码,但对于您更新的问题,您可以省略最新数据的 foreach 循环,而只使用 whereIn 产品和仓库结合whereExists子句

public function scopeOfList($query, $warehouseName = [], $productName = [], $date = '')
{
    $date = ($date == '' ? Carbon::today() : $date);
    $products = //...
    $warehouses = //...
    
    $query
        ->whereDate('a.updated_at', '<=', $date)
        ->whereIn('a.warehouse',$warehouses)
        ->whereIn('a.product',$products)
        ->whereExists(function ($query) use ($date){
           $query->select(DB::raw(1))
                 ->from('inventory as b')
                 ->whereDate('b.updated_at', '<=', $date)
                 ->whereRaw(DB::raw('a.warehouse = b.warehouse'))
                 ->whereRaw(DB::raw('a.prodcut = b.prodcut'))
                 ->groupByRaw('1')
                 ->havingRaw('max(b.updated_at) = a.updated_at')
                 ;
        });

    return $query;
}
$data = Inventory::from( 'inventory as a' )
                 ->OfList(['w1', 'w2'], ['INJR', 'TP'], Carbon::now()->subDays(3));

【讨论】:

  • 可能是我的问题不清楚。我已经更新了它。请看一下。很抱歉给您带来不便。
  • @MasumNishat 我已经更新了我的答案,请查看
  • 感谢您的回答,我试过了,也许还有一点。我收到一个错误:SQLSTATE[HY000]: General error: 1 a GROUP BY clause is required before HAVING (SQL: select * from "inventories" as "a" where strftime('%Y-%m-%d', "a"."updated_at") &lt;= cast(2021-02-25 as text) and "a"."warehouse" in (BD-KH-JH-MO-1, BD-KH-JH-MO-2) and "a"."product" in (INJR, TP) and exists (select 1 from "inventories" as "b" where strftime('%Y-%m-%d', "b"."updated_at") &lt;= cast(2021-02-25 as text) and a.warehouse = b.warehouse and a.prodcut = b.prodcut having max(b.updated_at) = a.updated_at))
  • @MasumNishat 我已经通过添加groupByRaw('1') 更新了我的答案,SQL lite 不允许有子句作为独立使用,所以我做了一个调整
  • 你知道为什么Inventory::from( 'inventory as a' )-&gt;OfList([$request-&gt;get('w')], [$id])-&gt;skip(5)-&gt;take(5)-&gt;get()会返回所有数据吗?它应该在前 5 个数据之后返回 5 个数据
【解决方案2】:

不发送查询参数并删除$query参数并使用

public function scopeOf($warehouse, $product, $date = '')
{
$date = ($date == '' ? Carbon::today() : $date);  // or instead Carbon::today() by date('Y-m-d H:i:s')
$query=DB::table('Inventory')
    ->where('warehouse','$warehouse')
    ->where('product','$product')
    ->where('updated_at','<=','$data')
    ->orderBy('updated_at','DESC')
    ->take(1)
    ->get();

return $query;
}

【讨论】:

  • 为什么在删除查询时使用范围。非常不推荐
  • 可能是我的问题不清楚。我已经更新了它。请看一下。很抱歉给您带来不便。
猜你喜欢
  • 1970-01-01
  • 2019-03-04
  • 2017-02-26
  • 2018-02-16
  • 1970-01-01
  • 1970-01-01
  • 2019-06-13
  • 2014-03-11
  • 2017-03-31
相关资源
最近更新 更多