How to select with the length of the field / untrim using select - postgresql

I have this table that I would like to select values from. The thing is that I would like to get the exact amount of length in the output of my select as I have in my table.
For example:
CREATE TABLE myschema.test_table
(
id serial NOT NULL,
otyp CHARACTER VARYING(3),
fname CHARACTER VARYING(8),
age integer
) WITH(OIDS=FALSE);
And let's say that this table contain
id | otyp | fname | age
----+------+--------+-----
1 | aa | gustav | 20
(1 row)
SELECT id || otyp || fname || age FROM myschema.test_table;
This would give me this result: 1aagustav20
I want the output to be 1aa gustav 20
Any help would appreciated!

Just do
SELECT id || otyp || ' ' || fname || ' ' || age FROM myschema.test_table;

I solved the problem!
SELECT id
|| RPAD(otyp, 3, ' ')
|| RPAD(fname, 8, ' ')
|| age
FROM
myschema.test_table;

Related

Compare two tables and find the missing column using left join

I wanted to compare the two tables employees and employees_a and find the missing columns in the table comployees_a.
select a.Column_name,
From User_tab_columns a
LEFT JOIN User_tab_columns b
ON upper(a.table_name) = upper(b.table_name)||'_A'
AND a.column_name = b.column_name
Where upper(a.Table_name) = 'EMPLOYEES'
AND upper(b.table_name) = 'EMPLOYEES_A'
AND b.column_name is NULL
;
But this doesnt seems to be working. No rows are returned.
My employees table has the below columns
emp_name
emp_id
base_location
department
current_location
salary
manager
employees_a table has below columns
emp_name
emp_id
base_location
department
current_location
I want to find the rest two columns and add them into employees_a table.
I have more than 50 tables like this to compare them and find the missing column and add those columns into their respective "_a" table.
Missing columns? Why not using the MINUS set operator, seems to be way simpler, e.g.
select column_name from user_tables where table_name = 'EMP_1'
minus
select column_name from user_tables where table_name = 'EMP_2'
Thirstly, check if user_tab_columns table contains columns of your tables (in my case user_tab_columns is empty and I have to use all_tab_columns):
select a.Column_name
From User_tab_columns a
Where upper(a.Table_name) = 'EMPLOYEES'
Secondly, remove line AND upper(b.table_name) = 'EMPLOYEES_A', because upper(b.table_name) is null in case a column is not found. You have b.table_name in JOIN part of the SELECT already.
select a.Column_name
From User_tab_columns a
LEFT JOIN User_tab_columns b
ON upper(a.table_name) = upper(b.table_name)||'_A'
AND a.column_name = b.column_name
Where upper(a.Table_name) = 'EMPLOYEES'
AND b.column_name is NULL
You do not need any joins and can use:
select 'ALTER TABLE EMPLOYEES_A ADD "'
|| Column_name || '" '
|| CASE MAX(data_type)
WHEN 'NUMBER'
THEN 'NUMBER(' || MAX(data_precision) || ',' || MAX(data_scale) || ')'
WHEN 'VARCHAR2'
THEN 'VARCHAR2(' || MAX(data_length) || ')'
END
AS sql
From User_tab_columns
Where Table_name IN ('EMPLOYEES', 'EMPLOYEES_A')
GROUP BY COLUMN_NAME
HAVING COUNT(CASE table_name WHEN 'EMPLOYEES' THEN 1 END) = 1
AND COUNT(CASE table_name WHEN 'EMPLOYEES_A' THEN 1 END) = 0;
Or, for multiple tables:
select 'ALTER TABLE ' || MAX(table_name) || '_A ADD "'
|| Column_name || '" '
|| CASE MAX(data_type)
WHEN 'NUMBER'
THEN 'NUMBER(' || MAX(data_precision) || ',' || MAX(data_scale) || ')'
WHEN 'VARCHAR2'
THEN 'VARCHAR2(' || MAX(data_length) || ')'
END
AS sql
From User_tab_columns
Where Table_name IN ('EMPLOYEES', 'EMPLOYEES_A', 'SOMETHING', 'SOMETHING_A')
GROUP BY
CASE
WHEN table_name LIKE '%_A'
THEN SUBSTR(table_name, 1, LENGTH(table_name) - 2)
ELSE table_name
END,
COLUMN_NAME
HAVING COUNT(CASE WHEN table_name NOT LIKE '%_A' THEN 1 END) = 1
AND COUNT(CASE WHEN table_name LIKE '%_A' THEN 1 END) = 0;
fiddle

