나는 하나의 테이블 회사 데이터가 저장되는 (PostgreSQL 9.6에서) 데이터베이스 디자인을 가지고 있습니다. 각 회사에는 세부 사항이 다른 테이블에 배치 된 담당자가 한 명 이상있을 수 있습니다. 그런 (간체) 스키마되는 것을 : JSON 배열 데이터의 하위 쿼리 대신 JOIN을 사용 하시겠습니까?
DROP TABLE IF EXISTS test_company;
CREATE TABLE test_company (id integer, company_name text, contact_person integer[]);
DROP TABLE IF EXISTS test_contact_person;
CREATE TABLE test_contact_person (id integer, person_name text);
지금과 같은 데이터를 고려
: 당신이 볼INSERT INTO test_company (id, company_name, contact_person) VALUES (1, 'Foo Ldt.', '{1,2}');
INSERT INTO test_company (id, company_name, contact_person) VALUES (2, 'Foo Sub Inc.', '{1,2}');
INSERT INTO test_company (id, company_name, contact_person) VALUES (3, 'Foo Sub Sub Inc.', '{1}');
INSERT INTO test_company (id, company_name, contact_person) VALUES (4, 'Bar Inc.', '{3,4}');
INSERT INTO test_company (id, company_name, contact_person) VALUES (5, 'Foo-Bar Joint-Venture', '{2,3,4}');
INSERT INTO test_contact_person(id, person_name) VALUES (1,'John');
INSERT INTO test_contact_person(id, person_name) VALUES (2,'Maria');
INSERT INTO test_contact_person(id, person_name) VALUES (3,'Bill');
INSERT INTO test_contact_person(id, person_name) VALUES (4,'Jane');
가, 한 사람이 여러 회사의 연락처, 심지어는 "쌍"수 ('{1,2}'
같은 수 있습니다). 조회 회사 인 경우에 지금
요구 사항, 즉 : 기업 당
- 한 행은 연락처 사람의
- 세부 사항은
[{"id":1,"person_name":"John"}]
지금 당장 하위 쿼리를 사용하여 문제를 해결할 수 있습니다.
SELECT
id,
company_name,
(
SELECT json_agg(my_subquery) FROM
(
SELECT id, person_name FROM test_contact_person
WHERE id = ANY(test_company.contact_person)
)
AS my_subquery
)
contact_person_expanded
FROM test_company;
예상되는 결과를 얻을 수 있습니다. 그러나 (항상 그렇듯이) 성능은 만족스럽지 않습니다. BTW : 현재 테이블에 인덱스가 없습니다. 지금 궁금합니다.
- JOIN을 사용하면 쿼리가 빨라 집니까? 그렇다면 정확히 JOIN이 JSON 배열을 반환하도록하려면 어떻게해야합니까?
- 인덱스를 사용하면 성능이 향상됩니까? '예'인 경우 : 어떤 종류의 색인 열입니까?
업데이트
그냥 참조를 위해 내가 Radim BACA에 의해 제안 된 솔루션은 성능을 확보 측면에서 작동하는 것 같다 점을 지적하고 싶습니다.
먼저 내가 다시 쿼리 내 버전을 시도
DROP TABLE IF EXISTS test_company;
CREATE TABLE test_company (id integer, company_name text, contact_person integer[]);
DROP TABLE IF EXISTS test_contact_person;
CREATE TABLE test_contact_person (id integer, person_name text);
DO $$
for(var i = 1; i < 20000; i++) {
plv8.execute('INSERT INTO test_contact_person(id, person_name) VALUES ($1,$2)',[i,'SomePerson' + i]);
}
for(var i = 1; i < 10000; i++) {
plv8.execute('INSERT INTO test_company (id, company_name, contact_person) VALUES ($1,$2,$3)',[i,'SomeCompany' + i,[i,(20 -i)]]);
}
$$ LANGUAGE plv8;
못생긴 plv8 루프에 더 많은 데이터를 입력 :
SELECT
id,
company_name,
(
SELECT json_agg(my_subquery) FROM
(
SELECT id, person_name FROM test_contact_person
WHERE id = ANY(test_company.contact_person)
)
AS my_subquery
)
contact_person_expanded
FROM test_company;
저를 제공합니다 (항상 내 지역에서 측정 pgAdmin의 컴퓨터 3) 약 23 초 실행 시간,
SELECT
comp.id,
comp.company_name,
json_agg(json_build_object('id', pers.id, 'person_name', pers.person_name)) AS contact_person_expanded
FROM test_company comp
JOIN test_contact_person pers ON comp.contact_person @> ARRAY[pers.id]
GROUP BY comp.id, comp.company_name
약 47 초가 소요됩니다. 색인없이.
DROP INDEX IF EXISTS idx_testcompany_contactperson;
CREATE INDEX idx_testcompany_contactperson on test_company USING GIN ("contact_person");
변경되지 않습니다 하위 쿼리와 버전의 실행 시간은이 효과를 조인을 사용하지만 때하는 극적이다 : 1.1 초
마지막으로 인덱스를 추가!
BTW : 서브 쿼리에서 test_company.contact_person @> ARRAY[id]
이 id = ANY(test_company.contact_person)
보다 빠르다고 한 번 들었습니다. 내가 테스트 한 내용은 사실이 아닙니다. 필자의 경우, 후자의 버전은 23 초 만에 모든 행을 반환했다. 처음에는 46 초가 걸렸다.
같은 GIN 인덱스를 사용하지 않습니다 허용해야합니다. 연락 담당자를 배치 한 회사 당 하나의 행이 필요합니다. 필터링은 다른 문제이며 여기서는 직접 관련이 없습니다. – cis
@cis가 업데이트되었습니다. 필터가 전체 솔루션의 성능 및 색인과 밀접하게 관련되어 있다고 생각합니다. –
감사합니다! 적어도 나에게 약간의 힌트를 준다. 그러나 수십개의 테이블과 컬럼을 재 설계하는 것은 의문의 여지가 있습니다. 그래서, 내 데이터베이스 디자인과 붙어있어 문제가 그 경우에 관한 쿼리를 최적화하는 방법입니다. 물론 필터는 성능 및 인덱스와 관련이 있지만 지금은 "JSON 배열에 대한 하위 쿼리 대 JOIN"문제에 대한 직접 연결을 볼 수 없습니다. – cis