Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/259.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 将列更新为联接列中的值_Php_Sql_Postgresql_Laravel_Eloquent - Fatal编程技术网

Php 将列更新为联接列中的值

Php 将列更新为联接列中的值,php,sql,postgresql,laravel,eloquent,Php,Sql,Postgresql,Laravel,Eloquent,我正在规范化数据库,需要运行~630k更新。以下是我的表格的基本结构: 领域 身份证 名字 统计数据 领域 域id 以前,数据库没有domains表,域存储在多个表中,有时作为列表(JSON文本)。我将每个域迁移到domains表中,现在我需要建立与stats表的关系,该表具有domain列。我添加了domain\u id列,并试图以某种方式更新它,以匹配domains表中域的id。stats表有超过2300万行,具有约630k个唯一域(统计数据为每小时一次)。我试着运行一个foreac

我正在规范化数据库,需要运行~630k更新。以下是我的表格的基本结构:

领域

  • 身份证
  • 名字
统计数据

  • 领域
  • 域id
以前,数据库没有domains表,域存储在多个表中,有时作为列表(JSON文本)。我将每个域迁移到
domains
表中,现在我需要建立与
stats
表的关系,该表具有
domain
列。我添加了
domain\u id
列,并试图以某种方式更新它,以匹配
domains
表中域的
id
stats
表有超过2300万行,具有约630k个唯一域(统计数据为每小时一次)。我试着运行一个foreach,但每个域大约需要2秒钟,再加上大约14天的时间来运行它们

以下是我目前的代码:

首先,我从
stats
表中找到
domains
表中缺少的所有域,并将它们保存在
domains
表中

$statDomains = Stat::select('domain')->groupBy('domain')->lists('domain');
$domains = [];
foreach(array_chunk($statDomains , 1000) as $domains1k){
    $domains = array_merge($domains, Domain::whereIn('name', $domains1k)->lists('name'));
}
$missingDomains = [];
foreach(array_diff($statDomains , $domains) as $missingDomain){
    $missingDomains[] = ['name' => $missingDomain];
}

if(!empty($missingDomains)){
    Domain::insert($missingDomains);
}
接下来,我从
domains
表中获取
stats
表中存在的所有域,并用该域更新
stats
表中的所有行

$domains = [];
foreach(array_chunk($statDomains, 1000) as $domains1k){
    $domains +=Domain::whereIn('name', $domains1k)->lists('name', 'id');
}
foreach($domains as $key => $domain){
        Stat::where('domain', $domain)->update(['domain_id' => $key]);
}
我会很感激一些有说服力的、查询生成器或者只是原始SQL的东西,它们可以更快地完成更新(最多两个小时?)。我在谷歌上搜索了一下,发现了类似的问题,但无法应用到我的案例中

编辑

我正在运行建议的解决方案。与此同时,我发现迁移的另外两个部分大约需要50分钟。在第一个列表中,我有一个表
domain\u list
。它有一个包含JSON编码域的文本列
domains
。我正在将这些域移动到
domain
表中,并在
domain\u list\u domains\u map
表中创建记录。代码如下:

foreach(DomainList::all() as $domainList){
    $attach = [];
    $domains = json_decode($domainList->domains, true);
    foreach($domains as $domain){
        $model = Domain::where('name', '=', $domain)->first();
        if(is_null($model) && !is_null($domain)){
            $model = new Domain();
            $model->name = $domain;
            $model->save();
        }
        if(!is_null($model)){
            $attach[] = $model->id;
        }
    }
    if(!empty($attach)){
        foreach(array_chunk(array_unique($attach), 1000) as $attach1k){
            $domainList->domains()->attach($attach1k);
        }
    }
}
我已经注意到,我可能应该首先找到所有唯一的域并将它们插入到domains表中,但是给出了前面问题的解决方案,我觉得可能有更好的方法在原始SQL中完成所有这些。第二部分非常相似,我可以通过查看第一部分的代码来解决它。该表是categories,它还有一个包含JSON编码域的domains文本列。非常感谢您的帮助

编辑2

下面是我运行的查询,它将现有表复制到一个新表,并填充了
domain\u id
列:

CREATE TABLE "stats_new" AS SELECT
    "s"."domain",
    "d"."id" AS "domain_id"
FROM
    "stats" "s"
JOIN "domains" "d" ON ("s"."domain" = "d"."name")

忘记php,转而使用原始sql——循环中的处理记录和多个执行语句会使其速度变慢。而是直接在数据库中运行以下查询:

update stats s set domain_id=(select d.id from domains d where d.name=s.domain);

原始SQL应该快几个数量级

第1步:
插入
在表
域中插入所有域名,除非它们已经存在:

插入域(名称)
选择不同的s.domain
从统计数字s
在d.name=s.domain上左连接域d
其中d.name为空;
如果您具有并发写访问权限,则可能存在竞争条件。最简单的解决方案是专门为事务创建
。否则,您可能会在操作进行到一半时遇到一个唯一的冲突,因为一个并发事务在这两者之间提交了相同的域名。一切都将被逆转

开始;
以独占模式锁定表域;
插入域(名称)
选择不同的s.domain
从统计数字s
在d.name=s.domain上左连接域d
其中d.name为空;
犯罪
域。名称
应该是唯一的
。该约束通过列上的索引实现,这将有助于下一步的性能

第二步:
更新
要更新部分行而不是全部行,请执行以下操作:
更新所有
域\u id
,使其成为
域的外键。name
。 但不要使用相关子查询,而是使用。这里快多了

更新统计信息
设置域\u id=d.id
来自域d
其中d.name=s.domain
并且域_id为空;——假设现有ID是正确的。
然后可以删除现在冗余的列
stats.domain

ALTER TABLE stats DROP column domain;
这非常便宜。该列在系统目录中被标记为死列。在更新或清空行之前,不会删除实际列值

要进一步提高性能,请直接删除操作不需要的所有索引,然后再创建它们-所有索引都在同一事务中

,要批量更新n行,请执行以下操作:

,因为您在评论中澄清了您正在更新所有行,所以如果约束和访问模式允许的话,创建一个新表会便宜很多

创建一个全新的表,删除旧表并重命名新表:

,如果需要保留现有表(由于并发访问或其他限制):


旁白:切勿使用非描述性术语,如
name
id
作为列名。这是一种普遍的反模式。模式应该是这样的:

创建表域(
域id串行主键
,域文本唯一不为NULL--猜测它应该是唯一的
);
创建表统计信息(
stats_id串行主键
,域\u id int引用域
--,域文本--可以在上述规范化后删除。
);

欧文的解决方案应该足够好,你应该能够在2小时内完成

如果你有一个很大的统计表,你可能想跳过最后的更新步骤。只是c