次のようなレガシーテーブルがあります。
従業員
------------------------------------
| employee_id | name
------------------------------------
| 1 | David
| 2 | Mathew
------------------------------------
給与
-------------------------------------
| employee_id | salary
-------------------------------------
| 2 | 200000
| 3 | 90000
-------------------------------------
結合とフィルターの後で、次のデータを取得したいと思います。
-----------------------------------------------------------
| address_id | employee_id | address
-----------------------------------------------------------
| 1 | 2 | street 1, NY
| 2 | 2 | street 2, DC
------------------------------------------------------------
次のクエリがあります。
SELECT employee_id, salary, address_arr
FROM employee
LEFT JOIN payroll on payroll.employee_id = employee.employee_id
INNER JOIN
(
SELECT employee_id, ARRAY_AGG(address) as address_arr
FROM addresses
GROUP BY employee_id
) table_address ON table_address.employee_id = employee.employee_id
WHERE employee.employee_id < 1000000
LIMIT 100
OFFSET 0
上記のクエリは目的の出力を提供しますが、JOIN
オペレーションで外部クエリに使用される前に、完全なアドレステーブルに対してGROUP BY
オペレーションが発生するため、非常に最適化されていません。
親切に答えてください:
GROUP BY
を使用して、アドレステーブル全体でLIMIT OFFSET
操作が発生しないようにするにはどうすればよいですか?WHERE employee.employee_id < 1000000
は、内部クエリのGROUP BY
操作の前または後にサブクエリに適用されますか。 GROUP BY
の後に条件が適用される場合、どうすればそれを回避できますか?注:使用されている実際のクエリには、複数のJOIN
sとサブクエリがあります。
これが本当に効率的かどうかはわかりませんが、制限を適用する派生テーブルに結合しようとすることができます。
select emp.employee_id, emp.salary, adr.address_arr
from (
SELECT employee_id, salary, address_arr
FROM employee
LEFT JOIN payroll on payroll.employee_id = employee.employee_id
WHERE employee.employee_id < 1000000
LIMIT 100
OFFSET 0
) as emp
JOIN (
SELECT a.employee_id, ARRAY_AGG(a.address) as address_arr
FROM addresses a
GROUP BY employee_id
) as adr ON adr.employee_id = emp.employee_id;
最初の派生テーブルは100行のみを選択し、結合/グループ化はそれらの100人の従業員に対してのみ実行されます。
オプティマイザがプッシュダウンしない場合は、代わりにラテラル結合を試行してプッシュダウンを「強制」できます。
select emp.employee_id, emp.salary, adr.address_arr
from (
SELECT employee_id, salary, address_arr
FROM employee
LEFT JOIN payroll on payroll.employee_id = employee.employee_id
WHERE employee.employee_id < 1000000
LIMIT 100
OFFSET 0
) as emp
LATERAL JOIN (
SELECT a.employee_id, ARRAY_AGG(a.address) as address_arr
FROM addresses a
WHERE a.employee_id = emp.employee_id
GROUP BY employee_id
) as adr ON adr.employee_id = emp.employee_id;
結合条件は実際には必要ありませんが、害はありません
私はこれが初めてで、いくつかの助けがありました、これが私が思いついたものです:
with t as (SELECT employee.employee_id, salary FROM employee LEFT JOIN payroll on payroll.employee_id = employee.employee_id WHERE employee.employee_id < 1000000 LIMIT 100 OFFSET 0)
select t.employee_id, max(t.salary), ARRAY_AGG(address) as address_arr from address left join t on address.employee_id = t.employee_id where address.employee_id = t.employee_id group by t.employee_id;
収量の分析について説明する
HashAggregate (cost=443931.83..443933.08 rows=100 width=44) (actual time=3173.705..3173.705 rows=1 loops=1)
Group Key: t.employee_id
CTE t
-> Limit (cost=313.99..316.90 rows=100 width=12) (actual time=7.441..7.511 rows=100 loops=1)
-> Hash Right Join (cost=313.99..145616.13 rows=4983381 width=12) (actual time=7.437..7.490 rows=100 loops=1)
Hash Cond: (payroll.employee_id = employee.employee_id)
-> Seq Scan on payroll (cost=0.00..76778.79 rows=4983879 width=12) (actual time=0.036..0.047 rows=100 loops=1)
-> Hash (cost=189.00..189.00 rows=9999 width=4) (actual time=7.370..7.370 rows=10000 loops=1)
Buckets: 16384 Batches: 1 Memory Usage: 480kB
-> Seq Scan on employee (cost=0.00..189.00 rows=9999 width=4) (actual time=0.016..3.783 rows=10000 loops=1)
Filter: (employee_id < 1000000)
-> Hash Join (cost=3.25..441981.29 rows=217817 width=37) (actual time=7.699..3119.096 rows=200000 loops=1)
Hash Cond: (address.employee_id = t.employee_id)
-> Seq Scan on address (cost=0.00..364792.00 rows=20002100 width=29) (actual time=0.067..1568.256 rows=20000000 loops=1)
-> Hash (cost=2.00..2.00 rows=100 width=12) (actual time=7.609..7.609 rows=100 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 13kB
-> CTE Scan on t (cost=0.00..2.00 rows=100 width=12) (actual time=7.449..7.580 rows=100 loops=1)
Planning time: 0.456 ms
Execution time: 3174.499 ms
(19 rows)
多分
SELECT employee.employee_id, payroll.salary, ARRAY_AGG(addresses.address)
FROM employee
INNER JOIN addresses ON addresses.employee_id = employee.employee_id
LEFT JOIN payroll on payroll.employee_id = employee.employee_id
WHERE employee.employee_id < 1000000
GROUP BY employee.employee_id
LIMIT 100
OFFSET 0
?
そして-あなたは本当にpayroll.salary
のNULLにつながるpayroll
テーブルの適切なレコードがないレコードで必要ですか?多分、INNER JOINで十分ですか?