snippetsqlMinor
Query result precedence if filter columns have values
Viewed 0 times
resultcolumnsqueryfiltervaluesprecedencehave
Problem
I have a table
The two
So we could have a default row for the company, and then one or more rows that override a configuration value for a specific warehouse or warehouse & section.
I want to return only the most specific row for a given company, warehouse, and section. Something like this pseudocode:
The most specific row shall take precedence.
Update
There can be multiple entries for the same
Is it also possible to return multiple rows for a single keyword?
"customer_config" with these columns:company (varchar)
warehouse (numeric)
section (numeric)
config_keyword (varchar)
config_value (varchar)The two
config_* columns can apply to an entire company (warehouse and section are null), an entire warehouse within a company (section is null), or a section within a warehouse.So we could have a default row for the company, and then one or more rows that override a configuration value for a specific warehouse or warehouse & section.
I want to return only the most specific row for a given company, warehouse, and section. Something like this pseudocode:
results = select * from customer_config where (all match)
if results empty
results = select * from customer_config where (company_code and warehouse match)
if results empty
results = select * from customer_config where (company_code matches)The most specific row shall take precedence.
Update
There can be multiple entries for the same
config_keyword on the same level.Is it also possible to return multiple rows for a single keyword?
Solution
"Only the most specific row"
While looking for a single result row (like your original question indicated):
This is a bit more verbose, but very clear and probably fastest if supported with an index on
Postgres will stop executing as soon as a row has been found. Test with
Note that
Similar:
"All rows for the most specific match"
If multiple rows can exist on each level.
Can be solved with pure SQL of course. For instance with a chain of CTEs. But this custom PL/pgSQL function should be more efficient:
Call:
Be sure to have the index mentioned above.
If the first query returns any rows, the function is done. The rest is not even planned. Etc.
Related:
I made the function
Allow plpgsql's
While looking for a single result row (like your original question indicated):
This is a bit more verbose, but very clear and probably fastest if supported with an index on
(company_code, warehouse, section) - like you should probably have anyway (depends on undisclosed info).SELECT * FROM customer_config
WHERE (company_code, warehouse, section) = ($1, $2, $3)
UNION ALL
SELECT * FROM customer_config
WHERE (company_code, warehouse) = ($1, $2)
UNION ALL
SELECT * FROM customer_config
WHERE company_code = $1
LIMIT 1;Postgres will stop executing as soon as a row has been found. Test with
EXPLAIN ANALYZE, you'll see "never executed" for remaining sub-SELECTs.Note that
LIMIT 1 applies to the whole query, not the last SELECT. (You'd need parentheses to change that.)Similar:
- How to force order of WHERE clauses in PostgreSQL?
"All rows for the most specific match"
If multiple rows can exist on each level.
Can be solved with pure SQL of course. For instance with a chain of CTEs. But this custom PL/pgSQL function should be more efficient:
CREATE OR REPLACE FUNCTION trade_volume (_company_code varchar, _warehouse numeric, _section numeric)
RETURNS SETOF customer_config
LANGUAGE plpgsql STABLE PARALLEL SAFE AS
$func$
BEGIN
RETURN QUERY
SELECT * FROM customer_config
WHERE (company_code, warehouse, section) = ($1, $2, $3);
IF FOUND THEN RETURN; END IF;
RETURN QUERY
SELECT * FROM customer_config
WHERE (company_code, warehouse) = ($1, $2);
IF FOUND THEN RETURN; END IF;
RETURN QUERY
SELECT * FROM customer_config
WHERE company_code = $1;
END
$func$;Call:
SELECT * FROM trade_volume ('my_comany_code', 123456, 123);Be sure to have the index mentioned above.
If the first query returns any rows, the function is done. The rest is not even planned. Etc.
Related:
- Return default rows from a function when first SELECT does not return rows
- Writing a view that caches to another table
I made the function
PARALLEL SAFE to allow parallelism in Postgres 14 or later. (Only relevant for big tables.) Quoting the release notes:Allow plpgsql's
RETURN QUERY to execute its query using parallelism (Tom Lane)Code Snippets
SELECT * FROM customer_config
WHERE (company_code, warehouse, section) = ($1, $2, $3)
UNION ALL
SELECT * FROM customer_config
WHERE (company_code, warehouse) = ($1, $2)
UNION ALL
SELECT * FROM customer_config
WHERE company_code = $1
LIMIT 1;CREATE OR REPLACE FUNCTION trade_volume (_company_code varchar, _warehouse numeric, _section numeric)
RETURNS SETOF customer_config
LANGUAGE plpgsql STABLE PARALLEL SAFE AS
$func$
BEGIN
RETURN QUERY
SELECT * FROM customer_config
WHERE (company_code, warehouse, section) = ($1, $2, $3);
IF FOUND THEN RETURN; END IF;
RETURN QUERY
SELECT * FROM customer_config
WHERE (company_code, warehouse) = ($1, $2);
IF FOUND THEN RETURN; END IF;
RETURN QUERY
SELECT * FROM customer_config
WHERE company_code = $1;
END
$func$;SELECT * FROM trade_volume ('my_comany_code', 123456, 123);Context
StackExchange Database Administrators Q#310146, answer score: 5
Revisions (0)
No revisions yet.