web-dev-qa-db-ja.com

あるテーブルから別のテーブルに新しい列の値のみを転送する方法は?

新しいテーブル_clientsを作成しようとしています。これには、現在のところ、user_id#__user_usergroup_map列が単純に入力されています。

理想的には、以下のコードは、#__user_usergroup_mapに一致し、group_id = 10テーブル内にまだ存在しないIDのみがある場合、_clientsテーブルからすべてのidをプルする必要があります。

これは、phpmyadminのgroup_id = 10でユーザーを追加/削除したときに機能するようです。 $nclは正しいリストを示しています。

#__clientsはページの更新時に正常に更新されますが、#__clientsに主キーが重複して挿入されるとSQLエラーが発生します。同じユーザーを#__clientsに再度追加しようとしているようです(最初の更新時にユーザーがテーブルに追加されました)。

これらのクライアントIDは$nclテーブルにまだないため、このコードは#__clientsのみを選択する必要があると思いました。誰かが何か考えがありますか?

$db = JFactory::getDBO();
$query = $db->getQuery(true);
$query->select('a.user_id', 'a.group_id', 'b.id')
      ->from('#__user_usergroup_map as a', '#__clients as b');
$query->join('RIGHT', '#__clients AS b ON a.user_id != b.id')
      ->where('a.group_id = ' . $db->quote("10"));
$db->setQuery($query);
$ncl = $db->loadColumn();
print_r($ncl);

foreach($ncl as $encl) {
    $db    = JFactory::getDbo();
    $query = $db->getQuery(true);
    $query->insert('#__clients')
          ->set('id = ' . (int) $encl);
    $db->setQuery($query);
    $db->execute();
}
3
Michael Brown

コードを次のように調整できます。

$db = JFactory::getDBO();
$query = $db->getQuery(true);
$query->select('a.user_id as id') /* changed to load object and insert it */
    ->from('#__user_usergroup_map as a')
    ->leftJoin('#__clients AS b ON a.user_id = b.id') /* you should join with "=" not with "!=" */
    ->where('a.group_id = ' . $db->quote("10"))
    ->where('b.id IS NULL'); /* and filter to "new only" like this */
$ncl = $db->setQuery($query)->loadColumn();
print_r($ncl);

foreach($ncl as $encl) {
    $db->insertObject('#__clients',$encl); /* less code this way */
}

このコードでうまくいきますが、さらに進んでそれを作ることができます

$db = JFactory::getDBO();
$sel_query = $db->getQuery(true);
$ins_query = $db->getQuery(true);
$sel_query->select('a.user_id as id')
    ->from('#__user_usergroup_map as a')
    ->leftJoin('#__clients AS b ON a.user_id = b.id')
    ->where('a.group_id = ' . $db->quote("10"))
    ->where('b.id IS NULL');
$ins_query->insert('#__clients')
    ->columns('id')
    ->values($sel_query);
$db->setQuery(str_replace('VALUES','',$ins_query))->execute();

このようにして、単一のリクエストでジョブを実行します。

1
Alexandr

私は今、より単純な方法を実現しました...すでに既存の配列を作成していたので、次のようなものを単に使用できます:

$filteredFoo = array_diff($foo, $bar);

これにより、各テーブルを表す2つの配列から重複が除外され、重複のリスクなしに$ filteredFooでSQL挿入が実行されます。

1
Michael Brown

_#__user_usergroup_map_から_#__clients_へのINSERTデータのみが必要なので、idはnullであり、_group_id_は_10_であるので、_LEFT JOIN_を使用する必要がありますテーブルを結合します。他の結合は、NULL列を生成する行を排除し、それは望ましくありません。

_|  #__user_usergroup_map |  LEFT JOIN ON user_id=id  |  #__clients  |
|------------------------|              <            |--------------|
|  user_id  |  group_id  |              <            |      id      |
|-----------|------------|              <            |--------------|
|     1     |     10     |              <            |     NULL     |
|     2     |      9     |              <            |     NULL     |
|     3     |     10     |              <            |     NULL     |
|     4     |      6     |              <            |     NULL     |
|     5     |     10     |              <            |       5      | //previously INSERTED for demonstration's sake
|     6     |     10     |              <            |     NULL     |
|     7     |     10     |              <            |     NULL     |
|     8     |     10     |              <            |     NULL     |
|     9     |      4     |              <            |     NULL     |
|    10     |     10     |              <            |     NULL     |
--------------------------                           ----------------
_

解決策に入る前に、php/Joomla構文が意図したクエリを生成していないという事実に注目したいと思います。

_$query = $db->getQuery(true)
            ->select('a.user_id', 'a.group_id', 'b.id')
            ->from('#__user_usergroup_map as a', '#__clients as b')
            ->join('RIGHT', '#__clients AS b ON a.user_id != b.id')
            ->where('a.group_id = ' . $db->quote("10"));
echo $query->dump();
_

生成:

_SELECT a.user_id                                    // 2nd & 3rd columns are lost (should have been written as an array, but they were unimportant anyhow)
FROM prefx_user_usergroup_map as a                  // comma joined table is lost (not that it was good for anything)
RIGHT JOIN prefx_clients AS b ON a.user_id != b.id  // this is not the correct join, nor ON logic
WHERE a.group_id = '10'                             // this is half of what is required
_

これはゼロ行を返します。


ロジック/構文がしっかりしているため、Alexandrのスニペットが期待どおりの結果を提供すると確信していますが、いくつかの違いがあるバージョンを提供します。

_$db = JFactory::getDBO();
try {
    $select_query = $db->getQuery(true)
                       ->select("A.user_id")
                       ->from("#__user_usergroup_map A")
                       ->leftJoin("#__clients B ON A.user_id = B.id")
                       ->where("A.group_id = 10 AND B.id IS NULL");
    //echo $select_query->dump();

    $insert_query = $db->getQuery(true)
                       ->insert('#__clients')
                       ->columns('id')
                       ->values($select_query);
    // echo $insert_query->dump();

    $db->setQuery($insert_query);
    $db->execute();
    // echo $db->getAffectedRows() , " row(s) inserted into clients table";
} catch (Exception $e) {
    echo "Syntax Error"; // . " & Error: " . $e->getMessage();
}
_

私は自分のスニペットをローカルホストで成功するようにテストしました。私は SQLFiddle Demo も作成しました。

  • _${Word}_query_の重複した記述を排除するために、getQuery()メソッドからの各クエリをチェーンしています。
  • qn()またはq()呼び出しは、解析/セキュリティ上の理由で必要ないため、使用していません。
  • 大文字のテーブルエイリアスを使用して、テーブル名と列から区別できるようにしています。
  • SELECTの列は必要ないため、エイリアスを設定していません。
  • setQuery(str_replace('VALUES','',$ins_query))は必要ありません。

Alexandrのソリューションを恥知らずに盗んでいると誰かが心配している場合は、_#__clients.id_列が_PRIMARY/UNIQUE KEY_であると仮定して同じ効果を持つ別のブルートフォースクエリビルドを投稿します。私はこのエレガントでないソリューションを使用するとは思いません(何らかの理由で、パフォーマンスが少し向上したとしても)。

_$select_query = $db->getQuery(true)
                   ->select("A.user_id")
                   ->from("#__user_usergroup_map A")
                   ->where("A.group_id = 10");
// echo $select_query->dump();

$insert_query = $db->getQuery(true)
                   ->insert('#__clients')
                   ->columns('id')
                   ->values($select_query);

$db->setQuery(str_replace("INSERT", "INSERT IGNORE", $insert_query));
_
0
mickmackusa