14

MySQLの複数のテーブルからデータをエクスポートするための最良の方法は何ですか。私は基本的に製品の詳細に取り組んでいます。製品に150のデータ属性があるとします。それを1行でエクスポートしてから、CSVまたはタブ区切り形式のフラットファイルにエクスポートするにはどうすればよいですか。

エラーが発生するテーブルが多すぎます。MySQLは結合で61個のテーブルしか使用できません

/**** Get Resultset *****/
$rs = mysql_query($sql);
/**** End of Get Resultset *****/

$objProfileHistory->addHistory($this->profile_id, "Loaded ". mysql_num_rows($rs)." records");


$this->runQuery($sql);

$this->exportToCSV();

/**
  * getAttributeDetails
  */
function getAttributeDetails(){
    global $dbObj, $profile;

    $base_table = "catalog_product_entity";
    $select  = array();
    $tables  = array();
    $i   = 0;

    $profile->showLog("Start fields mapping", "success");

   if( is_array($this->attributes_in_db) && sizeof($this->attributes_in_db) > 0 ){
    $arr = implode("','", $this->attributes_in_db);
    $sql = "select attribute_id, attribute_code, backend_type, frontend_input
        from eav_attribute 
        where attribute_code in ('".$arr."') 
        and entity_type_id = 
         (select entity_type_id 
          from eav_entity_type 
          where entity_type_code = 'catalog_product')";
    $rs = $dbObj->customqry($sql);

    if( $rs ){
     while( $row = mysql_fetch_assoc( $rs ) ){
      $backend_type  = $row["backend_type"];
      $attribut_code = $row["attribute_code"];
      $attribute_id = $row["attribute_id"];
      $frontend_input = $row["frontend_input"];
      switch( $backend_type ){
       case "text":
        $where[]  = $base_table."_".$backend_type."".$i.".attribute_id=".$attribute_id;
        $and[]  = $base_table.".entity_id=".$base_table."_".$backend_type."".$i.".entity_id";
        $select[]  = $base_table."_".$backend_type."".$i.".value as ".$attribut_code;
        $tables[]  = $base_table."_".$backend_type." as ".$base_table."_".$backend_type."".$i;
       break;

       case "decimal":
        $where[]  = $base_table."_".$backend_type."".$i.".attribute_id=".$attribute_id;
        $and[]  = $base_table.".entity_id=".$base_table."_".$backend_type."".$i.".entity_id";
        $select[]  = $base_table."_".$backend_type."".$i.".value as ".$attribut_code;
        $tables[]  = $base_table."_".$backend_type." as ".$base_table."_".$backend_type."".$i;
       break;

       case "static":
        $where[]  = $base_table."".$i.".entity_id=".$base_table.".entity_id";
        $and[]  = $base_table.".entity_id=".$base_table."".$i.".entity_id";
        $select[]  = $base_table."".$i.".".$attribut_code." as ".$attribut_code;
        $tables[]  = $base_table." as ".$base_table."".$i;
       break;

       case "int":
        if( $attribut_code == "tax_class_id" && $frontend_input == "select" ){
         $where[]  = "tax_class{$i}.class_id=(select ".$base_table."_".$backend_type."".$i.".value from ".$base_table."_".$backend_type." as ".$base_table."_".$backend_type."".$i." where  ".$base_table."_".$backend_type."".$i.".attribute_id=".$attribute_id." and ".$base_table."_".$backend_type."".$i.".entity_id=".$base_table.".entity_id limit 1))";
         $and[]  = "";
         $select[]  = "tax_class{$i}.class_name as {$attribut_code}";
         $tables[]  = "tax_class as tax_class{$i}";
         } else if( $frontend_input == "select" ){
         $where[]  = "eav_attribute_option_value{$i}.option_id=(select ".$base_table."_".$backend_type."".$i.".value from ".$base_table."_".$backend_type." as ".$base_table."_".$backend_type."".$i." where  ".$base_table."_".$backend_type."".$i.".attribute_id=".$attribute_id." and ".$base_table."_".$backend_type."".$i.".entity_id=".$base_table.".entity_id limit 1))";
         $and[]  = "";
         $select[] = "eav_attribute_option_value{$i}.value as {$attribut_code}";
         $tables[]  = "eav_attribute_option_value as eav_attribute_option_value{$i}";
        } else {
         $where[]  = $base_table."_".$backend_type."".$i.".attribute_id=".$attribute_id;
         $and[]  = $base_table.".entity_id=".$base_table."_".$backend_type."".$i.".entity_id";
         $select[]  = $base_table."_".$backend_type."".$i.".value as ".$attribut_code;
         $tables[]  = $base_table."_".$backend_type." as ".$base_table."_".$backend_type."".$i;
        }
       break;

       case "varchar":
        $where[]  = $base_table."_".$backend_type."".$i.".attribute_id=".$attribute_id;
        $and[]  = $base_table.".entity_id=".$base_table."_".$backend_type."".$i.".entity_id";
        $select[]  = $base_table."_".$backend_type."".$i.".value as ".$attribut_code;
        $tables[]  = $base_table."_".$backend_type." as ".$base_table."_".$backend_type."".$i;
       break;

       case "datetime":
        $where[]  = $base_table."_".$backend_type."".$i.".attribute_id=".$attribute_id;
        $and[]  = $base_table.".entity_id=".$base_table."_".$backend_type."".$i.".entity_id";
        $select[]  = $base_table."_".$backend_type."".$i.".value as ".$attribut_code;
        $tables[]  = $base_table."_".$backend_type." as ".$base_table."_".$backend_type."".$i;
       break;
      }//switch
      $i++;
     }//while


     $sql = "select ".implode(",", $select)." from ".$base_table;
     for($i=0; $i < sizeof($select); $i++){
      $sql .= " left join ". $tables[$i] . " on (".$where[$i];//." and ".$and[$i].")";
      if( strlen($and[$i]) > 0 ){
       $sql .= " and ".$and[$i].")";
      }
     }//for
     $sql .= " group by {$base_table}.entity_id ";
    }//if
    //echo $sql; exit;
    return $sql;
   }
   //echo $sql;
   //echo "<pre>";print_r($tables);print_r($select);print_r($where);print_r($and);
  }//end function

  /**
  * runQuery
  */
  function runQuery( $sql ){
   global $dbObj, $profile;
   if( $sql != "" ){
    $rs = $dbObj->customqry( $sql );
    $profile->showLog("Loaded ". mysql_num_rows($rs) ." records", "success");
    if( $rs ){
     $i = 0;
     while( $row = mysql_fetch_assoc( $rs ) ){
      $cnt = sizeof($this->attributes_in_db);
      for($j=0; $j < $cnt; $j++){
       $db_key  = $this->attributes_in_db[$j];
       $file_key = $this->attributes_in_file[$j];
       $this->export_data[$i][$db_key] = $row[$db_key];
      }
      $i++;
     }//while
    }
   }//if
  }//end function


  /**
  * exportToCSV
  */
  function exportToCSV(){
   global $smarty, $objProfileHistory, $profile;
   //$newFileName = $smarty->root_dir."/export/".$this->filename; //file name that you want to create
   $cnt = sizeof($this->var_array);
   for($i=0; $i < $cnt; $i++){
    extract($this->var_array[$i]);
   }//for


   if( $delimiter = "\t" ){
    $delimiter = "\t";//$delimiter;
   }

   if( strlen($filename) < 1 ){
    $filename = time().".csv";
   }

//    echo "<pre>";
//    print_r($this->action_array);
//    print_r($this->var_array);
//    print_r($this->map_array);
//    exit;
   # add amazon headers
   if( $this->action_array[0]['type'] == 'header' ){
//     $template_type  = $this->var_array[0]['template_type'];
//     $version   = $this->var_array[0]['version'];
//     $status_message = $this->var_array[0]['status_message'];
    $sStr = "TemplateType=".$template_type."{$delimiter}{$delimiter}Version=".$version."{$delimiter}{$delimiter}{$status_message}";
    $sStr .= "� ��\n"; //to seprate every record
   }





   $export_path = $path;
   $x_path = $profile->createDir( $export_path );

   $newFileName = $x_path ."/". $filename;

   $fpWrite = fopen($newFileName, "w"); // open file as writable

   # create header
   $cnt_header = sizeof($this->attributes_in_file);
   for( $i=0; $i < $cnt_header; $i++){
    $sStr .= $deli . $this->attributes_in_file[$i];
    $deli = $delimiter;
   }//for
   $sStr .= "� ��\n"; //to seprate every record

   # attach data
   $cnt_row = sizeof($this->export_data);
   for( $i=0; $i < $cnt_row; $i++ ){
    $sStr .= $saperator;
    $newdeli = "";
    for($j=0; $j < $cnt_header; $j++){
     $key  = $this->attributes_in_db[$j];
     $sku = $this->export_data[$i]["sku"];
4

4 に答える 4

24

EAVデザインを使用していて、可変数の属性から単一の行を再構築しようとしています。これは、EAV設計を使用して遭遇する多くの地雷の1つを示しています。つまり、1つのSQLクエリで実行できる結合の数には実際的な制限があります。

特にMySQLでは、ご存知のように厳しい制限があります。ただし、他のRDBMSブランドでも、結合のコストはテーブルの数に対して幾何学的であるため、効果的な制限があります。

EAVを使用する場合は、従来のデータベース設計のようにSQLで行を再構築しようとしないでください。代わりに、エンティティIDでソートされた行として属性をフェッチします。次に、アプリケーションコードでそれらを後処理します。これは、1つのステップでデータをダンプできないことを意味します。つまり、属性行をループするコードを記述し、データを出力する前にデータの各行を再編成する必要があります。

EAVは便利なデータベース設計ではありません。それを使用することには多くの高価な欠点があり、あなたはそれらの1つにぶつかっただけです。


EAVの使用が1つのビジネスをどのように運命づけたかについての素晴らしい話については、http: //www.simple-talk.com/opinion/opinion-pieces/bad-carma/を参照してください。

また、EAVはこのアンチパターンの例であるため、http://en.wikipedia.org/wiki/Inner-platform_effectも参照してください。


カタログ内の製品ごとに動的な属性セットをサポートする必要があることを理解しています。しかし、EAVはあなたのアプリケーションを殺そうとしています。動的属性をサポートするために私が行うことは次のとおりです。

  • すべての製品タイプに共通する属性ごとに、ベーステーブルに実際の列を定義します。製品名、価格、在庫数など。このセットにできるだけ多くの属性を含めることができるように、正規の製品エンティティを想像するように努力してください。

  • TEXT指定された各製品タイプのすべての追加属性に対して、タイプの列をもう1つ定義します。XML、JSON、YAML、独自の自家製DSLなど、適切な形式で、属性の シリアル化されたLOBとしてこの列に格納します。

    これをSQLクエリの単一の列として扱います。これらの属性に基づいて実行する必要のある検索、並べ替え、または表示では、TEXTblob全体をアプリケーションにフェッチして逆シリアル化し、アプリケーションコードを使用して属性を分析する必要があります。

于 2009-11-06T02:25:47.353 に答える
3

これだけ多くの属性がある場合、それはまばらなデータベースであると思います。そのため、大量の無駄なスペースがあります。

可能であれば、代わりにEntity-Attribute-Valueデータベースの使用を検討することをお勧めします。

http://en.wikipedia.org/wiki/Entity-attribute-value_model

これにより、データベースをリファクタリングする方法が得られますが、データベースをより拡張可能にし、必要なテーブルの数を減らすことができます。4〜6個のテーブル(2〜3個のエンティティテーブルとその属性)に分類できるはずです。すべてのクエリが動的になるため、クエリの作成は少し難しくなりますが、エクスポートが簡素化され、データベースのメンテナンスが簡素化されます。

このスキーマを使用する必要がある場合は、複数のトリガーを作成してから、複数のテーブルを結合しているトリガーを呼び出してクエリを実行できますが、パフォーマンスが大幅に低下します。

アップデート:

EAVテーブルが使用されており、MySQLはピボット関数を実行しないため、次の質問に対する回答を読むことをお勧めします。MySQLエンティティ属性値スキーマ をピボットする方法MySQLエンティティ属性値スキーマをピボットする方法

于 2009-11-06T02:17:22.300 に答える
1

EAVを使用していて、一度に多数の属性をエクスポートする場合は、実際には複数の一時テーブルを使用するのが最善の方法です。

各一時テーブルには、同じ主キー列があります。次に、それらすべてを結合して、csvにエクスポートします。

完全に具体化した例をやりたいかどうかはわかりませんが、うまくいけばより明確になるようなアウトラインを作成しようと思います。

1.)エクスポートする属性のリストを取得します。EAVattribute_valuesテーブルへの結合でそれらのattribute_idsを使用します。

2.)結合制限を超えないように、属性を分割します。元のテーブルと、結合ごとに1つのテーブルが必要なので、このスキームではテーブルごとに60の属性を持つことができます。

3.)属性のグループごとに「フラット」な一時テーブルを作成します。こんな感じになります。

