We've used functions in PostgreSQL before.
Simple math functions like SUM(), AVG(), MAX(), MIN() etc
In this document we look at important SQL functions you should know about.
Then we create functions based on SELECT statements.
SQL functions use simple queries. They are efficient but lack any logic control such as LOOPs and variables.
For that we need a procedural language such as PL/pgSQL.
see https://www.postgresql.org/docs/current/functions-conditional.html#FUNCTIONS-COALESCE-NVL-IFNULL
The COALESCE function returns the first of its arguments that is not null.
If all arguments are null, COALESCE returns null.
select COALESCE(1,2,3,4,5,6) -> 1select COALESCE(NULL,2) -> 2select COALESCE(NULL,NULL) -> NULLIf the last column is a string COALESCE will always return that string if the other arguments are NULL.
So COALESCE is often used to substitute a default value for null values when data is retrieved for display, for example:
This query returns description if it is not null, otherwise short_description if it is not null, otherwise (none).
SELECT COALESCE(description, short_description, '(none)')
This query returns unknown when domain is null
SELECT COALESCE(domain, 'unknown' ) from trees; --
We're working with the normalized version of the trees database. treesdb_v03
Using the trees table. select a random number of trees, (id, height, remarkable).
If the column remarkable is null, display unknown instead
Note: all arguments of
COALESCEneed to have the same type. To cast a Boolean into a text usename_of_boolean_column::text.
[solution]
<your query>
To concatenate string you can use the CONCAT function which concatenates the text representations of all the arguments and ignores NULL arguments.
concat ( val1 "any" [, val2 "any" [, ...] ] ) → text
For instance:
concat('abcde', 2, NULL, 22) → abcde222
|| symbolYou can also use the || symbol to concatenate strings.
text || text → text
Concatenates the two strings.
'Post' || 'greSQL' → PostgreSQL
Using the treesdb_v03, concatenate multiple columns into one
columns are :
also
UNK, using COALESCE.concat_ws to add a '-' (upper dash) between all the columnsREPLACE()see https://www.postgresql.org/docs/current/functions-string.html
Note: you don't have to return all the rows each time. You can limit to N random() rows;
Note: first build the main query that returns all the columns, then coalesce, then concatenate, then replace
<your query>
You can create a SQL function with:
CREATE [OR REPLACE] FUNCTION function_name(parameter1 type, parameter2 type, ...)
RETURNS return_type
AS $$
SQL_statement()
$$ LANGUAGE SQL;
where SQL_STATEMENT is a SQL query.
For instance
CREATE OR REPLACE FUNCTION add_numbers(a integer, b integer)
RETURNS integer
AS $$
SELECT a + b;
$$ LANGUAGE SQL;
which you can then use directly with :
SELECT add_numbers(5, 3);
If SQL functions are just simple queries, why use them ? Why not write the SQL query instead ?
Using a simple SQL function to insert a new row versus using a direct INSERT query has a few potential benefits and trade-offs, depending on your use case. Here are some points to consider:
Reusability:
INSERT query multiple times in different parts of your application.Abstraction:
INSERT query across your application.Encapsulation of Logic:
INSERT only if a certain condition is met, like checking if the record already exists in the table.Parameter Handling:
Security and Permissions:
INSERT permissions on the table. This can be useful for controlling access and enforcing specific behavior.insert_customer function but do not give them direct INSERT privileges on the customers table.Transaction Control:
Overhead:
INSERT query, as the function adds an extra layer of abstraction.INSERT query might be faster.Complexity:
INSERT without additional logic), the benefit of using a function might be minimal, and a direct INSERT query could suffice.Less Flexibility (in simple SQL functions):
Debugging and Transparency:
INSERT where you can see exactly what query is being executed.Using a function:
CREATE OR REPLACE FUNCTION insert_customer(c_name TEXT, c_email TEXT)
RETURNS VOID AS $$
INSERT INTO customers (name, email)
VALUES (c_name, c_email);
$$ LANGUAGE sql;
-- Then you can call it:
SELECT insert_customer('John Doe', '[email protected]');
Direct query:
INSERT INTO customers (name, email)
VALUES ('John Doe', '[email protected]');
In summary, functions provide reusability, encapsulation, and the ability to enforce logic, but if your insert operation is straightforward with no additional logic, a direct INSERT query is sufficient.
In PostgreSQL, function volatility categories (VOLATILE, STABLE, and IMMUTABLE) are important concepts that inform the query planner about the behavior of functions.
Setting the volatility of the SQL function helps the query planner optimize it.
To specify if a function should be considered as VOLATILE, STABLE or IMMUTABLE, you add the keyword after
$$ LANGUAGE SQL <VOLATILE | STABLE | IMMUTABLE>;
Here's an example of a VOLATILE function:
CREATE FUNCTION get_random_number() RETURNS integer AS $$
SELECT floor(random() * 100)::integer;
$$ LANGUAGE SQL VOLATILE;
Here's an example of a STABLE function:
CREATE FUNCTION get_current_timestamp() RETURNS timestamp AS $$
SELECT current_timestamp;
$$ LANGUAGE SQL STABLE;
-- Usage:
SELECT get_current_timestamp();
Here's an example of an IMMUTABLE function:
CREATE FUNCTION add_numbers(a integer, b integer) RETURNS integer AS $$
SELECT a + b;
$$ LANGUAGE SQL IMMUTABLE;
Key Points:
Optimization:
Default Behavior:
Performance Impact:
Incorrect Categorization:
Usage in Indexes:
Here's an example demonstrating how volatility affects query planning:
CREATE TABLE items (id serial PRIMARY KEY, price numeric);
-- VOLATILE function (default)
CREATE FUNCTION get_tax_rate() RETURNS numeric AS $$
SELECT 0.1; -- Assume this could change
$$ LANGUAGE SQL;
-- IMMUTABLE function
CREATE FUNCTION calculate_tax(price numeric) RETURNS numeric AS $$
SELECT price * 0.1;
$$ LANGUAGE SQL IMMUTABLE;
-- This query will recalculate get_tax_rate() for every row
EXPLAIN ANALYZE SELECT id, price * get_tax_rate() FROM items;
-- This query can optimize the calculate_tax() call
EXPLAIN ANALYZE SELECT id, calculate_tax(price) FROM items;
In the second query, the planner might be able to compute calculate_tax just once if it determines that this optimization is beneficial.
Rule of thumb:
It's safer to choose the more volatile option. Incorrectly marking a function as less volatile than it actually is can lead to incorrect query results, while being more conservative only potentially misses some optimization opportunities.