Reverse a column in postgres - sql

So I have a table in SQL server that is defined as such
create table test(value varchar(200), Reverse(value) as valueReverse);
now when I insert something in this table lets say I insert the string hello, it will store the value in the table as such.
value | valueReverse
--------------------
hello | olleh
I am trying to convert the table into PostgreSQL however the reverse() function is not working and it's giving me errors. What is the correct way to create this table in postgres?

For PostgreSQL 12 and above
If you are using Postgres 12 or higher then you can use GENERATED ALWAYS AS for the column valueReverse like below: Manual
create table test(value varchar(200),
valueReverse varchar(200) generated always as (reverse(value)) STORED );
DEMO
For PostgreSQL 11 or below
For earlier version you can use Triggers like below.
Creating Trigger Function
create or replace function trig_reverse() returns trigger as
$$
begin
new.valueReverse=reverse(new.value);
return new;
end;
$$
language plpgsql
Creating Trigger
create trigger trig_rev
before insert or update on test
for each row
execute procedure trig_reverse();
DEMO

Do not store string twice (redundantly). It will be much cleaner and cheaper overall to store it once and produce the reverted copy on the fly. You can use a VIEW if you need a drop-in replacement for your table:
CREATE TABLE base_test(value varchar(200));
INSERT INTO base_test VALUES ('hello');
CREATE VIEW test AS
SELECT *, reverse(value) AS value_reverse
FROM base_test;
db<>fiddle here
Related:
Computed / calculated / virtual / derived columns in PostgreSQL

Related

SQL - omit repeating the table name

Let's say I want to create a new table from an existing table in SQL (postgres). I want the new table to have the same name as the old table but I want it to be in a different schema.
Is there a way to do this without having to repeat the name of the two tables (who share one name?)
Let's say the name of the original table is public.student
CREATE TABLE student(
student_id INT PRIMARY KEY,
last_name VARCHAR(30),
major VARCHAR(30))
Now I want to have the exact table but I want it to be in test.student
I know I would "clone" that table via
CREATE TABLE test.student AS
SELECT *
FROM public.student;
but I would like to write this without having to repeat writing "student".
Is there a way to write a function for this?
I'm quite new to SQL, so I'm thankful for any help! I looked into functions and I wasn't able to make it work.
You could create a procedure (or a function) with dynamic SQL:
CREATE OR REPLACE PROCEDURE foo(_schema text, _table text)
LANGUAGE plpgsql AS
$func$
BEGIN
EXECUTE format('CREATE TABLE %1$I.%2$I AS TABLE public.%2$I'
, _schema, _table);
END
$func$;
Call:
CALL foo('test', 'student');
Note that identifers are case sensitive here!
Be wary of possible SQL injection. format() with the format specifier %I (for identifier) is safe. (nested $1, $2 are ordinal references to format input)
See:
Define table and column names as arguments in a plpgsql function?
Table name as a PostgreSQL function parameter

Create Trigger in Postgres asking for avg()

I'm trying to create a Trigger that adds the average of one column from table A into table B after each insert into table A.
Table A (id (integer), rating (integer))
Table B (id (serial), average (integer))
My Trigger is:
CREATE TRIGGER rat_trig
AFTER INSERT ON a
FOR EACH ROW EXECUTE PROCEDURE upd_rat_func();
My Trigger Function looks like this:
CREATE OR REPLACE FUNCTION upd_rat_func() RETURNS TRIGGER as $rating_update$
BEGIN
INSERT INTO b VALUES (DEFAULT, AVG(rating) FROM a);
RETURN NEW;
END;
$rating_update$ language plpgsql;
Postgres tells me there is an error in my function somewhere near or at 'from', yet I cannot find a way to make it work (The Trigger is not yet created, as I cannot create the function). Any help (or also any advice of where I can find more about the structure of trigger functions as the postgres documentation has been less than helpful) are very welcome.
The FROM has to belong to a SELECT (or a DELETE).
To use a subquery, use the complete query and put it into parentheses:
INSERT INTO b VALUES (DEFAULT, (SELECT AVG(rating) FROM a));
But that trigger does not make much sense to me. You want a new row with a new average for each INSERT? What about the other rows in b?
Perhaps you are looking for a materialized view.
Here is PostgreSQL doc:
https://www.postgresql.org/docs/12/plpgsql-trigger.html
try changing INSERT statement for smth like this:
INSERT INTO b (average) SELECT AVG(rating) FROM a;
make sure it works before adding it to trigger function

PL/SQL Oracle - Selecting from internal table