(Select Procedure) SUM of two different SMALLINT fields from different tables. I am using SQL Manager for Interbase and Firebird

I want to get the SUM of two different SMALLINT fields from different tables in select procedure. I am using SQL Manager for Interbase and Firebird
The values of field TRAINING_SRCVDAYS in Table E are 1 and 2 while the value of field LEAVE_WITHPAY_NUMDAY in Table A is 2.
Table E Table A
1 2
2
Expected output(Sum) 3 2
real output to my procedure 3 4
here is my code. please help
CREATE PROCEDURE AAAASAMPLE(
UPDATEHANDLER VARCHAR(50) CHARACTER SET ISO8859_1 COLLATE ISO8859_1,
WHAT_YEAR VARCHAR(50) CHARACTER SET ISO8859_1 COLLATE ISO8859_1)
RETURNS(
FULLNAME VARCHAR(100) CHARACTER SET ISO8859_1 COLLATE ISO8859_1,
TRAINING_SRCVDAYS SMALLINT,
LEAVE_WITHPAY_NUMDAY SMALLINT,
SICKLEAVE SMALLINT)
AS
BEGIN
FOR
SELECT
C.EMP_SURNAME || ', ' || C.EMP_FIRSTNAME || ' ' || C.EMP_MIDDLENAME || ' '||C.EMP_SUFFIXNAME,
SUM(E.TRAINING_SRCVDAYS),
SUM(A.LEAVE_WITHPAY_NUMDAY),
SUM(E.TRAINING_SRCVDAYS) - SUM(A.LEAVE_WITHPAY_NUMDAY)
FROM LEAVE_TABLE A, POSITION_TABLE B, EMP_TABLE C, TRAINTRN_TABLE D, TRAINTYP_TABLE E
WHERE
(A.EMP_PK =: UPDATEHANDLER AND A.LEAVE_FROM_YEAR =: WHAT_YEAR AND A.EMP_PK = C.EMP_PK
AND A.POSITION_PK = B.POSITION_PK AND B.POSITION_CLASS_REF = 0 )
AND
(D.EMP_PK = C.EMP_PK AND D.TRAINING_PK = E.TRAINING_PK AND D.EMP_PK =: UPDATEHANDLER
AND E.TRAINING_SRVCCRDT = 'Yes' AND E.TRAINING_FROMYEAR =: WHAT_YEAR)
GROUP BY
C.EMP_SURNAME || ', ' || C.EMP_FIRSTNAME || ' ' || C.EMP_MIDDLENAME || ' '|| C.EMP_SUFFIXNAME
INTO
:FULLNAME,
:TRAINING_SRCVDAYS,
:LEAVE_WITHPAY_NUMDAY,
:SICKLEAVE
DO
BEGIN
SUSPEND;
END
END;

Flattening Postgres nested JSONB column

