#postgresql #performance #query-optimization #postgresql-11
Вопрос:
У меня есть таблицы, как показано ниже:
Имя таблицы: Table_A
A_ID | Имя |
---|---|
21 | XYZ |
22 | IJK |
Имя таблицы: Table_S1
S_ID | S_Name | A_ID |
---|---|---|
123 | ABC123 | 21 |
124 | PQR321 | 22 |
Имя таблицы: Table_S2
S_ID | S2_Date_1 | S2_Date_2 |
---|---|---|
123 | 01/01/2015 | 02/04/2016 |
124 | 01/01/2021 | 02/04/2018 |
123 | нулевой | 02/04/2019 |
124 | 01/01/2017 | нулевой |
Table_A и Table_S1 имеют первичные ключи, а где как Table_S2 не имеет первичного ключа.
Table_S1.A_ID —> ссылается на —>> Первичный ключ A_ID Table_A
Table_S2.S_ID —> ссылается на —>> S_ID первичного ключа Table_S1
В таблице Table_S1 содержится около 400 000 записей, и каждая запись в таблице Table_S1 содержит 120 записей, на которые ссылаются в таблице Table_S2. Это означает, что Table_S2-огромная таблица, содержащая около 48 миллионов записей.
У меня есть требование извлекать годы из столбцов Table_S2 S2_Date_1 и S2_Date_2 по отдельности, используя 2 разных запроса, как показано ниже.
SELECT DISTINCT EXTRACT(YEAR FROM s2.S2_Date_1)
FROM Table_S2 s2, Table_S1 s1, Table_A a
WHERE s2.S_ID = s1.S_ID
AND s1.A_ID = a.A_ID
AND s1.B_ID = b.B_ID
AND a.A_Name = 'IJK';
Если я сгруппируюсь по A_ID, записи будут такими, как показано ниже:
A_ID | счет_из_таблиц 1 | Count_of_Table_S2 |
---|---|---|
21 | 100,000 | 12,000,000 |
22 | 150,000 | 18,000,000 |
23 | 90,000 | 10,800,000 |
В таблице Table_S2 нет разделов. Для извлечения всех лет из этих миллионов записей требуется около 1 минуты, мое требование-получить их за секунду. Возможно ли это сделать? Может ли кто-нибудь, пожалуйста, предложить лучший способ сделать это?
Заранее спасибо.
Комментарии:
1. Пожалуйста, покажите
EXPLAIN (ANALYZE, BUFFERS)
выходные данные для запроса, в идеале после настройкиtrack_io_timing = on
.2. Похоже, вам не нужны какие-либо столбцы объединенных таблиц, переписывание древних неявных соединений в СУЩЕСТВУЮЩЕЕ состояние может повысить производительность.
3. Вы не сможете динамически суммировать такое количество записей за секунду. Используйте материализованный вид.
Ответ №1:
У вас есть очень много строк для подведения итогов. Достоверно сократить это до одной секунды может оказаться невозможным.
Вы можете попробовать включить сохраненный сгенерированный столбец Table_S2
, а затем проиндексировать его.
ALTER TABLE Table_S2
ADD COLUMN S2_Date1_Year
GENERATED ALWAYS AS EXTRACT(YEAR FROM s2.S2_Date_1)
STORED;
Это дает вам колонку. Затем
ALTER TABLE TABLE_S2 ADD INDEX year_by_sid (S_ID, S2_Date1_Year);
добавляет индекс. Затем измените свой запрос на
SELECT DISTINCT s2.S2_Date1_Year ...
Возможно, но не обязательно, что ваш запрос будет быстрее: ему не нужно пересчитывать год в каждой строке.
Вместо этого вам может понадобиться материализованный вид.
CREATE MATERIALIZED VIEW View_S2_Year AS
SELECT DISTINCT S_ID, EXTRACT(YEAR FROM s2.S2_Date_1) AS S2_Date1_Year
FROM Table_S2;
CREATE INDEX year_by_sid ON View_S2_Year (S_ID, S2_Date1_Year);
Затем присоединитесь к этому View_S2_Year, а не к Table_S2.
Примечание. Вам нужно будет время от времени обновлять представление, чтобы синхронизировать его с базовой таблицей. Его содержимое будет устаревать после внесения изменений в базовую таблицу до тех пор, пока вы ее не обновите.
REFRESH MATERIALIZED VIEW View_S2_Year;
Комментарии:
1. Огромное всем вам спасибо. Я попробую этим способом и посмотрю, смогу ли я сделать все так просто, как вы упомянули. Еще раз поделюсь результатом. 🙂