How to select values into var using BIT operation? - postgresql

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

Related

How to condition on a list of parameters in PGSQL

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

PostgreSQL recursive parent/child query

I'm having some trouble working out the PostgreSQL documentation for recursive queries, and wonder if anyone might be able to offer a suggestion for the following.
Here's the data:
Table "public.subjects"
Column | Type | Collation | Nullable | Default
-------------------+-----------------------------+-----------+----------+--------------------------------------
id | bigint | | not null | nextval('subjects_id_seq'::regclass)
name | character varying | | |
Table "public.subject_associations"
Column | Type | Collation | Nullable | Default
------------+-----------------------------+-----------+----------+--------------------------------------------------
id | bigint | | not null | nextval('subject_associations_id_seq'::regclass)
parent_id | integer | | |
child_id | integer | | |
Here, a "subject" may have many parents and many children. Of course, at the top level a subject has no parents and at the bottom no children. For example:
parent_id | child_id
------------+------------
2 | 3
1 | 4
1 | 3
4 | 8
4 | 5
5 | 6
6 | 7
What I'm looking for is starting with a child_id to get all the ancestors, and with a parent_id, all the descendants. Therefore:
parent_id 1 -> children 3, 4, 5, 6, 7, 8
parent_id 2 -> children 3
child_id 3 -> parents 1, 2
child_id 4 -> parents 1
child_id 7 -> parents 6, 5, 4, 1
Though there seem to be a lot of examples of similar things about I'm having trouble making sense of them, so any suggestions I can try out would be welcome.
To get all children for subject 1, you can use
WITH RECURSIVE c AS (
SELECT 1 AS id
UNION ALL
SELECT sa.child_id
FROM subject_associations AS sa
JOIN c ON c.id = sa. parent_id
)
SELECT id FROM c;
CREATE OR REPLACE FUNCTION func_finddescendants(start_id integer)
RETURNS SETOF subject_associations
AS $$
DECLARE
BEGIN
RETURN QUERY
WITH RECURSIVE t
AS
(
SELECT *
FROM subject_associations sa
WHERE sa.id = start_id
UNION ALL
SELECT next.*
FROM t prev
JOIN subject_associations next ON (next.parentid = prev.id)
)
SELECT * FROM t;
END;
$$ LANGUAGE PLPGSQL;
Try this
--- Table
-- DROP SEQUENCE public.data_id_seq;
CREATE SEQUENCE "data_id_seq"
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
ALTER TABLE public.data_id_seq
OWNER TO postgres;
CREATE TABLE public.data
(
id integer NOT NULL DEFAULT nextval('data_id_seq'::regclass),
name character varying(50) NOT NULL,
label character varying(50) NOT NULL,
parent_id integer NOT NULL,
CONSTRAINT data_pkey PRIMARY KEY (id),
CONSTRAINT data_name_parent_id_unique UNIQUE (name, parent_id)
)
WITH (
OIDS=FALSE
);
INSERT INTO public.data(id, name, label, parent_id) VALUES (1,'animal','Animal',0);
INSERT INTO public.data(id, name, label, parent_id) VALUES (5,'birds','Birds',1);
INSERT INTO public.data(id, name, label, parent_id) VALUES (6,'fish','Fish',1);
INSERT INTO public.data(id, name, label, parent_id) VALUES (7,'parrot','Parrot',5);
INSERT INTO public.data(id, name, label, parent_id) VALUES (8,'barb','Barb',6);
--- Function
CREATE OR REPLACE FUNCTION public.get_all_children_of_parent(use_parent integer) RETURNS integer[] AS
$BODY$
DECLARE
process_parents INT4[] := ARRAY[ use_parent ];
children INT4[] := '{}';
new_children INT4[];
BEGIN
WHILE ( array_upper( process_parents, 1 ) IS NOT NULL ) LOOP
new_children := ARRAY( SELECT id FROM data WHERE parent_id = ANY( process_parents ) AND id <> ALL( children ) );
children := children || new_children;
process_parents := new_children;
END LOOP;
RETURN children;
END;
$BODY$
LANGUAGE plpgsql VOLATILE COST 100;
ALTER FUNCTION public.get_all_children_of_parent(integer) OWNER TO postgres
--- Test
SELECT * FROM data WHERE id = any(get_all_children_of_parent(1))
SELECT * FROM data WHERE id = any(get_all_children_of_parent(5))
SELECT * FROM data WHERE id = any(get_all_children_of_parent(6))

Use array of IDs to insert records into table if it does not already exist

I have created a postgresql function that takes a comma separated list of ids as input parameter. I then convert this comma separated list into an array.
CREATE FUNCTION myFunction(csvIDs text)
RETURNS void AS $$
DECLARE ids INT[];
BEGIN
ids = string_to_array(csvIDs,',');
-- INSERT INTO tableA
END; $$
LANGUAGE PLPGSQL;
What I want to do now is to INSERT a record for each of the id's(in the array) into TABLE A if the ID does not already exist in table. The new records should have value field set to 0.
Table is created like this
CREATE TABLE TableA (
id int PRIMARY KEY,
value int
);
Is this possible to do?
You can use unnest() function to get each element of your array.
create table tableA (id int);
insert into tableA values(13);
select t.ids
from (select unnest(string_to_array('12,13,14,15', ',')::int[]) ids) t
| ids |
| --: |
| 12 |
| 13 |
| 14 |
| 15 |
Now you can check if ids value exists before insert a new row.
CREATE FUNCTION myFunction(csvIDs text)
RETURNS int AS
$myFunction$
DECLARE
r_count int;
BEGIN
insert into tableA
select t.ids
from (select unnest(string_to_array(csvIDs,',')::int[]) ids) t
where not exists (select 1 from tableA where id = t.ids);
GET DIAGNOSTICS r_count = ROW_COUNT;
return r_count;
END;
$myFunction$
LANGUAGE PLPGSQL;
select myFunction('12,13,14,15') as inserted_rows;
| inserted_rows |
| ------------: |
| 3 |
select * from tableA;
| id |
| -: |
| 13 |
| 12 |
| 14 |
| 15 |
dbfiddle here

