7

私はサードパーティのデータベースへのアクセス権を与えられており、その情報を使用してツールを作成したいと考えています。本来の目的のために設計されたデータベースは非常に大きく、分離されています。次のタスクを完了する必要があります。

以下のスキーマから、次のタスクを完了する必要があります。

invTypes でアイテムを検索し、invTypeMaterials と ramTypeRequirements の両方をチェックして、アイテムを構築するために必要な材料があるかどうかを確認します。はいの場合は、invTypes でこれらの各マテリアルを検索し、プロセスを繰り返して、コンポーネントが必要かどうかを確認します。このループは、invTypeMaterials と ramTypeRequirements の両方のチェックが False になるまで続きます。これは 5 回または 6 回のループになる可能性がありますが、チェックするループごとに 5 回または 6 個の項目があるため、元の項目に対して 1 回のループを想定すると、1561 回のループになる可能性があります。 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

ここで再帰は避けられないようです。私はジャックの答えに参加します.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); }

}

ここではグローバル配列を使用しましたが、関数を書き直してデータを返すのは簡単です。

また、再帰が多すぎるのを避けるために、ここに深さ制限を設定することもできます。

一般に、DB データの編成は (このタスクにとって) あまり便利ではないと思います。このように再帰的にデータを保存するのはちょっと快適ですが、ご覧のとおり、すべての依存関係を取得するために、未知の量の反復とデータベースへの要求が発生します。そして、それは高価かもしれません(PHP <-> MySQL <-> PHP <->...)、特にDBがあなたの場合のようにリモートサーバー上にある場合、反復ごとに時間を失います。

もちろん、すべての要件を一度に取得できるようにデータ構造を再配置することは素晴らしいことですが、私が理解しているように、データベースへのアクセスは読み取り専用です。私の頭に浮かぶ2番目の解決策は、再帰的なMySQLストアドプロシージャですが、これもここでは不可能です。

場合によっては (一般的ではありません)、反復回数を減らすために、1 つのクエリでできるだけ多くのデータを取得し、それをローカルで操作することをお勧めします。DB のサイズや構造などがわからないため、ここで可能かどうかはわかりませんが、たとえば、必要なすべての依存関係が 1 つのグループに格納されていて、グループがそれほど大きくない場合は、おそらくPHP 配列への 1 回のリクエストですべてのグループ情報を取得し、その配列からローカルで情報を収集する方が速い場合があります。しかし、これは推測に過ぎず、テストと確認が必要です。

于 2012-06-07T15:07:05.397 に答える
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>

追加テーブルのどのデータをロードする必要があるかを推測しました。必要に応じて拡張します。

materialTypeIDandは、一致する行のrequiredTypeID場合は非 null になり、それ以外の場合は null になります。

より迅速に参照できるように、以前にロードしたタイプのテーブルを保持します。次に、2 番目のクエリでは、条件を `WHERE t.typeID IN () のようなものに置き換えます。

これが理にかなっているかどうか、そしてそれがあなたにとって役立つものに近いかどうか教えてください:)

于 2012-06-05T09:54:38.837 に答える