I am interested in learning how to use dynamic functions in Postgres. I basically need a dynamic function that will spit out a table or a subtable (a few chosen columns from an existing table).
I've created the following eval() function, but it is not working as I'd like it to.
I would like my function to return the result of a query that is introduced as a string within the function. The function currently returns only the first value from the first column (enrich_d_dkj_p_k27ac). It seems like I should change my returns from text to something else?
create or replace function eval() returns text
as
$body$
declare
result text;
begin
execute 'select enrich_d_dkj_p_k27ac,enrich_lr_dkj_p_k27ac,enrich_r_dkj_p_k27ac
from dkj_p_k27ac' into result;
return result;
end;
$body$
language plpgsql;
SELECT eval();
Result is:
eval text
2.4
But this is basically only the very first value of the first column - I will need the entire table to appear - or in other words - the result of the indicated SELECT.
It would work like this (but it's useless):
create or replace function eval()
RETURNS TABLE (enrich_d_dkj_p_k27ac text -- replace with actual column types
, enrich_lr_dkj_p_k27ac text
, enrich_r_dkj_p_k27ac text) AS
$func$
begin
RETURN QUERY EXECUTE
'select enrich_d_dkj_p_k27ac,enrich_lr_dkj_p_k27ac,enrich_r_dkj_p_k27ac
from dkj_p_k27ac';
end
$func$ language plpgsql;
Call:
SELECT * FROM eval();
You do not need the eval() function, just execute the statement directly:
select enrich_d_dkj_p_k27ac,enrich_lr_dkj_p_k27ac,enrich_r_dkj_p_k27ac
from dkj_p_k27ac;
The dynamic call cannot solve your underlying problem:
How to execute a string result of a stored procedure in postgres
Related
Hi all I started learning functions in MySQL and I am trying different things. I created this function below, that should return the count of different departments/titles
however I am getting an error on the group by part when I call the select statement
DELIMITER //
CREATE function num_of_titles(titles_ varchar(80)) returns char deterministic
return count(titles_);
// DELIMITER ;
select title, num_of_titles(title) as count_title from titles
group by 1;
enter image description here
The
return count(titles_);
is not valid SQL. COUNT-function cannot be used alone, only as part of a proper SQL query.
Consider the following example function:
CREATE FUNCTION test()
RETURNS INTEGER
LANGUAGE SQL
AS $$ SELECT * FROM UNNEST(ARRAY[1,2,3,4,5]); $$
;
When we execute it like so:
SELECT test();
the result we get back is 1. In other words, the function just returned the first element of the result set.
I have a convenience function that retrieves an ID based on data in the rest of the row. It lets callers query on data that is often unique to a particular row but is not strictly unique. Primarily, it will be used to populate some mostly static data. Allowing the call to use the row data instead of hard coding the ID makes it more readable and easier to maintain when new data does come along. This depends on the caller having some familiarity with the data, but that's a reasonable assumption to make for the usage at hand.
The problem is if the user makes a mistake and gives arguments that are insufficient to filter the query's results to one row, I want my function to error out instead of returning the first result. How can I accomplish this? Do I have any options other than to switch my language to PL/PGSQL and check it manually?
(Using PostgreSQL 9.3).
You could put the entire query as a subquery in the SELECT list. When attempting to return more than one row, this will fail with this error:
ERROR: more than one row returned by a subquery used as an expression
Based on your example, this one would fail:
CREATE FUNCTION test()
RETURNS INTEGER
LANGUAGE SQL
AS $$ SELECT (SELECT * FROM UNNEST(ARRAY[1,2,3,4,5])); $$
and this one wouldn't fail:
CREATE FUNCTION test()
RETURNS INTEGER
LANGUAGE SQL
AS $$ SELECT (SELECT * FROM UNNEST(ARRAY[1])); $$
I am working on a project in VS2012 while using MySQL database and Entity Framework 5.0.0.
When I try to create a new Complex type from a stored procedure, I get an error when clicking "Get Column Information" button:
The selected stored procedure or function returns no columns.
My stored procedure code is as following:
DROP PROCEDURE IF EXISTS SearchAlgemeenSet;
Delimiter //
CREATE PROCEDURE SearchAlgemeenSet(IN in_searchQuery VARCHAR(255))
BEGIN
SELECT Blokken, Jaargang, Werk_Uren
FROM algemeensets
WHERE Blokken LIKE in_searchQuery
OR Jaargang LIKE in_searchQuery
OR Werk_Uren LIKE in_searchQuery;
END
//
Delimiter ;
I'm positive that it returns columns if the in_searchQuery parameter has a match.
In my research, I have found plenty solutions for Microsoft SQL database. But none of those solutions apply to MySQL database.
How to solve this?
I'm positive that it returns columns, if the in_searchQuery
parameter has a match.
Because you are not using any wild card for partial search, unless it finds an exact match, for in_searchQuery value, no rows will be returned. For partial match to happen, you need to use wild card symbol '%' with the in_searchQuery value.
Modified procedure, should be like the following:
DROP PROCEDURE IF EXISTS SearchAlgemeenSet;
Delimiter //
CREATE PROCEDURE SearchAlgemeenSet(IN in_searchQuery VARCHAR(255))
BEGIN
set #search_criteria := concat( '%', in_searchQuery, '%' );
SELECT Blokken, Jaargang, Werk_Uren
FROM algemeensets
WHERE Blokken LIKE #search_criteria
OR Jaargang LIKE #search_criteria
OR Werk_Uren LIKE #search_criteria;
END;
//
Delimiter ;
When no search criteria has any matches found, and empty set would be returned.
In your scripting language, you have to check, in advance, whether the procedure has returned any results or not. Based on that, you can show a message that no data found or you can perform other actions.
I am attempting to write a function in PostgreSQL 9.0. This will eventually be used in a new aggregate function, but one step at a time.
Here is what I have so far:
create or replace function encstate(text,text) returns text as $$
DECLARE
oldstate alias for $1;
arg alias for $2;
BEGIN
IF length(oldstate)>0 then
select 'Encrypted';
else if
select '';
end if;
END;
$$ language sql strict immutable;
(I know I'm not yet using the $2 argument.)
The result is:
ERROR: syntax error at or near "alias"
LINE 3: oldstate alias for $1;
When I remove the DECLARE block and just refer to the arguments as $1 etc in the body, the result is:
ERROR: syntax error at or near "if"
LINE 3: if length($1)>0 then
As far as I can tell, what I have matches examples found on the web, except I could find no examples of functions with an if-statement, so I have no idea what I'm doing wrong. Any help would be appreciated.
I would suggest doing this as an SQL function:
create or replace function encstate(text,text) returns text as $$
SELECT CASE WHEN length($1)>0 then 'Encrypted' ELSE '' END;
$$ language sql strict immutable;
You could also do what you did with the other, but change sql to plpgsql. My suggestion though is that what you can do in an SQL function you should do in one usually. You will get better performance and the planner can do more with it.
If you want a SQL function:
create or replace function encstate(text, text) returns text as $$
select case
when length($1) > 0 then 'Encrypted'
else ''
end
;
$$ language sql strict immutable;
SQL has no variables or control structures as it is not procedural, it is declarative. If you want procedural features then use a plpgsql function:
create or replace function encstate(text, text) returns text as $$
DECLARE
oldstate alias for $1;
arg alias for $2;
BEGIN
IF length(oldstate) > 0 then
return 'Encrypted';
else
return '';
end if;
END;
$$ language plpgsql strict immutable;
SQL
CREATE OR REPLACE FUNCTION encstate(oldstate text, arg text)
RETURNS text LANGUAGE SQL IMMUTABLE AS
$func$
SELECT CASE WHEN $1 <> '' THEN 'Encrypted' ELSE '' END
$func$
PL/pgSQL
CREATE OR REPLACE FUNCTION encstate(oldstate text, arg text)
RETURNS text LANGUAGE plpgsql IMMUTABLE AS
$func$
BEGIN
IF oldstat <> '' THEN
RETURN 'Encrypted';
ELSE
RETURN '';
END IF;
END
$func$;
Major points
The expression length(x) > 0 (x being text) only excludes '' and NULL.
Use the 100 % equivalent expression x <> ''. Does the same simpler and faster, regardless of whether the function is declared STRICT or not.
Don't use plpgsql ALIAS if you don't have to. It's only there for compatibility and to rename pre-determined parameter names. The manual actively discourages its use for other purposes. I never use it. Named parameters are available since version 8.1. Simpler, better.
In SQL functions you can refer to parameter names (instead of positional parameters ($1, $2, ..) since PostgreSQL 9.2. It's still a good idea to name parameters even before that, for documentation.
I suspect you do not want to declare this function STRICT (synonym: RETURNS NULL ON NULL INPUT). Like the synonym implies, that returns NULL on (any) NULL input. Seems like you want an empty string ('') instead.
There is also a performance implication:
Function executes faster without STRICT modifier?
I am writing an application that supports custom fields. Currently I store all custom fields in a XML formated text field ( e.g. '<root><field1>val1</field1><field2>val2</field2></root>' in cust_field)
I am able to to use updateXML(cust_field, '/root/field1', '<field1>new value</field1') to update those values, however if I use updateXML(cust_field, '/root/field3', '<field3>new value</field3>') then it does not work since field3 is not in the old value. Is there a way to let MySQL automatically insert the new field3 node and its value into cust_field? I am thinking about stored procedure or even stored function but not familiar with both, can anyone point me to the right direction?
The MySQL XML Functions will not do this automatically.
You can create a stored function to call UpdateXML() if the element is present, otherwise to add the element using your own logic.
Here's a basic template to get you started:
DELIMITER $$
CREATE FUNCTION update_xml(xml_target text, xpath_expr text, new_xml text) returns text
BEGIN
DECLARE return_val text;
IF (ExtractValue(xml_target,xpath_expr) != '')
THEN
RETURN updateXML(xml_target,xpath_expr,new_xml);
ELSE
SET return_val := xml_target;
-- add code here to insert the new element into your XML string
RETURN return_val;
END IF;
END $$
I actually ended up putting the logic in my app sql engine.