can not get all column values separately but instead get all values in one column postgresql --9.5

CREATE OR REPLACE FUNCTION public.get_locations(
location_word varchar(50)
)
RETURNS TABLE
(
country varchar(50),
city varchar(50)
)
AS $$
DECLARE
location_word_ varchar(50);
BEGIN
location_word_:=concat(location_word, '%');
RETURN QUERY EXECUTE format(' (SELECT c.country, ''''::varchar(50) as city FROM webuser.country c
WHERE lower(c.country) LIKE %L LIMIT 1)
UNION
(SELECT c.country,ci.city FROM webuser.country c
JOIN webuser.city ci ON c.country_id=ci.country_id
WHERE lower(ci.city) LIKE %L LIMIT 4)',
location_word_,
location_word_ ) ;
END
$$ language PLPGSQL STABLE;
SELECT public.get_locations('a'::varchar(50));
I get this;
+get_locations +
+record +
------------------
+(Andorra,"") +
+(Germany,Aach) +
+(Germany,Aalen) +
+(Germany,Achim) +
+(Germany,Adenau)+
How can i place/get the values column by column like below? Because otherwise i can not match the values correctly. I should get the values column by column as countries and cities etc.
|country | city |
-------------------------------
| Andorra | "" |
| Germany | Aach |
| Germany | Aalen |
| Germany | Achim |
| Germany | Adenau |
Your function is declared as returns table so you have to use it like a table:
SELECT *
FROM public.get_locations('a'::varchar(50));
Unrelated, but:
Your function is way too complicated, you don't need dynamic SQL, nor do you need a PL/pgSQL function.
You can simplify that to:
CREATE OR REPLACE FUNCTION public.get_locations(p_location_word varchar(50))
RETURNS TABLE(country varchar(50), city varchar(50))
AS $$
(SELECT c.country, ''::varchar(50) as city
FROM webuser.country c
WHERE lower(c.country) LIKE concat(p_location_word, '%')
LIMIT 1)
UNION ALL
(SELECT c.country ,ci.city
FROM webuser.country c
JOIN webuser.city ci ON c.country_id = ci.country_id
WHERE lower(ci.city) LIKE concat(p_location_word, '%')
LIMIT 4)
$$
language SQL;

How can I get result by using “execute ‘delete from table1'"

when I use execute command to run a sql cmd, I want to get the result of it.
As we know, I can get total counts by variable sc when I use :
execute 'select * from table" into sc;
But How can I get result by using:
execute 'delete from table1'"?
when I use INTO, it turns out
ERROR: "INTO used with a command that cannot return data"
execute 'WITH row_deleted AS (DELETE FROM table1 RETURNING *) SELECT count(*) FROM row_deleted' into c;
You can use it inside a plsql funtion as following:
--Drop the table and the functin if it exist:
DROP TABLE IF EXISTS table1;
DROP FUNCTION if exists _deleted_rows();
--Create the table for the example:
CREATE TABLE table1
(
row_id serial NOT NULL,
col1 character varying,
CONSTRAINT table1_pkey PRIMARY KEY (row_id)
);
--Insert some rows:
insert into table1 (col1) values ('test1');
insert into table1 (col1) values ('test2');
insert into table1 (col1) values ('test3');
--Ctreate the function that count the number of deleted rows of the table: table1
CREATE OR REPLACE FUNCTION _deleted_rows()
RETURNS character varying AS
$BODY$declare
nbr_deleted integer;
begin
execute 'WITH row_deleted AS (DELETE FROM table1 RETURNING *) SELECT count(*) FROM row_deleted' into nbr_deleted;
return (nbr_deleted);
end;$BODY$
LANGUAGE plpgsql VOLATILE;
Test that function (got problem building shema on sqlfidlle):
select * from _deleted_rows();
_deleted_rows
---------------
3
(1 ligne)
Execute command
DELETE command
It's a little unclear to me what you are trying to do, but you should be able use "RETURNING". Here I am just returning the rows that were deleted:
CREATE TEMP TABLE foo(id int, description text);
INSERT INTO foo VALUES
(1, 'HELLO'),
(2, 'WORLD');
DELETE FROM foo returning *;
+----+-------------+
| id | description |
+----+-------------+
| 1 | HELLO |
| 2 | WORLD |
+----+-------------+
(2 rows)
Also, if you need them moved "into" a table (for example), you could do something like:
DROP TABLE IF EXISTS foo;
DROP TABLE IF EXISTS deleted_foo;
CREATE TEMP TABLE foo(id int, description text);
INSERT INTO foo VALUES
(1, 'HELLO'),
(2, 'WORLD');
CREATE TEMP TABLE deleted_foo(id int, description text);
WITH x AS (DELETE FROM foo RETURNING *)
INSERT INTO deleted_foo
SELECT * FROM x;
SELECT * FROM deleted_foo;
+----+-------------+
| id | description |
+----+-------------+
| 1 | HELLO |
| 2 | WORLD |
+----+-------------+
(2 rows)
Assuming that you are doing this from inside a plpgsql function, you could also use the ROW_COUNT variable. For example:
GET DIAGNOSTICS integer_var = ROW_COUNT;
This would give you the number of rows that were deleted.