これが間違った場所である場合はお詫びしますが、私のクエリが適切かどうかについては本当に意見が必要です。
基本的に、グループごとの古いエントリと無効なエントリの数を取得する必要があります。これは、NULL値または単に1か月前の値で識別されます。選択したデータは、「統計」と呼ばれる小さなテーブルに保存されます。 PHPはcronスクリプトに使用されます。
さて、問題は私がまだ初心者なので、クエリが高速か低速かわからないことです。次のクエリを完了するには、約3分かかります。上司によると遅いため、少なくとも50%高速にする必要がありますが、スピードアップのために何かできるかどうか私自身はわかりません。 「cat_01」テーブルには50ミルの行があり、他のテーブルには2000未満です。
使用したすべての列にインデックスを追加したため、時間が6分から3分に短縮されました。
私が使用しているクエリは次のとおりです(テーブルと列に仮名を使用したので、ロジックは気にしないでください)。
SELECT
'cat_01' as category,
m.shop_id,
o.name,
count(m.shop_id) as total,
tr2.traffic,
IF(mm.bought IS NULL,0,mm.bought) as bought
FROM cat_01 m
JOIN shops o
ON m.shop_id = o.id
JOIN (
select
mmm.shop_id,
max(mmm.buy_date) as bought
FROM cat_01 mmm
GROUP BY mmm.shop_id
) mm
ON o.id = mm.shop_id
LEFT JOIN (
SELECT tr.shop_id, sum(tr.sales) AS traffic
FROM (
SELECT
mmmmm.shop_id,
bs.sales,
bs.order_id,
bs.id
FROM cat_01 mmmmm
JOIN orders bs
ON mmmmm.order = bs.order_id
) tr group by tr.shop_id
) tr2
ON tr2.shop_id = mm.shop_id
WHERE (m.buy_date IS NULL)
OR (m.buy_date < UNIX_TIMESTAMP())
GROUP BY m.shop_id
CREATE TABLE `cat_01` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`shop_id` int(11) DEFAULT '0',
`buy_date` int(11) DEFAULT NULL,
`order` int(11) DEFAULT '0',
PRIMARY KEY (`id`),
KEY `shop_id` (`shop_id`),
KEY `buy_date` (`buy_date`),
KEY `order` (`order`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
CREATE TABLE `shops` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
CREATE TABLE `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`order_id` int(11) NOT NULL,
`sales` int(11) NOT NULL DEFAULT '0',
`status` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`),
KEY `order_id` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
これが説明です(自宅からライブデータにアクセスできないため、ダミー行を使用しましたが、残りは同じです)
[〜#〜] edit [〜#〜]:例としてUNIX_TIMESTAMP()を追加
EDIT2:SELECTからサブクエリを削除して別々に使用し、PHPループを使用して、結果を混合して適切なINSERTクエリを作成しました。
追伸sctackechangeでのSQLコードのフォーマットがどのように見えるかの直接的な例を見つけることができないようです。そのため、文字化けが発生した場合は、すみやかに修正してみます。
私はEXPLAIN結果を持っていますが、それをフォーマットする方法がわかりません、後で画像を投稿する可能性があります。
特定の情報が必要な場合は、遠慮なく尋ねてください。私は何を調べればよいかわかりません。
FROM ( SELECT ... ) JOIN ( SELECT ... )
うまく最適化されません。クエリを作成するより良い方法を考えてください。それが失敗した場合は、サブクエリの1つをTEMPORARY TABLE
に入れ、それにインデックスを追加します。
日付には、DATE
ではなくINT
のデータ型の使用を検討してください。
OR
はパフォーマンスを向上させます(インデックスを使用できないためです)。 IS NULL
を処理する他の方法を検討してください。
その他
order_id
がorders
で一意である場合、id
とユーザーorder_id
をPRIMARY KEY
として削除します。 (さらに他のテーブルに必要な変更はありますか?)PKによる検索は、2次キーによる検索よりも高速です。
これらのテーブルの一部の情報が「静的」である(一度書き込まれたが、更新または削除されていない)場合は、「要約テーブル」の作成と保守を検討してください。
このクエリを13のカテゴリに対して13回実行する場合、GROUP BY category_id
を使用すると、1回の実行がmay速くなります。
OR
をUNION
mayに変換すると、特により良いインデックスを使用できる場合にスピードアップします。
まず、次のインデックスを追加します。
ALTER TABLE cat_01
ADD INDEX `ShopBuyDates` (`shop_id`,`buy_date`)
ALTER TABLE orders
ADD INDEX `OrderSales` (`order_id`,`sales`)
次に、このクエリを試して、結果を報告します。少なくとも2回は実行し、最初の結果のパフォーマンスを破棄して、キャッシュへのデータ入力の影響を洗い流してください。
SELECT
'cat_01' as category,
m.shop_id,
o.name,
count(m.shop_id) as total,
tr2.traffic,
IF(mm.bought IS NULL,0,mm.bought) as bought
FROM cat_01 m
JOIN shops o
ON m.shop_id = o.id
JOIN (
select
mmm.shop_id,
max(mmm.buy_date) as bought
FROM cat_01 mmm
GROUP BY mmm.shop_id
) mm
ON o.id = mm.shop_id
LEFT JOIN (
SELECT mmmmm.shop_id, sum(bs.sales) AS traffic
FROM cat_01 mmmmm
JOIN orders bs
ON mmmmm.order = bs.order_id
group by mmmmm.shop_id
) tr2
ON tr2.shop_id = mm.shop_id
WHERE (m.buy_date IS NULL)
OR (m.buy_date = 0)
OR (m.buy_date < 1)
GROUP BY m.shop_id
これによる改善を確認した後(このクエリからの新しいEXPLAINも良いでしょう)、反復してクエリの他の原因を確認できます。