複数の自己結合の最適化、またはより優れたテーブル/ DB設計に関するアドバイスを探しています。
テーブルの1つは次のようになります(関連するcolsのみ)。
CREATE TABLE IF NOT EXISTS CountryData (
countryDataID INT PRIMARY KEY AUTO_INCREMENT,
dataID INT NOT NULL REFERENCES DataSources (dataID),
dataCode VARCHAR(30) NULL,
countryID INT NOT NULL REFERENCES Countries (countryID),
year INT NOT NULL ,
data DEC(20,4) NULL,
INDEX countryDataYear (dataID, countryID, year));
data
列には、数百の指標、90か国、合計で100万行までの30年の値があります。標準クエリでは、特定の年とCの国のN個のインジケーターを選択し、最大90行のCxNテーブルを生成する必要があります。
すべての値が1つの列にあるため、自己結合は適切な方法のように見えました。そのため、インデックス作成や新しい(一時)テーブルの作成など、これらを高速化するためのさまざまな提案を試しました。 9回の自己結合では、クエリは1分弱かかります。それを超えて、それは永遠に回転します。
自己結合が行われる新しいテーブルには、約1,000行しかなく、関連する変数と思われるものにインデックスが付けられています。作成には約0.5秒かかります。
CREATE TABLE Growth
SELECT dataID, countryID, year, data
FROM CountryData
WHERE dataID > 522 AND year = 2017;
CREATE INDEX growth_ix
ON Growth (dataID, countryID);
SELECT
クエリは、結果テーブルに最大XXのインジケーターを配置しますが、XXは残念ながら<10です。
SELECT
Countries.countryName AS Country,
em01.em,
em02.em,
em03.em
...
emX.em
FROM
(SELECT
em1.data AS em,
em1.countryID
FROM Growth AS em1
WHERE
em1.dataID = 523) as em01
JOIN
(SELECT
em2.data AS em,
em2.countryID
FROM Growth AS em2
WHERE
em2.dataID = 524) as em02
USING (countryID)
JOIN
(SELECT
em3.data AS em,
em3.countryID
FROM Growth AS em3
WHERE
em3.dataID = 525) as em03
USING (countryID)
...
JOIN
(SELECT
emX.data AS em,
emX.countryID
FROM Growth AS em5
WHERE
emX.dataID = 527) as emXX
USING (countryID)
JOIN Countries
USING (countryID)
さらにいくつかの変数を取得し、さらに他のテーブルを結合する可能性があります。これをより効率的に実行する方法があるのか、または自己結合を回避するために異なる列にインジケーターがあるワイドテーブルを使用するなど、まったく異なるアプローチを取るべきなのか、と私は思っています。
テーブルを非正規化する必要はないと思います。効果的に使用されるインデックスがある場合、自己結合は正常に機能します。特定のクエリでは、(year, dataID, countryID, data)
にインデックスを追加し、派生テーブルは使用しません。
SELECT
c.countryName AS Country,
em01.data AS data01,
em02.data AS data02,
...
emXX.data AS dataXX
FROM
Countries AS c
JOIN CountryData AS em01
ON em01.year = 2017
AND em01.dataID = 523
AND em01.countryID = c.countryID
JOIN CountryData AS em02
ON em02.year = 2017
AND em02.dataID = 524
AND em02.countryID = c.countryID
...
JOIN CountryData AS emXX
ON emXX.year = 2017
AND emXX.dataID = YYY
AND emXX.countryID = c.countryID
;
あなたが持つ唯一の問題は、MySQLがクエリで最大61の結合というハード制限を持っていることです。したがって、上記のクエリでは90列にすることはできません。
別の観察として、集約されたデータは必要ないようですが、(小さなテーブルでも大きなテーブルでも構いません)のごく一部です。推奨インデックスを使用すると、次のようなクエリを作成できます。
SELECT
cd.countryID,
c.countryName AS Country,
cd.dataID,
cd.data
FROM
Countries AS c
JOIN CountryData AS cd
ON cd.countryID = c.countryID
WHERE
cd.year = 2017
AND cd.dataID IN (522, 523, ..., YYY)
ORDER BY
cd.countryID,
cd.dataID ;
アプリケーションでピボット変換を実行します。