CREATE TEMPORARY TABLE temp1
[(create_definition,...)]
SELECT t1.product_id, t1.sku, t2.color, GROUP_CONCAT(t3.sizes SEPARATOR ',') as sizes,
...

#( suppose the product has multiple sizes and you want them shown comma-separated in your export)

FROM products t1
LEFT JOIN eav_attribute_values t2 ON t1.product_id = t2.product_id AND t2.attribute_id = 55
LEFT JOIN eav_attribute_values t3 ON t1.product_id = t2.product_id AND t2.attribute_id = 76
... etc for up to 60 attributes

CREATE TEMPORARY TABLE temp2 ... # repeat for next 60 attributes

4.)これで、一時テーブルtemp1、temp2、temp3などができました。これらはすべて同じ主キー(たとえば、product_idやproduct_sku)を共有します。60未満の一時テーブル(ばかげている)があると仮定すると、これらすべてを結合して1つのテーブルを作成できます。

私のシステムでは、一時テーブルが3つを超えているとは思いませんが、それはかなりの量です。

CREATE TEMPORARY TABLE export_data
[(create_definition,...)]
SELECT t1.*, t2.*, t3.* FROM # though I would not actually use * here b/c it would cause repeated key fields. I would list out all the columns
temp1 t1 LEFT JOIN temp2 t2 ON t1.product_id = t2.product_id
LEFT JOIN temp3 t3 ON t1.product_id = t3.product_id # etc for more joins

5.)エクスポート。MySQLのファイルエクスポート機能を使用してCSVを作成します。PHPを使用してユーザーに送信します。

それがお役に立てば幸いです。

また、上記のプロセスは私にとってかなり速く実行されることに注意してください。一時テーブルを使用する理由は、使用後に自動的に削除されるためです。また、一時テーブルは作成したユーザーにのみ存在するため、複数のユーザーが互いに干渉することなく同じタイプのプロセスを実行できるためです。

于 2013-09-15T09:59:33.907 に答える
0

Spring Bootでは、フェッチタイプをLAZYとして使用していない場合に発生します。この種の状況では、テーブルの1つに61を超えるサブ関係がありますEX-テーブル名A、B、C、およびD

  • A関連B
  • B関連C
  • C関連D

この状況では、Aには3つのサブリレーションがあります。Aからデータを取得すると、関係に基づいてDのデータにアクセスできます。

したがって、FetchタイプをLAZYとして使用します

于 2019-06-02T18:50:18.313 に答える