CREATE FUNCTION (TVF)
On this page
Creates a user-defined table-valued function (TVF).SELECT
statement in the function body, and returns a single table-type value (similar to a view).
Syntax
CREATE [OR REPLACE] FUNCTION function_name ( [parameter_list] )
RETURNS TABLE AS RETURN function_body ;
parameter_list:
variable_name data_type [data_type_modifier] [, ...]
data_type_modifier:
DEFAULT default_value | NOT NULL | NULL | COLLATE collation_name
Return Type
-
return_
type: A scalar value or a TABLE
value.If a TABLE
value, includes a list of<column name> <data_
values such astype> TABLE(a TEXT, b INT)
.
Arguments
OR REPLACE
If specified, replaces a TVF if one already exists with the same name.
function_
The name of the function.
You can also specify database_
and function_
together by replacing function_
with database_
instead of defaulting to the current context database.
CREATE FUNCTION db.some_func(a int)...
Table Valued Function names are case-sensitive.
parameter_
Input parameters are optional.,
).
The following example shows how to declare a single input parameter:
CREATE FUNCTION single_param_example(a INT) ...
The following example demonstrates how to declare more than one input parameter:
CREATE FUNCTION multi_param_example(a INT, b VARCHAR(255), c DECIMAL(19,4)) ...
Only scalar data types are allowed for TVF input parameters, and each valid type is enumerated in the syntax block above.ARRAY
or RECORD
, cannot be declared as input parameters for TVFs.
Default values can be specified for input parameters by using the DEFAULT
constraint.
CREATE FUNCTION default_example(a INT DEFAULT 5) ...
Input parameters can also specify data type modifiers, namely NOT NULL
, NULL
, and COLLATE
.
CREATE FUNCTION modifier_example(a TEXT NOT NULL COLLATE utf8_bin) ...
You can use parameters with any supported character sets using the following syntax.
CREATE FUNCTION function_name(product_id INT, feedback VARCHAR(20) COLLATE utf8_general_ci);
…
In a similar fashion, you can use variables with any supported character sets.
For more information, see Character Encoding.
data_
Any scalar-valued data type as described in the Data Types topic.ARRAY
and RECORD
types.
function_
The function body of a TVF may contain any valid SELECT
statement.
Remarks
Table-valued functions (TVFs) in SingleStore provide a mechanism to return a result set from a single user-defined SELECT
statement, much like a view.DECIMAL
input type and returns the items in a store’s inventory that match the desired price:
CREATE FUNCTION inventory_with_price(price DECIMAL(19,4))RETURNS TABLE AS RETURNSELECT * FROM store_inventory WHERE item_price = price;
This TVF specifies an input parameter named price
, which is used to query the store_
table and find all items of the specified price.
SELECT * FROM inventory_with_price(1.9900);
Once the inventory_
function has been created, it can be executed with different DECIMAL
input values to query the store_
table in a dynamic, reusable manner.
TVFs can also reference other TVFs in the function body.tvf_
references tvf_
:
CREATE FUNCTION tvf_1(a INT)RETURNS TABLE AS RETURNSELECT * FROM store_inventoryLIMIT a;CREATE FUNCTION tvf_2()RETURNS TABLE AS RETURNSELECT * FROM tvf_1(10)GROUP BY item_priceORDER BY item_priceLIMIT 10;
In the example above, a table named store_
contains a item_
column.tvf_
function is referenced in tvf_
and its logical result set is queried as a table type.
This command causes implicit commits.
Refer to the Permission Matrix for the required permission.
Executing TVFs via the Command Line
Instead of copying/pasting TVFs, you can define a TVF in a .
file and execute it..
file:
% cat userFile.sql
USE db1;
CREATE OR REPLACE FUNCTION tvf_2(num INT)
RETURNS TABLE AS
RETURN SELECT * FROM stock
LIMIT num;
SELECT * FROM tvf_2();
% singlestore < userFile.sql
Important
The command singlestore < userFile.
runs all the commands in the .
file.singlestore-client
package is required to run this command.
Generate a Series of Numbers or Dates
The CREATE FUNCTION(TVF)
command can be used to create a series of numbers or dates.
The following example creates a series of integers starting with 1.
DELIMITER //CREATE FUNCTION SERIES_INT1(a INT DEFAULT 10) RETURNS ARRAY(INT) ASDECLAREnum_arr ARRAY(INT) = CREATE_ARRAY(a);BEGINFOR i IN 1..a LOOPnum_arr[i-1] = i;END LOOP;RETURN num_arr;END //DELIMITER ;
The TABLE
function can be used to convert the array returned from the SERIES_
function into rows of sequential integers.TABLE
function is executed without a parameter, the result will produce 10 rows as the default value for a
is 10, as specified in the CREATE FUNCTION
statement.
SELECT * FROM TABLE(SERIES_INT1());
+-----------+
| table_col |
+-----------+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
+-----------+
SELECT * FROM TABLE(SERIES_INT1(5));
+-----------+
| table_col |
+-----------+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
+-----------+
The following function provides a more customized numeric series.
DELIMITER //CREATE FUNCTION SERIES_INT2(start_value INT, increment_value INT, num_elements INT) RETURNS ARRAY(INT) ASDECLAREnum_arr ARRAY(INT) = CREATE_ARRAY(num_elements);current_value INT := start_value;BEGINFOR i IN 1..num_elements LOOPnum_arr[i-1] = current_value;current_value := current_value + increment_value;END LOOP;RETURN num_arr;END //DELIMITER ;
The following execution of the function TABLE(SERIES_
starts the series at 2, increments by 3, and produces 6 elements total.
SELECT * FROM TABLE(SERIES_INT2(2,3,6));
+-----------+
| table_col |
+-----------+
| 2 |
| 5 |
| 8 |
| 11 |
| 14 |
| 17 |
+-----------+
The following function creates a series of dates with starting dates, ending dates, and a pre-set date interval supplied by the user.
DELIMITER //CREATE OR REPLACE FUNCTION SERIES_DATE1(_start DATETIME, _stop DATETIME, _step VARCHAR(40))RETURNS ARRAY(DATETIME) ASDECLAREarr ARRAY(DATETIME);final_arr ARRAY(DATETIME);l_interval VARCHAR(20);step INT;l_count INT;startdt DATETIME;enddt DATETIME;BEGINIF _step REGEXP 'INTERVAL [0-9]+ (SECOND|MINUTE|HOUR|DAY|WEEK|MONTH|YEAR)' = 1 THENl_interval = SUBSTRING_INDEX(_step, ' ', -1);step = CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(_step, ' ', 2), ' ', -1) AS UNSIGNED);ELSERAISE user_exception('step parameter should be in the form INTERVAL n SECOND|MINUTE|HOUR|DAY|WEEK|MONTH|YEAR..');END IF;arr = CREATE_ARRAY(1000000);l_count = 0;startdt = _start;enddt = _stop;arr[l_count] = startdt;WHILE startdt <= enddt LOOPIF UPPER(l_interval) = 'SECOND' THENstartdt = DATE_ADD(startdt, INTERVAL step SECOND);ELSEIF UPPER(l_interval) = 'MINUTE' THENstartdt = DATE_ADD(startdt, INTERVAL step MINUTE);ELSEIF UPPER(l_interval) = 'HOUR' THENstartdt = DATE_ADD(startdt, INTERVAL step HOUR);ELSEIF UPPER(l_interval) = 'DAY' THENstartdt = DATE_ADD(startdt, INTERVAL step DAY);ELSEIF UPPER(l_interval) = 'WEEK' THENstartdt = DATE_ADD(startdt, INTERVAL step WEEK);ELSEIF UPPER(l_interval) = 'MONTH' THENstartdt = DATE_ADD(startdt, INTERVAL step MONTH);ELSEIF UPPER(l_interval) = 'YEAR' THENstartdt = DATE_ADD(startdt, INTERVAL step YEAR);END IF;l_count = l_count + 1;arr[l_count] = startdt;END LOOP;-- final array to outputfinal_arr = CREATE_ARRAY(l_count+1);FOR i IN 0..l_count LOOPfinal_arr[i] = arr[i];END LOOP;return final_arr;END //DELIMITER ;
The TABLE
function converts the returned array into rows of sequential dates.
SELECT * FROM TABLE(SERIES_DATE1('2024-04-29','2024-06-30','INTERVAL 1 DAY'));
+---------------------+
| table_col |
+---------------------+
| 2024-04-29 00:00:00 |
| 2024-04-30 00:00:00 |
| 2024-05-01 00:00:00 |
| . . . . . . . . . . |
| . . . . . . . . . . |
| 2024-06-29 00:00:00 |
| 2024-06-30 00:00:00 |
| 2024-07-01 00:00:00 |
+---------------------+
64 rows in set (0.03 sec)
SELECT * FROM TABLE(SERIES_DATE1('2024-04-29','2024-06-30','INTERVAL 1 WEEK'));
+---------------------+
| table_col |
+---------------------+
| 2024-04-29 00:00:00 |
| 2024-05-06 00:00:00 |
| 2024-05-13 00:00:00 |
| 2024-05-20 00:00:00 |
| 2024-05-27 00:00:00 |
| 2024-06-03 00:00:00 |
| 2024-06-10 00:00:00 |
| 2024-06-17 00:00:00 |
| 2024-06-24 00:00:00 |
| 2024-07-01 00:00:00 |
+---------------------+
10 rows in set (0.03 sec)
MySQL Client Delimiters
When creating a TVF using a MySQL-compatible client connected to SingleStore, you must change the client delimiter to ensure that the function definition is correctly passed to the server and then set it back to a semicolon after the alternate delimiter is no longer needed.
Security and Permissions
TVFs in SingleStore use the Definer Security Model.SELECT
statement that makes up the TVF’s function body.EXECUTE
permissions on the TVF.EXECUTE
permissions on any TVFs used in the function’s body.
Limitations
TVFs are limited in the following ways:
SELECT Statement Limitations
TVFs may only contain a single SELECT
statement; any other DML or DDL statement in the TVF function body is invalid.SELECT
statements are currently not supported.
Unique Column Names
Each column in a TVF’s logical result set must have a unique name.
Scalar Input Parameters
TVF input parameters may only be scalar value types, such as INT
, VARCHAR
, and so on.ARRAY
or RECORD
cannot be used as input parameter types.
Self-Calling (Recursive Calls)
While a TVF can reference other TVFs in the function body, a TVF may not call itself, directly or indirectly.
Database Scope
Unlike UDFs, TVFs may only be executed within the scope of the database in which they are created.
CREATE FUNCTION cross_database_invalid()RETURNS TABLE AS RETURNSELECT * FROM otherdb.other_tvf();
Related Topics
Last modified: May 1, 2024