I'm looking to see how to flatten data nested in a JSONB column.
As an example, say we have the table users with user_id(int) and siblings(JSONB)
With rows like:
id | JSONB
---------------------
1 | {"brother": {"first_name":"Sam", "last_name":"Smith"}, "sister": {"first_name":"Sally", "last_name":"Smith"}
2 | {"sister": {"first_name":"Jill"}}
I'm looking for a query that will return a response like:
id | sibling | first_name | last_name
-------------------------------------
1 | "brother" | "Sam" | "Smith"
1 | "sister" | "Sally" | "Smith"
2 | "sister" | "Jill" | null
I develop to this use it in psql.
To check code I create small view t1:
CREATE VIEW t1 AS (
SELECT 1 AS id, '{"brother": {"first_name":"Sam", "last_name":"Smith"}, "sister": {"first_name":"Sally", "last_name":"Smith"}}'::jsonb AS jsonb
UNION SELECT 2, '{"sister": {"first_name":"Jill", "last_name":"Johnson"}}'
UNION SELECT 3, '{"sister": {"first_name":"Jill", "x_name":"Johnson"}}'
);
The first task is to found list of possible key:
WITH fields AS (
SELECT DISTINCT jff.key
FROM t1,
jsonb_each(jsonb) AS jf,
jsonb_each(jf.value) AS jff
)
SELECT * FROM fields;
The result is:
key
------------
first_name
last_name
x_name
The next step is generate queries:
SELECT 'SELECT id, jf.key as sibling, ' || (
WITH fields AS (
SELECT DISTINCT jff.key
FROM t1,
jsonb_each(jsonb) AS jf,
jsonb_each(jf.value) AS jff
)
SELECT string_agg('jf.value->>''' || key || ''' as "' || key || '"', ',' ORDER BY key)
FROM fields
)
|| ' FROM t1, jsonb_each(jsonb) AS jf ORDER BY 1, 2, 3;' AS cmd;
It returns:
cmd
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SELECT id, jf.key as sibling,jf.value->>'first_name' as "first_name",jf.value->>'last_name' as "last_name",jf.value->>'x_name' as "x_name" FROM t1, jsonb_each(jsonb) AS jf ORDER BY 1, 2, 3;
(1 row)
To set result as psql variable I use gset:
\gset
After that you can call query:
:cmd
id | sibling | first_name | last_name | x_name
----+---------+------------+-----------+---------
1 | brother | Sam | Smith |
1 | sister | Sally | Smith |
2 | sister | Jill | Johnson |
3 | sister | Jill | | Johnson
(4 rows)
To run it from external languages you can create postgres function than return SQL command:
CREATE OR REPLACE FUNCTION build_query(IN tname text, OUT cmd text) AS $sql$
BEGIN
EXECUTE $cmd$
SELECT 'SELECT id, jf.key as sibling, ' || (
WITH fields AS (
SELECT DISTINCT jff.key
FROM t1,
jsonb_each(jsonb) AS jf,
jsonb_each(jf.value) AS jff
)
SELECT string_agg('jf.value->>''' || key || ''' as "' || key || '"', ',' ORDER BY key)
FROM fields
)
|| ' FROM $cmd$ || quote_ident(tname) || $cmd$ , jsonb_each(jsonb) AS jf ORDER BY 1, 2, 3;'$cmd$ INTO cmd;
RETURN;
END;
$sql$ LANGUAGE plpgsql;
SELECT * FROM build_query('t1');
cmd
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SELECT id, jf.key as sibling, jf.value->>'first_name' as "first_name",jf.value->>'last_name' as "last_name",jf.value->>'x_name' as "x_name" FROM t1 , jsonb_each(jsonb) AS jf ORDER BY 1, 2, 3;
(1 row)

sql to display all names in 1 row (1 string)

ad_org table with column id & name
ad_org
ad_org_id | name
----------------------------------+-----------
357947E87C284935AD1D783CF6F099A1 | Spain
43D590B4814049C6B85C6545E8264E37 | Main
5EFF95EB540740A3B10510D9814EFAD5 | USA
2878085215E54C73A04D394BFD170733 | India
22669845D93A49A98932CE29AE02E0FD | Honkong
how to get output of all names(in 1 string) in this way from the above database
Spain | Main | USA | India | Honkong
in 1 select statement.
Use string_agg.
SELECT string_agg("name", ' | ') FROM thetable;
For older PostgreSQL, you must use array_agg and array_to_string:
SELECT array_to_string( array_agg("name"), ' | ') FROM thetable;
If you want a particular order, put it in the aggregate, e.g for alphabetical:
SELECT string_agg("name", ' | ' ORDER BY "name") FROM thetable;
use below code
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(ColumnName)
from yourtable
group by ColumnName, id
order by id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ' + #cols + ' from
(
select value, ColumnName
from yourtable
) x
pivot
(
max(value)
for ColumnName in (' + #cols + ')
) p '
execute(#query)
Click here for Demo
got it by searching..
Equivalent to PostgreSQL array() / array_to_string() functions in Oracle 9i
select array_to_string(array(select name from ad_org), '|') as names;

Postgresql - how to groups rows and convert some rows to columns [duplicate]

I have an interesting conundrum which I believe can be solved in purely SQL. I have tables similar to the following:
responses:
user_id | question_id | body
----------------------------
1 | 1 | Yes
2 | 1 | Yes
1 | 2 | Yes
2 | 2 | No
1 | 3 | No
2 | 3 | No
questions:
id | body
-------------------------
1 | Do you like apples?
2 | Do you like oranges?
3 | Do you like carrots?
and I would like to get the following output
user_id | Do you like apples? | Do you like oranges? | Do you like carrots?
---------------------------------------------------------------------------
1 | Yes | Yes | No
2 | Yes | No | No
I don't know how many questions there will be, and they will be dynamic, so I can't just code for every question. I am using PostgreSQL and I believe this is called transposition, but I can't seem to find anything that says the standard way of doing this in SQL. I remember doing this in my database class back in college, but it was in MySQL and I honestly don't remember how we did it.
I'm assuming it will be a combination of joins and a GROUP BY statement, but I can't even figure out how to start.
Anybody know how to do this? Thanks very much!
Edit 1: I found some information about using a crosstab which seems to be what I want, but I'm having trouble making sense of it. Links to better articles would be greatly appreciated!
Use:
SELECT r.user_id,
MAX(CASE WHEN r.question_id = 1 THEN r.body ELSE NULL END) AS "Do you like apples?",
MAX(CASE WHEN r.question_id = 2 THEN r.body ELSE NULL END) AS "Do you like oranges?",
MAX(CASE WHEN r.question_id = 3 THEN r.body ELSE NULL END) AS "Do you like carrots?"
FROM RESPONSES r
JOIN QUESTIONS q ON q.id = r.question_id
GROUP BY r.user_id
This is a standard pivot query, because you are "pivoting" the data from rows to columnar data.
I implemented a truly dynamic function to handle this problem without having to hard code any specific class of answers or use external modules/extensions. It also gives full control over column ordering and supports multiple key and class/attribute columns.
You can find it here: https://github.com/jumpstarter-io/colpivot
Example that solves this particular problem:
begin;
create temporary table responses (
user_id integer,
question_id integer,
body text
) on commit drop;
create temporary table questions (
id integer,
body text
) on commit drop;
insert into responses values (1,1,'Yes'), (2,1,'Yes'), (1,2,'Yes'), (2,2,'No'), (1,3,'No'), (2,3,'No');
insert into questions values (1, 'Do you like apples?'), (2, 'Do you like oranges?'), (3, 'Do you like carrots?');
select colpivot('_output', $$
select r.user_id, q.body q, r.body a from responses r
join questions q on q.id = r.question_id
$$, array['user_id'], array['q'], '#.a', null);
select * from _output;
rollback;
This outputs:
user_id | 'Do you like apples?' | 'Do you like carrots?' | 'Do you like oranges?'
---------+-----------------------+------------------------+------------------------
1 | Yes | No | Yes
2 | Yes | No | No
You can solve this example with the crosstab function in this way
drop table if exists responses;
create table responses (
user_id integer,
question_id integer,
body text
);
drop table if exists questions;
create table questions (
id integer,
body text
);
insert into responses values (1,1,'Yes'), (2,1,'Yes'), (1,2,'Yes'), (2,2,'No'), (1,3,'No'), (2,3,'No');
insert into questions values (1, 'Do you like apples?'), (2, 'Do you like oranges?'), (3, 'Do you like carrots?');
select * from crosstab('select responses.user_id, questions.body, responses.body from responses, questions where questions.id = responses.question_id order by user_id') as ct(userid integer, "Do you like apples?" text, "Do you like oranges?" text, "Do you like carrots?" text);
First, you must install tablefunc extension. Since 9.1 version you can do it using create extension:
CREATE EXTENSION tablefunc;
I wrote a function to generate the dynamic query.
It generates the sql for the crosstab and creates a view (drops it first if it exists).
You can than select from the view to get your results.
Here is the function:
CREATE OR REPLACE FUNCTION public.c_crosstab (
eavsql_inarg varchar,
resview varchar,
rowid varchar,
colid varchar,
val varchar,
agr varchar
)
RETURNS void AS
$body$
DECLARE
casesql varchar;
dynsql varchar;
r record;
BEGIN
dynsql='';
for r in
select * from pg_views where lower(viewname) = lower(resview)
loop
execute 'DROP VIEW ' || resview;
end loop;
casesql='SELECT DISTINCT ' || colid || ' AS v from (' || eavsql_inarg || ') eav ORDER BY ' || colid;
FOR r IN EXECUTE casesql Loop
dynsql = dynsql || ', ' || agr || '(CASE WHEN ' || colid || '=''' || r.v || ''' THEN ' || val || ' ELSE NULL END) AS ' || agr || '_' || r.v;
END LOOP;
dynsql = 'CREATE VIEW ' || resview || ' AS SELECT ' || rowid || dynsql || ' from (' || eavsql_inarg || ') eav GROUP BY ' || rowid;
RAISE NOTICE 'dynsql %1', dynsql;
EXECUTE dynsql;
END
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;
And here is how I use it:
SELECT c_crosstab('query_txt', 'view_name', 'entity_column_name', 'attribute_column_name', 'value_column_name', 'first');
Example:
Fist you run:
SELECT c_crosstab('Select * from table', 'ct_view', 'usr_id', 'question_id', 'response_value', 'first');
Than:
Select * from ct_view;
There is an example of this in contrib/tablefunc/.