MySQLはデータベースのN+1クエリ問題を解決します

MySQLはデータベースのN+1クエリ問題を解決します

導入

HibernateやMyBatisなどのORMフレームワークでは、部門に関連付けられたユーザーオブジェクトなどの関連オブジェクトを設定できます。
n人のユーザーが見つかった場合、n個の部門クエリが必要です。ユーザーをクエリすることは選択操作であり、ユーザーの関連をクエリすることは
dept は n 回なので、n+1 の問題ですが、1+n と呼ぶ方が適切です。

mybatisの設定

ユーザーマッパー.xml

<resultMap id="BaseResultMap" タイプ="testmaven.entity.User">
  <id 列="id" jdbcType="INTEGER" プロパティ="id" />
  <結果列="名前" jdbcType="VARCHAR" プロパティ="名前" />
  <結果列="年齢" jdbcType="INTEGER" プロパティ="年齢" />
  <結果列="dept_id" jdbcType="INTEGER" プロパティ="deptId" />
  <関連付けプロパティ="dept" 列="dept_id" fetchType="eager" select="testmaven.mapper.DeptMapper.selectByPrimaryKey" ></関連付け>
 </結果マップ>

データテーブルは次のとおりです。

部門テーブル

|ID|名前|

ユーザーテーブル

|id|名前|部門ID|

要件は、次の構造を持つデータを取得することです。

[
  { "id":1, "name":"テスト", "department_id":1, "department":{ "id":1, "name":"テスト部門"
    }
  }
]

方法1: ループクエリ

ユーザーリストを照会する

対応する部門情報を照会するための循環ユーザーリスト

$users = $db->query('SELECT * FROM `user`'); foreach($users as &$user) {
  $users['department'] = $db->query('SELECT * FROM `department` WHERE `id` = '.$user['department_id']);
}

この方法では、1+N クエリ (リストへのクエリ 1 つ、部門へのクエリ N つ) が実行されますが、パフォーマンスが最も低いため、お勧めできません。

方法2: テーブルを結合する

結合テーブルを通じてユーザーと部門のデータを照会する

返されたデータの処理

$users = $db->query('SELECT * FROM `user` INNER JOIN `department` ON `department`.`id` = `user`.`department_id`'); // 手動処理では、必要な構造として結果が返されます

この方法には実際には制限があります。ユーザーと部門が同じサーバー上にない場合、テーブルを結合することはできません。

方法3: 1+1クエリ

このメソッドはまずユーザーリストを一度照会します

リストから部門IDを取り出して配列を形成する

ステップ2で部門を問い合わせる

最終データを統合する

コードは大まかに次のようになります。

$users = $db->query('SELECT * FROM `user`');
$departmentIds = [ ]; foreach($users を $user として) { if(!in_array($user['department_id'], $departmentIds)) {
    $departmentIds[] = $user['department_id'];
  }
}
$departments = $db->query('SELECT * FROM `department` WHERE id in ('.join(',',$department_id).')');
$map = []; // [部門ID => 部門項目]foreach($departments as $department) {
  $map[$department['id']] = $department;
}foreach($users を $user として) {
  $user['department'] = $map[$user['department_id']] ?? null;
 }

この方法は 2 つのテーブルに制限がなく、マイクロサービスの現在の人気を考慮すると、より優れたアプローチです。

以下もご興味があるかもしれません:
  • 少なくともn日間連続してログインしているユーザーに対するSQLクエリ
  • MySQLはすべてのカテゴリの最初のNレコードを取得します
  • MySQL で n 回以上連続して出現する数字を見つける方法

<<:  JavaScript でプロパティハイジャックを実装する方法 defineProperty

>>:  Centos8 でローカル Web サーバーを構築するための実装手順

推薦する

CocosCreatorでゲームコントローラーを使用する方法

目次1. シーンレイアウト2. ハンドルリスナーを追加する1. イベントの変更を監視する2. 座標設...

VMWare12 グラフィックチュートリアルで Apple Mac OS X をインストールする

1. はじめに:友人はシステム知識を学びたいと考えており、Apple のラップトップを使用していまし...

MySQL例外に対する一般的な解決策をいくつか分析する

目次序文1. コードによって設定されたデータベース名またはパスワードがローカルデータベースと一致して...

CentOS 7 で Docker のポート転送をファイアウォールと互換性のあるように設定する方法

CentOS 7 では、次のようなコマンドを使用してホスト ポートをコンテナー ポートにマッピングす...

Dockerでspringcloudプロジェクトをデプロイする方法

目次Dockerイメージのダウンロードmysqlとnacosを起動する独自のJavaプロジェクトを変...

Dockerを使用してサーバー上で複数のPHPバージョンを実行する

PHP7 がリリースされてからかなり時間が経ちますが、パフォーマンスが大幅に向上したことはよく知られ...

MySQLオンラインDDL gh-ostの使用の概要

背景: DBA として、大規模なテーブルの DDL 変更のほとんどは、Percona の pt-on...

Dockerディスク容量不足の問題を解決する

Docker が配置されているサーバーをしばらく稼働させたところ、サーバーのディスク ディレクトリの...

Docker 経由で Spring Boot アプリケーションを公開およびデプロイするプロセスの分析

目次手動展開1.アイデアを使ってSpring Bootプロジェクトを作成する2. プロジェクトをJa...

Vue 名前付きスロットの基本的な使用例

序文名前付きスロットは、スロット内の「name」属性を使用して要素にバインドされます。知らせ: 1....

Firefox で Flash を再生するためのオブジェクトとパラメータの書き方

コードをコピーコードは次のとおりです。 <object classid="clsid...

Vueフィルターの詳細な説明

<本文> <div id="ルート"> <h2&...

Reactコンポーネントのライフサイクル機能についての簡単な説明

React コンポーネントのライフサイクル機能とは何ですか?ライフサイクル関数は、ES6 構文クラス...

Vue で手ぶれ補正とスロットリングを使用する方法

目次序文コンセプト安定意味使用シナリオコードVueでの使用スロットリング意味使用シナリオコードVue...

ウェブページの読みやすさを向上させるいくつかの方法

1. 対照的な色を使用します。ここでのコントラストとは、テキストの色と背景色のコントラストを指します...