Sac*_*tte 7 php mysql codeigniter doctrine-query doctrine-orm
我遵循数据库架构 -

现在部门,年份和部门表已经填满了信息.
我现在需要插入学生数据.学生数据将从xls文件导入(导入和解析部分已完成).正如您在架构中看到的那样,表中的列student_data引用year_id,department_di和division_id.因此,在插入时我需要他们的ID字段,因为xls具有各自的名称值.
所以我要根据每个学生的列值来获取相应的ID.因此,这引入了3个查询,用于在学生表中插入一条记录.像这样 -
forloop(...):
$studentData = new Entities\StudentData();
$year = $this->em->getRepository("Entities\Year")->findBy(array('year_name' => $this->year[$i]));
$department = $this->em->getRepository("Entities\Department")->findBy(array('department_name' => $this->branch[$i]));
$division = $this->em->getRepository("Entities\Division")->findBy(array('division_name'=>$this->division[$i]));
$studentData->setYear($year[0]);
$studentData->setDepartment($department[0]);
$studentData->setDivision($division[0]);
//other data
.
.
.
.
.
$this->em->persist($studentData);
endforloop();
$this->em->flush();
$this->em->clear();
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,我将为每个部门,年份和部门获得ID循环.假设我正在导入100个学生列表,因此它最终会运行300个查询来获取这3个ID字段.
我可以在插入数据时直接从他们的名字中获取年份,部门和部门的ID吗?我是学说的新手,我不知道如何去做.
更新 如果问题不清楚,请告诉我.我可以用更多细节更新它或重组它.
优化
您可以在不使用 Doctrine 的结果缓存的情况下优化流程:
首先创建他们的 id 的年份映射,如下所示:
$yearsMap = array();
$q = $em->createQuery('SELECT y.id, y.year_name FROM Entities\Year y');
foreach ($q->getScalarResult() as $row) {
$yearsMap[$row['year_name']] = $row['id'];
}
Run Code Online (Sandbox Code Playgroud)
还创建部门到其 ID 的映射,以及部门到其 ID 的映射。这将导致 3 个(轻型)查询。放置此代码的最佳位置是(自定义)存储库中。
接下来,您可以运行循环,但“获取”实际实体,如下所示:
$year = $this->em->getReference('Entities\Year', $yearsMap[$this->year[$i]]);
$department = $this->em->getReference('Entities\Department', $departmentsMap[$this->branch[$i]]);
$division = $this->em->getReference('Entities\Division', $divisionsMap[$this->division[$i]]);
Run Code Online (Sandbox Code Playgroud)
我说“get”,因为getReference()实际上创建了一个代理(除非它已经由实体管理器加载,但在这种情况下它可能不是)。该代理尚未加载,因此此处不执行任何查询。
其余代码不需要更改。
现在,当flush()被调用时,Doctrine 将仅加载每个不同的年份/部门/部门一次。这仍然可能会导致一些查询,具体取决于使用了多少个不同的年份/部门/部门。因此,如果所有 100 名学生都使用不同的年份/系/分部,您最终将得到 403 个查询(3 个用于地图,300 个用于加载代理,100 个用于插入学生)。但是,如果所有 100 名学生都使用同一年级/系/部门,则最终只会有 106 个查询(3 个用于地图,3 个用于加载代理,100 个用于插入学生)。
另一种优化方式
另一种方法是使用您收集的名称来获取您需要的所有实体:
$q = $em->createQuery('SELECT y FROM Entities\Year y INDEX BY y.year_name WHERE y.year_name IN(:years)');
$q->setParameter('years', $yearNames);
$yearsMap = $q->getResult();
Run Code Online (Sandbox Code Playgroud)
现在,您只需 1 个查询即可获得所需的所有 Year 实体。您可以对部门和部门执行相同的操作。
另请注意INDEX BYDQL 语句中的:这将确保您将获得一个year_name以实体为键、以实体为值的数组。您可以直接在循环中使用它,如下所示:
$year = $yearsMap[$this->year[$i]];
$department = $departmentsMap[$this->branch[$i]];
$division = $divisionsMap[$this->division[$i]];
Run Code Online (Sandbox Code Playgroud)
100 名学生的最终结果始终是 103 个查询(3 个用于地图,100 个用于插入学生)。
缓存
当您需要相对频繁地运行此循环并且它会给数据库带来压力时,明智的做法是使用 Doctrine 的结果缓存。但有几点需要注意:
getReference()(尚)不支持结果缓存,并且不会自动使用结果缓存。所以我建议你把这样的东西放在存储库中:
public function findOneYearByName($name)
{
$q = $em->createQuery('SELECT y FROM Entities\Year y WHERE y.year_name = :year');
$q->setParameter('year', $name);
$q->useResultCache(true);
return $q->getSingleResult();
}
Run Code Online (Sandbox Code Playgroud)
您可能想要配置结果缓存,请参阅相关文档。
另一个注意事项是,结果缓存将在水合之前缓存从数据库获取的结果。因此,即使使用结果缓存,实际实体每次都会被水合。因此我仍然建议使用地图,但实现方式略有不同:
$yearsMap = array();
$departmentsMap = array();
$divisionsMap = array();
forloop (...):
if (!isset($yearsMap[$this->year[$i]])) {
$yearsMap[$this->year[$i]] = $this->em->getRepository('Entities\Year')->findOneYearByName($this->year[$i]);
}
if (!isset($departmentsMap[$this->branch[$i]])) {
$departmentsMap[$this->branch[$i]] = $this->em->getRepository('Entities\Department')->findOneDepartmentByName($this->branch[$i]);
}
if (!isset($divisionsMap[$this->division[$i]])) {
$divisionsMap[$this->division[$i]] = $this->em->getRepository('Entities\Division')->findOneDivisionByName($this->division[$i]);
}
$year = $yearsMap[$this->year[$i]];
$department = $departmentsMap[$this->branch[$i]];
$division = $divisionsMap[$this->division[$i]];
Run Code Online (Sandbox Code Playgroud)
这将确保每个不同的年份/部门/部门仅补充一次水分。
PS:使用结果缓存“以另一种方式优化”不会那么有效,因为每次运行循环时,年份/部门/部门的名称可能会有所不同。每次更改名称时,查询都会发生变化,并且无法使用缓存的结果。
DBAL
插入数据时可以直接从名称中获取年份、部门和部门的 ID 吗?
你可以,但你不会使用 ORM,而只能使用 DBAL。你基本上这样做:
$connection = $em->getConnection();
$statement = $conn->executeQuery('insert query', array('parameter1', 'etc'));
$statement->execute();
Run Code Online (Sandbox Code Playgroud)
我怀疑这会更有效,因为 MySQL(或您使用的任何供应商)仍然会为每个插入执行这 3 个(子)查询,它们只是不会“通过网络”。而且您无法从 ORM 获得任何帮助,例如管理关联等。
不过,您仍然可以在这里找到有关该主题的所有内容。