EDITED
I have a problem with performing some PL/SQL code.
I have real table a. I want to take only elements with range<=100. I make a collection inside my PL/SQL based on that table. Then I want to perform SELECT operation on that collection. But I got a problem with it.
Prepared table (this is all for example, it's not a real problem. I just would like to know how can I select from collection in PL/SQL code block).
CREATE TABLE a (amount NUMBER);
INSERT INTO a VALUES (50);
INSERT INTO a VALUES (100);
INSERT INTO a VALUES (200);
And then I got this block:
DECLARE
TYPE aTable IS TABLE OF a%ROWTYPE;
aActual aTable;
temp NUMBER;
BEGIN
SELECT * BULK COLLECT INTO aActual
FROM a WHERE amount<=100;
SELECT SUM(amount) INTO temp FROM TABLE(aActual);
DBMS_OUTPUT.PUT_LINE(temp);
END;
But I got eroor PLS-00642 and ORA-22905.
What can I do? Why it doesn't work that way?
I'm on Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production version (according to SELECT * FROM V$VERSION;)
You can't do it because aTable is not a database table. (Yes I know it's defined with table of but that doesn't define a table. One of those things.)
To ask SQL to treat a collection as a database table you would use the table() construction:
select sum(amount) into temp from table(aActual);
although this will fail in your example due to scoping issues and you'll get the self-explanatory
PLS-00642: local collection types not allowed in SQL statements
For it to work, you'd need a schema-level type i.e. one created with create type:
create or replace type xyz as object (a integer, b varchar2(3), c date);
create or replace type xyz_tt as table of xyz;
Now type xyz_tt is in effect published to SQL and it can be used in SQL table() expressions.
As WilliamRobertson showed, you can't use a PL/SQL collection in a SQL query. You can loop over the collection and add each amount to your temp variable, initialising it to zero first:
temp := 0;
for i in 1..aActual.count loop
temp := temp + aActual(i).amount;
end loop;
DBMS_OUTPUT.PUT_LINE(temp);

Generic Postgres 9.5 trigger to convert an UPDATE into modified INSERT

Is it possible to create a generic (not table-specific) trigger in Postgres 9.5 that would perform on instead of update that converts the update into an insert?
Basically what I want to do is (pseudocode):
sql
instead of UPDATE on TG_TABLE_NAME INSERT on TG_TABLE_NAME
I know I can create a very table-specific trigger that maps each value into an insert statement. What I'm trying to do is get away from creating this trigger on every single table.
It is a bit of an oddball idea (nothing personal), but how about this:
CREATE FUNCTION not_update_but_insert() RETURNS trigger AS $$
BEGIN
INSERT INTO TG_TABLE_NAME -- Do an INSERT...
SELECT NEW.*; -- ... using the values from the row to be updated
RETURN NULL; -- Fail the UPDATE
END;
$$ LANGUAGE plpgsql;
Obviously this would not work for any table that has a PRIMARY KEY or other UNIQUE constraints. You do have to CREATE TRIGGER x BEFORE UPDATE for every table this would apply to, so analyze your table structure before creating the trigger.
There is obviously a work-around - at least for the PKs based on a sequence - by examining the information_schema for "safe" columns in TG_TABLE_NAME and then assembling their names into strings to splice into the INSERT statement (column list of main statement and select list). Just leave the columns with sequences or appropriate default values out. This, however, does not address UNIQUE constraints that have no obvious replacement (like a user name or an email address).

SQL postgresql insert statement

I have a following question, for example I have a following table:
CREATE TABLE "regions" (gid serial PRIMARY KEY,
"__gid" int8,
"name" varchar(20),
"language" varchar(7),
"population" int8);
And I want to insert some records, say one of the values for "name" is - 'B', what sort of code would I have to write to change 'B' to 'English-Speaking'? Is that done with some sort of trigger? So would I have to write a trigger to change the values automatically on insert? Any help greatly appriciated!!!
It's an UPDATE statement which will do what you wish, in this case:
UPDATE regions set name = 'English-Speaking' where name = 'B';
To put this in a function use something like:
CREATE OR REPLACE FUNCTION insert_into_wgs()
RETURNS void AS
$$
BEGIN
UPDATE regions SET name = 'English-Speaking' WHERE name = 'B';
END
$$
LANGUAGE 'pgpsql';
Then you create a trigger to run this function:
CREATE TRIGGER log_update
AFTER UPDATE ON accounts
FOR EACH ROW
WHEN (OLD.* IS DISTINCT FROM NEW.*)
EXECUTE PROCEDURE
insert_into_wgs();
Assuming I've guessed what you mean correctly from your description:
You will need a simple BEFORE INSERT OR UPDATE ... FOR EACH ROW EXECUTE trigger to invoke a PL/PgSQL trigger procedure that changes the value of the NEW record and then does a RETURN NEW.
The documentation contains abundant details, and since this is homework I'm not going to provide a complete example. Start with CREATE TRIGGER and PL/pgSQL trigger procedures.