MariaDB講座 / 結果を変えずに日付検索・ソート・ページ分割を改善する
問い合わせ改善は、必要な行を保ちながら条件・順序・ページ移動を適切なアクセス経路へ合わせる作業です。主キーと表示順の同値性を先に確認し、性能は実際の計画と代表データで評価します。
速さより先に同じ結果かを確かめる
境界時刻や同時刻の行を落とした問い合わせは、同値な改善ではありません。期間は開始以上・次境界未満、順序は一意な補助キー、ページ方式は移動と整合性の要件に合わせます。
ORDER BYとキーセット、前後の実行計画で結果と費用を分けて確認します。部分一致を前方一致へ変えるなど、要求自体を削って速くする方法は別の検索です。
期間順の表と索引を準備する
course_userでshopcourseに接続し、SELECT VERSION(), DATABASE();とSHOW TABLES;で対象を確認します。以下は独立した実習表の作成・追加を含む完全な例で、UTF-8ファイルとして実行できます。同名表があれば定義と行を調べ、必要なものを保存してから判断します。
USE shopcourse; CREATE TABLE c41_customers ( customer_id INT NOT NULL PRIMARY KEY, customer_name VARCHAR(50) NOT NULL, city VARCHAR(30) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE c41_products ( product_id INT NOT NULL PRIMARY KEY, product_name VARCHAR(100) NOT NULL, category VARCHAR(30) NOT NULL, price DECIMAL(10,2) NOT NULL CHECK (price >= 0), stock INT NOT NULL CHECK (stock >= 0) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE c41_orders ( order_id INT NOT NULL PRIMARY KEY, customer_id INT NOT NULL, ordered_at DATETIME NOT NULL, status VARCHAR(20) NOT NULL, shipping_fee DECIMAL(10,2) NOT NULL DEFAULT 1000, CONSTRAINT fk_c41_order_customer FOREIGN KEY (customer_id) REFERENCES c41_customers(customer_id) ON DELETE RESTRICT ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE c41_items ( order_id INT NOT NULL, product_id INT NOT NULL, quantity INT NOT NULL CHECK (quantity > 0), unit_price DECIMAL(10,2) NOT NULL CHECK (unit_price >= 0), PRIMARY KEY (order_id, product_id), CONSTRAINT fk_c41_item_order FOREIGN KEY (order_id) REFERENCES c41_orders(order_id) ON DELETE RESTRICT, CONSTRAINT fk_c41_item_product FOREIGN KEY (product_id) REFERENCES c41_products(product_id) ON DELETE RESTRICT ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; INSERT INTO c41_customers VALUES (1,'김민수','서울'),(2,'이서연','부산'), (3,'박지우','서울'),(4,'최하늘','대전'); INSERT INTO c41_products VALUES (1,'노트','문구',3500,20),(2,'볼펜','문구',1500,50), (3,'머그컵','생활',8000,12),(4,'스탠드','생활',25000,5); INSERT INTO c41_orders (order_id,customer_id,ordered_at,status,shipping_fee) VALUES (101,1,'2026-01-10 10:00:00','paid',1000), (102,1,'2026-01-20 11:00:00','paid',1000), (103,2,'2026-02-05 12:00:00','paid',1000), (104,3,'2026-02-10 13:00:00','canceled',1000); INSERT INTO c41_items VALUES (101,1,2,3500),(101,2,3,1500),(102,3,1,8000), (103,1,1,3500),(103,3,2,8000),(104,2,1,1500);
文字列は原文の韓国語サンプルを維持しています。商品1~4はノート、ペン、マグカップ、ライト、顧客1・3はソウル、2は釜山、4は大田です。UTF-8で保存・接続し、安定したIDと数値で照合します。
CREATE INDEX idx_c41_time ON c41_orders(ordered_at,order_id);
四注文では全走査も合理的です。ここは行と境界の正しさを学ぶ例で、規模による効果は別の代表データで測定します。
日付関数と明示的な範囲を比べる
同じ一日を二通りで選びます。
SELECT order_id FROM c41_orders WHERE DATE(ordered_at)='2026-01-10' ORDER BY order_id; SELECT order_id FROM c41_orders WHERE ordered_at>='2026-01-10' AND ordered_at<'2026-01-11' ORDER BY order_id;
どちらも101です。範囲は時刻や小数秒を含む一日を表します。MariaDB 11.1以降では対応するDATE・YEAR条件を索引範囲へ変換できるため、「列の関数は必ず索引を使えない」とは言いません。明示範囲は意図が読みやすくても、それだけで高速の証拠ではありません。実際のEXPLAINでキーと範囲を比較します。
時刻とIDの両方で次ページを選ぶ
降順の例です。
SELECT order_id,ordered_at FROM c41_orders ORDER BY ordered_at DESC,order_id DESC LIMIT 2; SELECT order_id,ordered_at FROM c41_orders WHERE ordered_at<'2026-02-05 12:00:00' OR(ordered_at='2026-02-05 12:00:00' AND order_id<103) ORDER BY ordered_at DESC,order_id DESC LIMIT 2;
最初は104・103、103の時刻とIDを使う次は102・101です。同時刻を落とさないためID条件も必要で、方向を変えたら不等号も見直します。キーセットは深いOFFSETを避け得ますが、任意番号のページ移動とは違います。同時更新の間に一覧を固定するには別の方針が必要です。
同時刻の境界を取消可能な実験で確認する
同じ接続で次を実行します。
START TRANSACTION; UPDATE c41_orders SET ordered_at='2026-02-05 12:00:00' WHERE order_id=104; SELECT order_id,ordered_at FROM c41_orders ORDER BY ordered_at DESC,order_id DESC LIMIT 1; SELECT order_id,ordered_at FROM c41_orders WHERE ordered_at<'2026-02-05 12:00:00' OR (ordered_at='2026-02-05 12:00:00' AND order_id<104) ORDER BY ordered_at DESC,order_id DESC LIMIT 1; ROLLBACK;
一ページ目は104、次は同時刻の103です。時刻だけを境界にすると飛ばす場合があります。ROLLBACK後に104の元時刻も確認します。データを書き換えたまま比較しないでください。
投影列・検索模様・条件を記録する
SELECT *を減らすと大きい文章の転送などを避けられますが、対象行を減らすこととは別です。カバリングの可能性も計画で確認します。通常のB-treeでは前方一致と先頭ワイルドカードの部分一致で可能な経路が違うので、検索意図と照合順序を先に決めます。
両SQL、返るキー、版、索引、件数と分布を記録し、キャッシュ差を切り分けます。関連する別のソートやページ要件も同じ経路へ依存していないかを調べます。
やり直すときは、実際のDB・定義・行を確認し、必要なデータを保存して、この記事の削除可能な表だけを子から親の順に消します。DROP TABLEは定義とデータを削除し、通常のROLLBACKでは戻せません。
DROP TABLE shopcourse.c41_items; DROP TABLE shopcourse.c41_orders; DROP TABLE shopcourse.c41_products; DROP TABLE shopcourse.c41_customers;




