I would first rethink the design. You only need one table:
CREATE TABLE c (
id integer PRIMARY KEY
,parent_id integer REFERENCES c(id)
,state boolean
);
With the layout as presented in the question, the query could be:
SELECT DISTINCT a.*
FROM a
JOIN b ON a.node_id = b.id
LEFT JOIN a ca ON ca.child_node_id = a.node_id
LEFT JOIN b cb ON cb.id = ca.node_id AND cb.state = FALSE
WHERE b.state = FALSE
AND cb.id IS NULL
This includes nodes that are turned off and have no children at all.
To exclude nodes without children, replace the first LEFT JOIN
with a plain JOIN
.
Or, may be faster:
SELECT a.*
FROM a
JOIN b ON a.node_id = b.id
WHERE NOT EXISTS (
SELECT 1
FROM a ca
JOIN b cb ON cb.id = ca.node_id
WHERE ca.child_node_id = a.node_id
AND cb.state = FALSE
)
You have an unresolved naming conflict.
You must be using an old version of Postgres without declaring it. Or you are operating with non-default configuration setting.
Here you declare a variable named measurement_id
:
DECLARE
measurement_id INTEGER;
It's a folly to use ambiguous variable names to begin with. If you do it anyway, you must know what you are doing. I make it a habbit to prepend variable names with an underscore unlike column names, like _measurement_id
.
The later SELECT
statement is ambiguous:
ORDER BY measurement_id
This would raise an error message in modern PostgreSQL with default configuration. Per the documentation:
By default, PL/pgSQL will report an error if a name in a SQL statement
could refer to either a variable or a table column.
And:
To change this behavior on a system-wide basis, set the configuration
parameter plpgsql.variable_conflict
to one of error, use_variable, or
use_column (where error is the factory default). This parameter
affects subsequent compilations of statements in PL/pgSQL functions,
but not statements already compiled in the current session. Because
changing this setting can cause unexpected changes in the behavior of
PL/pgSQL functions, it can only be changed by a superuser.
In Postgres older than 9.0 this would be resolved to mean the variable. Per the documentation
In such cases you can specify that PL/pgSQL should resolve ambiguous
references as the variable (which is compatible with PL/pgSQL's
behavior before PostgreSQL 9.0)
Bold emphasis mine.
This would result in arbitrary results, since the sort order is now undetermined.
Audited Function
CREATE OR REPLACE FUNCTION insaft_function()
RETURNS TRIGGER AS
$func$
DECLARE
_measurement_id integer;
_file_header_index_start integer := TG_ARGV[0]::int;
_file_header_index_end integer := TG_ARGV[1]::int;
BEGIN
SELECT a.measurement_id INTO _measurement_id
FROM measurements a
ORDER BY a.measurement_id DESC -- you had ambiguity here!
LIMIT 1;
IF TG_OP = 'INSERT' THEN -- noise if only used in AFTER INSERT trigger
INSERT INTO file_headers (measurement_id, file_header_index_start
, file_header_index_end)
VALUES (_measurement_id, _file_header_index_start, _file_header_index_end);
END IF;
RETURN NULL; -- result is ignored since this is an AFTER trigger
END
$func$ LANGUAGE plpgsql;
Note how I named it insaft_function()
, since this is only to be used in an AFTER INSERT
trigger.
Trigger:
CREATE TRIGGER insaft_measurement_ids
AFTER INSERT ON measurements
FOR EACH ROW EXECUTE PROCEDURE insaft_function(1, 666);
But for the provided setup, you can radically simplify the function:
CREATE OR REPLACE FUNCTION insaft_function()
RETURNS TRIGGER AS
$func$
BEGIN
INSERT INTO file_headers (measurement_id, file_header_index_start
, file_header_index_end)
VALUES (NEW.measurement_id, TG_ARGV[0]::int, TG_ARGV[1]::int);
RETURN NULL; -- result ignored since this is an AFTER trigger
END
$func$ LANGUAGE plpgsql;
Best Answer
CASE
probably isn't the right tool here. But you can use a Boolean expression.