我可以访问第三方数据库,并希望使用他们的信息创建工具。为其原始目的而设计的数据库非常庞大且隔离。我需要完成以下任务:
从下面的Schema中,我需要完成以下任务:
在invTypes中查找项目,同时检查invTypeMaterials和ramTypeRequirements以查看是否需要构建项目的任何材料。如果是,则在invTypes中查找每个材质,然后再次重复该过程以查看那些是否需要组件。这个循环一直持续到对invTypeMaterials和ramTypeRequirements的检查都是False,这可以是5或6个循环,但是每个循环要检查5或6个项目,因此可能是1561个循环,假设原始项目有1个循环,那么每个循环5个循环材料有5,5次。
现在我尝试完成代码并提出了以下内容:
$materialList = array();
function getList($dbc, $item) {
global $materialList;
// Obtain initial material list
$materials = materialList($dbc, $item);
// For each row in the database
while ($material == mysqli_fetch_array($materials)) {
// Check if there are any sub materials required
if (subList($dbc, $material['ID'])) {
// If so then recurse over the list the given quantity (it has already done it once)
for ($i = 0; $i < $material['Qty'] - 1; $i++) {
if (!subList($dbc, $material['ID'])) {
break;
}
}
} else {
// If there are no further materials then this is the base material so add to the array.
$materialList .= array(
"Name" => $mMaterial['Name'],
"Qty" => $mMaterial['Qty'],
"ID" => $material['ID']
);
}
}
return $materialList;
}
function subList($dbc, $item) {
global $materialList;
// Query the material incase it require further building
$mMaterials = materialList($dbc, $item['ID']);
// If the database returns any rows, then it must have more sub-materials required
if (mysqli_num_rows($mMaterials) > 0) {
// Check the sub-materials to see if they intern require futher materials
if (subList($dbc, $material['ID'])) {
// If the function returns true then iterate over the list the given quantity (its already done it once before)
for ($i = 0; $i < $material['Qty'] - 1; $i++) {
if (!subList($dbc, $material['ID'])) {
break;
}
}
} else {
// if the database returns 0 rows then this object is the base material so add to array.
$materialList .= array(
"Name" => $mMaterial['Name'],
"Qty" => $mMaterial['Qty'],
"ID" => $material['ID']
);
return true;
}
} else {
return false;
}
}
function materialList($dbc, $item) {
// Query
$query = " SELECT i.typeID AS ID, i.typeName AS Name, m.Quantity AS Qty
FROM invTypes AS i
LEFT JOIN invTypeMaterials AS m
ON m.materialTypeID = i.typeID
LEFT JOIN ramTypeRequirements AS r
ON r.typeID = i.typeID
WHERE groupID NOT IN(278,269,278,270,268) AND m.typeID = $item";
$snippets = mysqli_query($dbc, $query) or die('Error: ' . mysqli_error($dbc));
return $snippets;
}
我确信你们都注意到这个代码在涉及递归数据库调用时会违反每个编程规则。特别是subList()
不断调用自己,直到它发现它是错误的,这并不是很实用。 SQL不是我强大的套件,但我不能为我的生活解决如何克服这个问题。
任何指针都会非常有用,我当然不会要求你们为我重写我的全部代码,但如果你对我应该考虑的内容有任何想法,我将不胜感激。
答案 0 :(得分:1)
作为通用解决方案,我会执行以下操作:
typeID
,从invTypeMaterials
和ramTypeRequirements
SELECT
查询并继续循环SELECT t.*, m.materialTypeID, m.quantity AS m_quantity, r.requiredTypeID, r.quantity AS r_quantity
FROM invTypes t
LEFT JOIN invTypeMaterials m USING (typeID)
LEFT JOIN ramTypeRequirements r USING (typeID)
WHERE <conditions to select the types>
我只是猜测需要加载额外表中的哪些数据;在必要时扩展。
对于匹配行,materialTypeID
和requiredTypeID
将为非null,否则为null。
保留之前已加载的类型表,以便更快地参考。然后对于第二个查询,将条件替换为“WHERE t.typeID IN()
”请告诉我这是否有意义以及它是否接近对您有用的内容:)
答案 1 :(得分:1)
看起来这里递归是不可避免的。我加入Jack的答案,只需用PHP代码扩展它:)
我必须警告你,我从未执行过它,所以它需要调试,但我希望你能得到这个想法。 :)
$checked_dependencies = array();
$materials = array();
function materialList( $ids ) {
// if we have an array of IDs, condition is ".. in (...)"
if(is_array($ids)) {
$condition = 'IN ('.implode(',',$ids).')';
// add all to checked dependencies
foreach($ids as $id) { $checked_dependencies[] = $id; }
}else{
// otherwise, checking for particular ID
$condition = "= {$ids}";
// add to checked dependencies
$checked_dependencies[] = $ids;
}
$query = "SELECT t.*,
m.materialTypeID, m.quantity AS m_quantity,
r.requiredTypeID,
r.quantity AS r_quantity
FROM invTypes t
LEFT JOIN invTypeMaterials m ON t.typeId = m.typeId
LEFT JOIN ramTypeRequirements r ON t.typeId = r.typeId
WHERE t.typeID {$condition}";
$res = mysqli_query($dbc, $query);
// this will be the list of IDs which we need to get
$ids_to_check = array();
while($material = mysqli_fetch_assoc($res)) {
$materialList[] = $material; // you can get only needed fields
// if we didn't check the dependencies already, adding them to the list
// (if they aren't there yet)
if(!in_array($material['materialTypeId'], $checked_dependencies)
&& !in_array($material['materialTypeId'], $ids_to_check)
&& !is_null($material['materialTypeId'])) {
$ids_to_check[] = $material['materialTypeId'];
}
if(!in_array($material['requiredTypeId'], $checked_dependencies)
&& !in_array($material['requiredTypeId'], $ids_to_check)
&& !is_null($material['requiredTypeId'])) {
$ids_to_check[] = $material['requiredTypeId'];
}
}
// if the result array isn't empty, recursively calling same func
if(!empty($ids_to_check)) { materialList($ids_to_check); }
}
我在这里使用了一个全局数组,但很容易重写func来返回数据。
此外,我们可以在此处设置一些深度限制以避免过多的递归。
一般来说,我认为这不是一个非常方便(对于这项任务)的数据库数据组织。以这种方式递归存储数据有点舒服,但是,如您所见,它会导致未知的迭代次数和对数据库的请求以获取所有依赖项。这可能是昂贵的(PHP&lt; - &gt; MySQL&lt; - &gt; PHP&lt; - &gt; ...),在每次迭代时我们都会浪费时间,特别是如果数据库在远程服务器就像你的情况一样。
当然,重新安排数据结构以获得立即获得所有需求的可能性会很棒,但据我所知,您拥有对数据库的只读访问权限。我想到的第二个解决方案是一个递归的MySQL存储过程,这在这里也是不可能的。
在某些情况下(通常不是)最好在一个查询中获取尽可能多的数据,并在本地操作,以减少迭代次数。很难说这里是否有可能,因为我不知道数据库的大小和结构等,但是如果所有必需的依赖项存储在一个组中,并且组的数量不是很大,那么将一个请求中的所有组信息传递给PHP数组然后在本地从该数组中收集信息可能会更快。但是 - 这只是猜测而且需要测试和检查。