I need to create function returning schedule of trains in different stations.
I have something like that:
create or replace function f124(st varchar) returns table(odjazdy varchar, kierunek varchar, przewoznik varchar, peron varchar, tor int)
as
$$ declare s varchar; sta varchar:=st;
begin
drop table if exists f124p;
create table f124p(odjazdy varchar, kierunek varchar, przewoznik varchar, peron varchar, tor int);
select string_agg('insert into f124p select k."'||nr||'", ''stacja'', ''przewoznik'', t.peron, t.tor from gdymal_ic_kursy k inner join gdymal_ic_trasa t on k.stacja=t.stacja where t.stacja=''sta'';','') into s from generate_series(5110, 5118,2) as nr;
execute s;
return query select * from f124p;
end $$ language plpgsql;
The problem is that this function returns empty table. It shouldn't be like that beacuse there are information which must be in this table. I think there is a problem near k."'||nr||'"
The insert should bring data from columns named "5110", "5112" as varchars. (In gdymal_ic_kursy there are columns "5110", "5112", ..., "5118"). Maybe this is a problem. Maybe You have any tips how should I repair this function?
I don't know what it means, but it works here:
\i tmp.sql
CREATE TABLE gdymal_ic_kursy
( stacja text
, "5110" text
, "5111" text
, "5112" text
, "5113" text
, "5114" text
, "5115" text
, "5116" text
, "5117" text
, "5118" text
);
INSERT INTO gdymal_ic_kursy VALUES ('sta', '1', '2', '3', '4', '5', '6', '7', '8');
CREATE TABLE gdymal_ic_trasa
( stacja text
, peron text
, tor integer
);
INSERT INTO gdymal_ic_trasa(stacja,peron,tor) VALUES ('sta', 'one', 666 );
-- ------------------------------
create or replace function f124(st varchar)
returns table(odjazdy varchar, kierunek varchar, przewoznik varchar, peron varchar, tor int)
as
$func$
declare s varchar; sta varchar := st;
begin
drop table if exists f124p;
create table f124p(odjazdy varchar, kierunek varchar, przewoznik varchar, peron varchar, tor int);
select string_agg('insert into f124p select k."'||nr||'", ''stacja'', ''przewoznik'', t.peron, t.tor
from gdymal_ic_kursy k
inner join gdymal_ic_trasa t on k.stacja=t.stacja
where t.stacja=''sta'';','')
into s from generate_series(5110, 5118,2) as nr;
execute s;
return query select * from f124p;
end $func$ language plpgsql;
select *
FROM f124('OMG')
;
Result:
DROP SCHEMA
CREATE SCHEMA
SET
CREATE TABLE
INSERT 0 1
CREATE TABLE
INSERT 0 1
CREATE FUNCTION
NOTICE: table "f124p" does not exist, skipping
odjazdy | kierunek | przewoznik | peron | tor
---------+----------+------------+-------+-----
1 | stacja | przewoznik | one | 666
3 | stacja | przewoznik | one | 666
5 | stacja | przewoznik | one | 666
7 | stacja | przewoznik | one | 666
| stacja | przewoznik | one | 666
(5 rows)
Related
I want to write function to get data from two different tables
My code:
create function
return table(a integer,b integer,c integer,k integer,l integer,m integer);
if(x=1) then
select a,b,c from mst_1
else
select k,l,m from mst_2
end IF;
end;
the problem is that two tables posses different columns, I'm getting error.
I replicated a case similar to yours, and it's just a matter of using the correct sintax.
If you have two tables like test and test_other like in my case
create table test (id serial, name varchar, surname varchar);
insert into test values(1,'Carlo', 'Rossi');
insert into test values(2,'Giovanni', 'Galli');
create table test_other (id_other serial, name_other varchar, surname_other varchar);
insert into test_other values(1,'Beppe', 'Bianchi');
insert into test_other values(2,'Salmo', 'Verdi');
you now want a function that returns the 3 columns from test if an input parameter is 1, the 3 columns from test_other otherwise.
Your function will look like the following
create or replace function case_return(x integer)
returns table(id integer,value_1 varchar, value_2 varchar)
language plpgsql
as
$$
begin
if(x=1) then
return query select test.id,test.name,test.surname from test;
else
return query select test_other.id_other, test_other.name_other, test_other.surname_other from test_other;
end IF;
end;
$$
;
The function always returns the columns id, value_1 and value_2 as per definition even if your source columns are different
defaultdb=> select * from case_return(0); id | value_1 | value_2
----+---------+---------
1 | Beppe | Bianchi
2 | Salmo | Verdi
(2 rows)
defaultdb=> select * from case_return(1); id | value_1 | value_2
----+----------+---------
1 | Carlo | Rossi
2 | Giovanni | Galli
(2 rows)
I'm looking to input a list of integers into a function and return rows based on this. In this case, I want to select some attributes based on employer id, e.g. select where employer id = 102,103 and 105.
This is what I have tried.
CREATE OR REPLACE FUNCTION dummy(VARIADIC e_id NUMERIC[])
RETURNS TABLE (
emp_id INT,
first_name VARCHAR,
last_name VARCHAR,
salary INT
)
AS $$
BEGIN
RETURN QUERY SELECT em.emp_id, em.first_name, em.last_name, em.salary
FROM employee em
WHERE em.emp_id IN e_id;
END; $$
LANGUAGE PLPGSQL;
SELECT *
FROM dummy(102, 103, 105);
The error appears to be from the line containing WHERE. How am I able to select based on the list of integers e_id?
WHERE em.emp_id IN e_id
Since you are dealing with a list of numbers, you could just use the any construct instead of in:
CREATE OR REPLACE FUNCTION dummy(VARIADIC e_id NUMERIC[])
RETURNS TABLE (
emp_id INT,
first_name VARCHAR,
last_name VARCHAR,
salary INT
)
AS $$
BEGIN
RETURN QUERY SELECT em.emp_id, em.first_name, em.last_name, em.salary
FROM employee em
WHERE em.emp_id = any (e_id);
END; $$
LANGUAGE PLPGSQL;
Demo on DB Fiddle
create table employee(emp_id int, first_name varchar(50), last_name varchar(50), salary int);
insert into employee values
(1, 'a', 'b', 50),
(2, 'c', 'd', 100),
(3, 'e', 'f', 400);
CREATE OR REPLACE FUNCTION dummy(VARIADIC e_id NUMERIC[])
RETURNS TABLE (
emp_id INT,
first_name VARCHAR,
last_name VARCHAR,
salary INT
)
AS $$
BEGIN
RETURN QUERY SELECT em.emp_id, em.first_name, em.last_name, em.salary
FROM employee em
WHERE em.emp_id = any (e_id);
END; $$
LANGUAGE PLPGSQL;
SELECT * FROM dummy(1, 2);
emp_id | first_name | last_name | salary
-----: | :--------- | :-------- | -----:
1 | a | b | 50
2 | c | d | 100
For my application, I created a SQL file patch to add a column user_name to an existant table :
CREATE OR REPLACE FUNCTION add_col(
_tbl VARCHAR, -- Table
_col VARCHAR, -- Column to add
_type regtype -- Type of that column
) RETURNS BOOL AS $$
BEGIN
-- Returns true if column has been added; false otherwise.
IF EXISTS (SELECT DISTINCT column_name
FROM information_schema.columns
WHERE table_schema LIKE current_schema and table_name LIKE _tbl and column_name LIKE _col
)
THEN
-- Column already exists in that table of that schema: do nothing.
RETURN false;
END IF;
-- Add column
EXECUTE 'ALTER TABLE ' || _tbl || ' ADD COLUMN ' || _col || ' ' || _type;
RETURN true;
END; $$ language 'plpgsql';
SELECT add_col('ack_event', 'user_name', 'VARCHAR(30)');
If I execute that and do a SELECT to display ack_event content, we see that the column user_name is added but with the "character varying" type:
| id | user_name |
| bigint | character varying |
|--------+-------------------+
| | |
However if the ack_event column is created directly with the user_name column, the type of user_name is "character_varying(30)" :
CREATE TABLE ACK_EVENT(
ID int8 not null,
USER_NAME VARCHAR(30),
CONSTRAINT PK_ACK_EVENT PRIMARY KEY (ID)
);
Result:
| id | user_name |
| bigint | character varying(30) |
|--------+-----------------------+
| | |
Why is there this inconsistency ? Is there a way to correct it and have character varying (30) in both cases ?
This may introduce some validation issues, but if you change the datatype of _type from regtype to a text datatype, I think it will input your DDL much more literally:
CREATE OR REPLACE FUNCTION add_col(
_tbl VARCHAR,
_col VARCHAR,
_type text -- this was previously regtype
) RETURNS varchar AS $$
I want to create a function that will update a column of type varchar to a preferred string that is referenced in the column of another table to help me clean this column more iteratively.
CREATE TABLE big_table (
mn_uid NUMERIC PRIMARY KEY,
user_name VARCHAR
);
INSERT INTO big_table VALUES
(1, 'DAVE'),
(2, 'Dave'),
(3, 'david'),
(4, 'Jak'),
(5, 'jack'),
(6, 'Jack'),
(7, 'Grant');
CREATE TABLE nameKey_table (
nk_uid NUMERIC PRIMARY KEY,
correct VARCHAR,
wrong VARCHAR
);
INSERT INTO nameKey_table VALUES
(1, 'David', 'Dave_DAVE_dave_DAVID_david'),
(2, 'Jack', 'JACK_jack_Jak_jak');
I want to perform the following procedure:
UPDATE big_table
SET user_name = (SELECT correct
FROM nameKey_table
WHERE wrong
LIKE '%DAVE%')
WHERE user_name = 'DAVE';
but looped over each user_name in big_table so that I have a function that can do something like this:
UPDATE big_table SET user_name = corrected_name_fn();
Here is my attempt to do something like this but I can't seem to get it to work:
CREATE FUNCTION corrected_name_fn() RETURNS VARCHAR AS $$
DECLARE entry RECORD;
DECLARE correct_name VARCHAR;
BEGIN
FOR entry IN SELECT DISTINCT user_name FROM big_table LOOP
EXECUTE 'SELECT correct
FROM nameKey_table
WHERE wrong
LIKE ''%$1%'''
INTO correct_name
USING entry;
RETURN correct_name;
END LOOP;
END;
$$ LANGUAGE plpgsql;
I want the final output in big_table to be:
| mn_uid | user_name |
| 1 | 'David' |
| 2 | 'David' |
| 3 | 'David' |
| 4 | 'Jack' |
| 5 | 'Jack' |
| 6 | 'Jack' |
| 7 | 'Grant' |
I realize rows 6 and 7 provide two unique cases that I want to build into the function with IF ELSE statements.
If user_name is in nameKey_table.correct, go to next
If user_name is not in nameKey_table.correct or does not match a string in nameKey_table.wrong, leave as is.
Thanks for any help on this!!
It sounds like you want a trigger on the table. Here is my suggestion:
CREATE OR REPLACE FUNCTION tf_fix_name() RETURNS TRIGGER AS
$$
DECLARE
corrected_name TEXT;
BEGIN
SELECT correct INTO corrected_name FROM nameKey_table WHERE expression ~* NEW.user_name;
IF FOUND THEN
NEW.user_name := corrected_name;
END IF;
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
CREATE TEMP TABLE big_table (
mn_uid INT PRIMARY KEY,
user_name TEXT NOT NULL
);
CREATE TRIGGER trigger_fix_name
BEFORE INSERT
ON big_table
FOR EACH ROW
EXECUTE PROCEDURE tf_fix_name();
CREATE TEMP TABLE nameKey_table (
nk_uid INT PRIMARY KEY,
correct TEXT NOT NULL,
expression TEXT NOT NULL
);
INSERT INTO nameKey_table VALUES
(1, 'David', '(dave|david)'),
(2, 'Jack', '(jack|jak)');
INSERT INTO big_table VALUES
(1, 'DAVE'),
(2, 'Dave'),
(3, 'david'),
(4, 'Jak'),
(5, 'jack'),
(6, 'Jack'),
(7, 'Grant');
SELECT * FROM big_table;
+--------+-----------+
| mn_uid | user_name |
+--------+-----------+
| 1 | David |
| 2 | David |
| 3 | David |
| 4 | Jack |
| 5 | Jack |
| 6 | Jack |
| 7 | Grant |
+--------+-----------+
(7 rows)
Note: I think you can do what you want a lot easier with a case insensitive regular expression. And I also changed your primary keys to INTs. Not sure why they are numerics, but it doesn't really change the solutions. My solution was developed and tested on PostgreSQL 9.6.
You don't need a function; you can just update one table from the contents of another table:
UPDATE big_table dst
SET user_name = src.correct
FROM nameKey_table src
WHERE src.wrong LIKE '%' || dst.user_name || '%'
AND dst.user_name <> src.correct -- avoid idempotent updates
;
And if you need performance, dont rely on the LIKE operator, it cannot use indexes for leading %. Instead, use a lookup-table with one entry per row:
CREATE TABLE bad_spell (
correct VARCHAR,
wrong VARCHAR PRIMARY KEY -- This will cause an unique index to be created.
);
INSERT INTO bad_spell VALUES
('David', 'Dave')
,('David', 'DAVE')
,('David', 'dave')
,('David', 'DAVID')
,('David', 'david')
,('Jack', 'JACK')
,('Jack', 'jack')
,('Jack', 'Jak')
,('Jack', 'jak')
;
-- This indexes could be temporary
CREATE INDEX ON big_table(user_name);
-- EXPLAIN
UPDATE big_table dst
SET user_name = src.correct
FROM bad_spell src
WHERE dst.user_name = src.wrong
AND dst.user_name <> src.correct -- avoid idempotent updates
;
SELECT* FROM big_table
;
I have a table
create table t
(id serial primary key,
name text,
val INTEGER);
insert into t(name, val)
select 'user1', x'0001'::INT
union all
select 'user1', x'0010'::INT
union all
select 'user1', x'0110'::INT
union all
select 'user2', x'0001'::INT
How I can select values into variable using bit operation for given name?
create or replace function get_union(
name text,
OUT retval int
)
as $BODY$
begin
-- ?
end
$BODY$ language plpgsql;
For example, the function should return 111 for name 'user1'
select to_hex(x'0001'::int | x'0010'::int | x'0100'::int);
---
111
select to_hex(bit_or(val)), bit_or(val), bit_or(val)::bit(16)
from t
where name = 'user1';
to_hex | bit_or | bit_or
--------+--------+------------------
111 | 273 | 0000000100010001