What command should I use to make my void function display a paticular table using a variable from the function in the where clause? - postgresql

I basically have a void function that creates a tuple on a existing table. Now at the end of the function I want display the table with the updated tuple. I am running it problems when trying to do this.
This is the statement I am using:
EXECUTE 'SELECT * FROM table WHERE IDNo = idnumber';
-- (idnumber is a variable that is assigned a value in the function)
I get the following error:
ERROR: column "idnumber" does not exist.
Can someone please help me find a solution.

For the actual query, you would want to do something like this:
execute 'select * from table where IDNo = $1' using idnumber;
With the key being the $1 and the USING clause to interpolate the variable.
That should resolve the column error regarding idnumber.
However, I'm not quite sure what you mean by:
display the table with the updated tuple
Do you mean you want to return all the rows in the table including the newly added row? Or just the newly added row? or something else?
Edit in response to comment from OP:
The substitution variables, e.g. $1, $2, $3... are scoped (i.e. unique) to each separate execute statement. So if you had two statements, the first with 3 variables, the second with three, you could use $1, $2, $3 in each and they would refer to the variables mentioned in the USING clause for that individual statement.
See the Postgres Basic Statements doc, specifically the section entitled 40.5.4. Executing Dynamic Commands, for more detail.
Second edit in response to display comments from OP:
When executeing statements, they won't output the way, say, a select statement would if you were doing it within psql or pgadmin. Rather, you have a couple different options, depending on what you ultimately want to do.
First, you could use an INTO clause to put the result into a record (although how you do this depends on whether it's just one row or many rows).
You would need to declare it in that case in the declaration section something like this: foo RECORD;
And then add INTO foo before the USING clause. If it complains about more than one record, you could add LIMIT 1 clause at the end of the query.
You could then do whatever else you wanted to with that record, including RAISE NOTICE with interpolating the record's columns, which would print it to the console.
If you want the entire table, and you want it to "display" more like psql or similar would (that is, return the rows obtained), you would want to have the function return a setof a specific type.
So it may then look something like this:
create function get_table() returns setof table as $$
execute 'select * from table where IDNo = $1' using idnumber;
$$ language 'plpgsql';
Where table is the name of the table you want. If you just want to return the existing rows of the table, this sort of query should work. It would then "display" in a client (e.g. psql, etc.) as the result set.
If you want to modify that (say, by dynamically adding some columns), then you would need to define that new type specifically, and then use that as the type being returned.
See the Postgres Wiki for more details. The wiki content is pretty old (Postgres 7.x vintage), but it generally still applies.

Related

How to pass a Tables Column into a plpgsql Functiion while performing a SELECT... statement

I googled but everyone was asking for how to pass tables or how to use the return result into a Function; I want to do neither. I simply want to take the value of a Column (lets assume col2 below is of the text datatype) of a table, and pass that data into a Function, so I can manipulate the data, but in the SELECT... statement itself, i.e.
SELECT t.col1, "myCustomFunction"(t.col2)
FROM tbl t
WHERE t.col1 = 'someCondition';
CREATE OR REPLACE FUNCTION myCustomFunction(myArg text)
RETURNS text AS $$
DECLARE
BEGIN
RETURN UPPER(myArg);
END
$$ LANGUAGE plpgsql;
... So if myCustomerFunction()'s job was capitalize letters (its not, just an example), the output would be the table with col2 data all capitalized.
Is this possible? I supposed it would be no different than embedding a CASE expression there, which I know works, and a Function returns a result, so I assumed it would be the same, but I am getting SQL Error
You cannot to pass named column to some function and you cannot to return this named column like table with this column. The table is set of rows, and almost all processing in Postgres is based on rows processing. Usually you need to hold only data of one row in memory, so you can process much bigger dataset than is your memory.
Inside PL/pgSQL function you have not informations about outer. You can get just data of scalar types, arrays of scalars, or composite or arrays of composites (or ranges and multiranges - this special kind of composite and array of composite). Nothing else.
Theoretically you can aggregate data in one column to array, and later you can expand this array to table. But these operations are memory expensive and can be slow. You need it only in few cases (like computing of median function), but it is slow, and there is risk of out of memory exception.
When object names are not doubled quoted Postgres processes then internally as lower case. When doubled quoted the are processed exactly as quoted. The thing is these may not be the same. You defined the function as FUNCTION myCustomFunction(myArg text) Not doubled quoted, but attempt to call it via "myCustomFunction"(t.col2). Unfortunately myCustomFunction processed as mycustomfunction but "myCustomFunction" is processed exactly as it appears. Those are NOT the same. Either change your select to:
SELECT t.col1,myCustomFunction(t.col2)
FROM tbl t
WHERE t.col1 = 'someCondition';
or change the function definition to:
CREATE OR REPLACE FUNCTION "myCustomFunction"(myArg text)
RETURNS text AS $$
DECLARE
BEGIN
RETURN UPPER(myArg);
END
$$ LANGUAGE plpgsql;

Returning column value after insert into table and set it into a variable [duplicate]

I have a table. I wrote a function in plpgsql that inserts a row into this table:
INSERT INTO simpleTalbe (name,money) values('momo',1000) ;
This table has serial field called id. I want in the function after I insert the row to know the id that the new row received.
I thought to use:
select nextval('serial');
before the insert, is there a better solution?
Use the RETURNING clause. You need to save the result somewhere inside PL/pgSQL - with an appended INTO ..
INSERT INTO simpleTalbe (name,money) values('momo',1000)
RETURNING id
INTO _my_id_variable;
_my_id_variable must have been declared with a matching data type.
Related:
PostgreSQL next value of the sequences?
Depending on what you plan to do with it, there is often a better solution with pure SQL. Examples:
Combining INSERT statements in a data-modifying CTE with a CASE expression
PostgreSQL multi INSERT...RETURNING with multiple columns
select nextval('serial'); would not do what you want; nextval() actually increments the sequence, and then the INSERT would increment it again. (Also, 'serial' is not the name of the sequence your serial column uses.)
#Erwin's answer (INSERT ... RETURNING) is the best answer, as the syntax was introduced specifically for this situation, but you could also do a
SELECT currval('simpletalbe_id_seq') INTO ...
any time after your INSERT to retrieve the current value of the sequence. (Note the sequence name format tablename_columnname_seq for the automatically-defined sequence backing the serial column.)

Executing queries dynamically in PL/pgSQL

I have found solutions (I think) to the problem I'm about to ask for on Oracle and SQL Server, but can't seem to translate this into a Postgres solution. I am using Postgres 9.3.6.
The idea is to be able to generate "metadata" about the table content for profiling purposes. This can only be done (AFAIK) by having queries run for each column so as to find out, say... min/max/count values and such. In order to automate the procedure, it is preferable to have the queries generated by the DB, then executed.
With an example salesdata table, I'm able to generate a select query for each column, returning the min() value, using the following snippet:
SELECT 'SELECT min('||column_name||') as minval_'||column_name||' from salesdata '
FROM information_schema.columns
WHERE table_name = 'salesdata'
The advantage being that the db will generate the code regardless of the number of columns.
Now there's a myriad places I had in mind for storing these queries, either a variable of some sort, or a table column, the idea being to then have these queries execute.
I thought of storing the generated queries in a variable then executing them using the EXECUTE (or EXECUTE IMMEDIATE) statement which is the approach employed here (see right pane), but Postgres won't let me declare a variable outside a function and I've been scratching my head with how this would fit together, whether that's even the direction to follow, perhaps there's something simpler.
Would you have any pointers, I'm currently trying something like this, inspired by this other question but have no idea whether I'm headed in the right direction:
CREATE OR REPLACE FUNCTION foo()
RETURNS void AS
$$
DECLARE
dyn_sql text;
BEGIN
dyn_sql := SELECT 'SELECT min('||column_name||') from salesdata'
FROM information_schema.columns
WHERE table_name = 'salesdata';
execute dyn_sql
END
$$ LANGUAGE PLPGSQL;
System statistics
Before you roll your own, have a look at the system table pg_statistic or the view pg_stats:
This view allows access only to rows of pg_statistic that correspond
to tables the user has permission to read, and therefore it is safe to
allow public read access to this view.
It might already have some of the statistics you are about to compute. It's populated by ANALYZE, so you might run that for new (or any) tables before checking.
-- ANALYZE tbl; -- optionally, to init / refresh
SELECT * FROM pg_stats
WHERE tablename = 'tbl'
AND schemaname = 'public';
Generic dynamic plpgsql function
You want to return the minimum value for every column in a given table. This is not a trivial task, because a function (like SQL in general) demands to know the return type at creation time - or at least at call time with the help of polymorphic data types.
This function does everything automatically and safely. Works for any table, as long as the aggregate function min() is allowed for every column. But you need to know your way around PL/pgSQL.
CREATE OR REPLACE FUNCTION f_min_of(_tbl anyelement)
RETURNS SETOF anyelement
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE (
SELECT format('SELECT (t::%2$s).* FROM (SELECT min(%1$s) FROM %2$s) t'
, string_agg(quote_ident(attname), '), min(' ORDER BY attnum)
, pg_typeof(_tbl)::text)
FROM pg_attribute
WHERE attrelid = pg_typeof(_tbl)::text::regclass
AND NOT attisdropped -- no dropped (dead) columns
AND attnum > 0 -- no system columns
);
END
$func$;
Call (important!):
SELECT * FROM f_min_of(NULL::tbl); -- tbl being the table name
db<>fiddle here
Old sqlfiddle
You need to understand these concepts:
Dynamic SQL in plpgsql with EXECUTE
Polymorphic types
Row types and table types in Postgres
How to defend against SQL injection
Aggregate functions
System catalogs
Related answer with detailed explanation:
Table name as a PostgreSQL function parameter
Refactor a PL/pgSQL function to return the output of various SELECT queries
Postgres data type cast
How to set value of composite variable field using dynamic SQL
How to check if a table exists in a given schema
Select columns with particular column names in PostgreSQL
Generate series of dates - using date type as input
Special difficulty with type mismatch
I am taking advantage of Postgres defining a row type for every existing table. Using the concept of polymorphic types I am able to create one function that works for any table.
However, some aggregate functions return related but different data types as compared to the underlying column. For instance, min(varchar_column) returns text, which is bit-compatible, but not exactly the same data type. PL/pgSQL functions have a weak spot here and insist on data types exactly as declared in the RETURNS clause. No attempt to cast, not even implicit casts, not to speak of assignment casts.
That should be improved. Tested with Postgres 9.3. Did not retest with 9.4, but I am pretty sure, nothing has changed in this area.
That's where this construct comes in as workaround:
SELECT (t::tbl).* FROM (SELECT ... FROM tbl) t;
By casting the whole row to the row type of the underlying table explicitly we force assignment casts to get original data types for every column.
This might fail for some aggregate function. sum() returns numeric for a sum(bigint_column) to accommodate for a sum overflowing the base data type. Casting back to bigint might fail ...
#Erwin Brandstetter, Many thanks for the extensive answer. pg_stats does indeed provide a few things, but what I really need to draw a complete profile is a variety of things, min, max values, counts, count of nulls, mean etc... so a bunch of queries have to be ran for each columns, some with GROUP BY and such.
Also, thanks for highlighting the importance of data types, i was sort of expecting this to throw a spanner in the works at some point, my main concern was with how to automate the query generation, and its execution, this last bit being my main concern.
I have tried the function you provide (I probably will need to start learning some plpgsql) but get a error at the SELECT (t::tbl) :
ERROR: type "tbl" does not exist
btw, what is the (t::abc) notation referred as, in python this would be a list slice, but it’s probably not the case in PLPGSQL

How to cut seconds from an interval column?

In my table results from column work_time (interval type) display as 200:00:00. Is it possible to cut the seconds part, so it will be displayed as 200:00? Or, even better: 200h00min (I've seen it accepts h unit in insert so why not load it like this?).
Preferably, by altering work_time column, not by changing the select query.
This is not something you should do by altering a column but by changing the select query in some way. If you change the column you are changing storage and functional uses, and that's not good. To change it on output, you need to modify how it is retrieved.
You have two basic options. The first is to modify your select queries directly, using to_char(myintervalcol, 'HH24:MI')
However if your issue is that you have a common format you want to have universal access to in your select query, PostgreSQL has a neat trick I call "table methods." You can attach a function to a table in such a way that you can call it in a similar (but not quite identical) syntax to a new column. In this case you would do something like:
CREATE OR REPLACE FUNCTION myinterval_nosecs(mytable) RETURNS text LANGUAGE SQL
IMMUTABLE AS
$$
SELECT to_char($1.myintervalcol, 'HH24:MI');
$$;
This works on the row input, not on the underlying table. As it always returns the same information for the same input, you can mark it immutable and even index the output (meaning it can be run at plan time and indexed used).
To call this, you'd do something like:
SELECT myinterval_nosecs(m) FROM mytable m;
But you can then use the special syntax above to rewrite that as:
SELECT m.myinterval_nosecs FROM mytable m;
Note that since myinterval_nosecs is a function you cannot omit the m. at the beginning. This is because the query planner will rewrite the query in the former syntax and will not guess as to which relation you mean to run it against.

SELECT FROM a function returning a record with arbirary number of columns

I'm using PostgreSQL database.
I've a my plpgsql FUNCTION that returns a single record with an arbitrary number of columns.
Due to this arbitrariness I would need to use something like:
SELECT * FROM my_function(97)
But this doesn't work as Postgres gives me the following error:
a column definition list is required for functions returning "record"
But if I do:
SELECT my_function(97)
I can see the expected result but encapsulated in a single column.
Is there a way to fetch the expected result as a set of columns as intended by the function and not a single column encapsulating all of them?
When a function just RETURNS record or SETOF record (and no OUT parameters to go with it), PostgreSQL does not know the names and types of its elements and you are required to provide a column definition list with every call.
Avoid that if at all possible and return a well known (row) type instead. There are a several ways to declare the return type. See:
PostgreSQL: ERROR: 42601: a column definition list is required for functions returning "record"
Refactor a PL/pgSQL function to return the output of various SELECT queries
There are quite a few related questions on SO. Try a search!
When using a set returning function (setof) in the select list, on the left hand side of the FROM, the function returns a composite type. Using a function in the select list can be hard to avoid when using a table as input to a function.
A way to SELECT items from a single column of composite type follows:
SELECT
(my_function).field1,
(my_function).field2,
(my_function).field3
FROM
(SELECT my_function(*)
FROM sometable) t
You have a few options here:
Return a REFCURSOR and fetch from that cursor in the application. Note you can actually return multiple REFCURSORS if you need to return multiple result sets.
Return an XML document and parse it in the application.
Use a bunch of OUT variables, return RECORD, and determine which of these to select from
The basic problem is that the actual return results need to be known at planning time so you can't just return an arbitrary number of columns. The planner needs to know what is going to be returned.
In order to return a "set of columns" you will have define a return type as TABLE or SETOF in which case you actually return a SET of records which you should be able to SELECT from.
For more information about functions returning SETOF take a look at this link to documentation
I'm not certain that I follow what you're after, but does this work?
SELECT (my_function(97)).my_column