7

我已获得访问第三方数据库的权限,并希望使用他们的信息创建一个工具。为其最初目的设计的数据库非常庞大且隔离。我需要完成以下任务:

从下面的 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 不是我的强项,但我终其一生都无法解决这个问题。

任何指针都会非常有帮助,我当然不会要求你们中的任何人为我重写我的整个代码,但如果你对我应该考虑什么有任何想法,我将不胜感激。

4

2 回答 2

1

作为通用解决方案,我将执行以下操作:

  • 对于每一个typeID,从两者中收集invTypeMaterialsramTypeRequirements
  • 从收集的数据中,您创建一个新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>

我刚刚猜测需要加载额外表中的哪些数据;必要时扩大。

对于匹配行, materialTypeIDandrequiredTypeID将为非 null,否则为 null。

保留您之前已加载的类型表,以便更快地参考。然后对于第二个查询,您将条件替换为 `WHERE t.typeID IN ()

让我知道这是否有意义,以及它是否接近对您有用的东西:)

于 2012-06-05T09:54:38.837 回答
1

看起来这里的递归是不可避免的。我加入了杰克的回答,只是会用 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 <-> MySQL <-> PHP <->...),在每次迭代中我们都会浪费时间,特别是如果数据库像您的情况一样位于远程服务器上。

当然,重新安排数据结构以便一次获得所有要求会很好,但据我了解,您对数据库具有只读访问权限。我想到的第二个解决方案是递归 MySQL 存储过程,这在这里也是不可能的。

在某些情况下(通常不是),最好在一个查询中获取尽可能多的数据,并在本地对其进行操作,以减少迭代次数。很难说这里是否可能,因为我不知道数据库的大小和结构等,但是例如,如果所有需要的依赖项都存储在一个组中,并且这些组不是很大,也许它在对 PHP 数组的一个请求中获取所有组信息然后在本地从该数组收集信息可能会更快。但是 - 这只是一个猜测,需要测试和检查。

于 2012-06-07T15:07:05.397 回答