Вы можете сделать это, LEFT JOIN
используя CTE для каждого типа дочерней таблицы, для которой вы хотите подавить родительские результаты (я изменил history_payslip.id
на payslip_id
, чтобы упростить объединения):
WITH hours AS (
SELECT hpd.payslip_id, hpd.employee_id, hpd.year, hpd.month
FROM history_payslip_data hpd
--JOIN history_payslip hp USING (payslip_id)
JOIN history_payslip_data_hours h USING (payslip_id)
WHERE h.code in ('code1', 'code2', 'code3')
), other AS (
SELECT hpd.payslip_id, hpd.employee_id, hpd.year, hpd.month
FROM history_payslip_data hpd
--JOIN history_payslip hp USING (payslip_id)
JOIN history_payslip_data_other o USING (payslip_id)
WHERE o.code in ('code1', 'code2', 'code3')
)
SELECT hp.payslip_id as pid, hpd.employee_id as eid, hpd.year, hpd.month
FROM history_payslip_data hpd
JOIN history_payslip hp USING (payslip_id)
LEFT JOIN hours h USING (payslip_id, employee_id)
LEFT JOIN other o USING (payslip_id, employee_id)
WHERE hpd.payslip_type = 'PL'
AND hp.locked IS true
AND h.payslip_id IS NULL
AND o.payslip_id IS NULL
Вот как я тестирую это с запросом выше, сохраненным как new-query.sql
:
main.sql:
CREATE SCHEMA so54111738;
SET search_path=so54111738;
\i ./tables.ddl
\i ./setup.sql
--\i ./query.sql
\i ./new-query.sql
DROP SCHEMA so54111738 CASCADE;
tables.ddl:
CREATE TABLE history_payslip (
payslip_id int,
locked boolean
);
CREATE TABLE history_payslip_data (
id serial,
employee_id int,
payslip_id int,
payslip_type text,
year int,
month int
);
CREATE TABLE history_payslip_data_hours (
payslip_id int,
code text
);
CREATE TABLE history_payslip_data_other (
payslip_id int,
code text
);
setup.sql:
/* these two groups of inserts represent a payslip plus an hours row that means it should be excluded */
/* payslip 1 for employee 2; hours entry with code 4 */
INSERT INTO history_payslip (payslip_id, locked) VALUES (1, true);
INSERT INTO history_payslip_data (payslip_id, payslip_type, employee_id, year, month) VALUES (1, 'PL', 2, 2018, 12);
INSERT INTO history_payslip_data_hours (payslip_id, code) VALUES (1, 'code4');
/* payslip 1 for employee 2; hours entry with code 3 */
INSERT INTO history_payslip (payslip_id, locked) VALUES (1, true);
INSERT INTO history_payslip_data (payslip_id, payslip_type, employee_id, year, month) VALUES (1, '??', 2, 2018, 12);
INSERT INTO history_payslip_data_hours (payslip_id, code) VALUES (1, 'code3');
/* these two groups of inserts represent a payslip plus an other row that means it should be excluded */
/* payslip 2 for employee 3; other entry with code 4 */
INSERT INTO history_payslip (payslip_id, locked) VALUES (2, true);
INSERT INTO history_payslip_data (payslip_id, payslip_type, employee_id, year, month) VALUES (2, 'PL', 3, 2018, 11);
INSERT INTO history_payslip_data_other (payslip_id, code) VALUES (2, 'code4');
/* payslip 2 for employee 3; other entry with code 3 */
INSERT INTO history_payslip (payslip_id, locked) VALUES (2, true);
INSERT INTO history_payslip_data (payslip_id, payslip_type, employee_id, year, month) VALUES (2, '??', 3, 2018, 11);
INSERT INTO history_payslip_data_other (payslip_id, code) VALUES (2, 'code3');
/* this group of inserts represents a payslip that should be returned because it has no matching hours or other entries */
/* payslip 5 for employee 5; hours entry with code 4 */
INSERT INTO history_payslip (payslip_id, locked) VALUES (5, true);
INSERT INTO history_payslip_data (payslip_id, payslip_type, employee_id, year, month) VALUES (5, 'PL', 5, 2018, 10);
INSERT INTO history_payslip_data_hours (payslip_id, code) VALUES (5, 'code4');