Seperate Input/Output type for a Table in PostgreSQL - postgresql

I was wondering if it is possible to have the input for a column be one string, with the output being a different string through some dictionary in PostgreSQL. I do know how to use CASE to convert numbers to strings using a SELECT statement, however, I was hoping to create a table such that inputs only require numbers but outputs always give strings.
As an example, for currency USD, CDN and GBP, where 1 = USD, 2 = CDN and 3 = GBP, an example would be:
CREATE TABLE test_table (
currency CHAR (1) CHECK (currency IN ('1','2','3'))
)
Where I could do this:
INSERT INTO test_table (currency)
VALUES ('1')
INSERT INTO test_table (currency)
VALUES ('1')
INSERT INTO test_table (currency)
VALUES ('2')
INSERT INTO test_table (currency)
VALUES ('3')
INSERT INTO test_table (currency)
VALUES ('3')
and the output would look like this:

You can use a CASE expression:
select case currency
when '1' then 'USD'
when '2' then 'CDN'
when '3' then 'GBP'
when '4' then 'EUR'
end as currency
from test_table;
But a better solution would be to create a currency table:
create table currency
(
id integer primary key,
currency_code varchar(3)
);
Then create a foreign key from your base table to the lookup table:
create table test_table
(
...
currency_id integer not null references currency,
...
);
Then use a join to display the code:
select c.code
from test_table t
join currency c on c.id = t.currency_id;

Related

Transform Postgres rows data into columns based on condition

I am wanting to use Postgres transforming rows data into columns. I have id and value column, id column will have value as 'Account_Number' and 'Account_Holder_Name' and value column corresponding to the actual value.
The below table is the representation of the data I will hold in a table and belongs to the custom fields, so the id column may also contain more field names and the value field will contain the actual value of that field
Table: trans
id
type
booking_date
1
Deposit
2022-02-02
2
Withdraw
2022-02-03
Table: trans_custom_fields
id
value
transId
ACCOUNT_HOLDER_NAME
Manoj Sharma
1
ACCOUNT_NUMBER
113565TTE44656
1
RECIPT_NUMBER
24324.
1
ACCOUNT_HOLDER_NAME
Another User
2
ACCOUNT_NUMBER
35546656TRFG23
2
RECIPT_NUMBER
24324686
2
Now I am want to transform this table data in the below format which can be used in the join query too and shown as a single record.
Table: join resultset
ACCOUNT_HOLDER_NAME
ACCOUNT_NUMBER
RECIPT_NUMBER
transId
Manoj Sharma
113565TTE44656
24324
1
Another User
35546656TRFG23
24324686
2
What can I try next?
--https://www.postgresql.org/docs/current/sql-keywords-appendix.html
--data init. value kind of ambiguous for me. I change to _value.
--obviously now, the id and _value will be as text type.
begin;
create table trans_custom_fields(id text, _value text,transid integer );
insert into trans_custom_fields values('ACCOUNT_HOLDER_NAME','Manoj Sharma',1);
insert into trans_custom_fields values('ACCOUNT_NUMBER', '113565TTE44656', 1);
insert into trans_custom_fields values( 'RECIPT_NUMBER', '24324.', 1);
insert into trans_custom_fields values( 'ACCOUNT_HOLDER_NAME', 'Another User', 2);
insert into trans_custom_fields values('ACCOUNT_NUMBER', '35546656TRFG23', 2);
insert into trans_custom_fields values('RECIPT_NUMBER', '24324686', 2);
commit;
--step 1 create a new temp table a, transform trans_custom_fields to a jsonb table.
create temp table a as (select to_jsonb(row)
from ( select id, _value, transid from trans_custom_fields t ) row);
--step 2 rename '_value' to ACCOUNT_HOLDER_NAME,ACCOUNT_NUMBER, RECIPT_NUMBER RESPECTIVELY
update a set to_jsonb
= to_jsonb - '_value' || jsonb_build_object( to_jsonb->>'id',to_jsonb -> '_value') returning *;
--step 3 remove the 'id' key value pair
update a set to_jsonb = to_jsonb - 'id';
--step 4. aggregate to one jsonb based o transid
select jsonb_agg (to_jsonb - 'transid' ),a.to_jsonb->>'transid'
as transid from a group by a.to_jsonb ->>'transid';
--step5 get the result.
with a as (select jsonb_agg (to_jsonb - 'transid' ),
a.to_jsonb->>'transid' as transid
from a group by a.to_jsonb ->>'transid')
select transid,
jsonb_agg -> 0 ->> 'ACCOUNT_HOLDER_NAME' as ACCOUNT_HOLDER_NAME,
jsonb_agg -> 1 ->> 'ACCOUNT_NUMBER' as ACCOUNT_NUMBER,
jsonb_agg -> 2 ->> 'RECIPT_NUMBER' as RECIPT_NUMBER
from a;
step2 reference: PostgreSQL rename attribute in jsonb field
Below is the query to create the respective table and insert some data.
begin;
create table trans_custom_fields(id text, _value text,transid integer );
insert into trans_custom_fields values('ACCOUNT_HOLDER_NAME','Manoj Sharma',1);
insert into trans_custom_fields values('ACCOUNT_NUMBER', '113565TTE44656', 1);
insert into trans_custom_fields values( 'RECIPT_NUMBER', '24324.', 1);
insert into trans_custom_fields values( 'ACCOUNT_HOLDER_NAME', 'Another User', 2);
insert into trans_custom_fields values('ACCOUNT_NUMBER', '35546656TRFG23', 2);
insert into trans_custom_fields values('RECIPT_NUMBER', '24324686', 2);
commit;
Now I want to do the transformation for this data and here I am going to use crosstab feature of Postgres.
SELECT *
FROM crosstab(
'SELECT transid, id, _value
FROM trans_custom_fields
ORDER BY 1,2'
) AS ct (transid int, ACCOUNT_HOLDER_NAME text, ACCOUNT_NUMBER text);
I am really thankful to crosstab example for just helping me understand and write my own answer for my question, also thank #mark who does provide the queries and resolution but that fit better as of now.

COALESCE types character varying and numeric cannot be matched

I have a table named table1 with columns Gender varchar(10) and Team numeric.
create table table1 (
ID integer
Gender varchar(10),
Team numeric
);
insert into table1 (ID,Gender,Team) values
(1,'M',NULL),
(2,NULL,10),
(3,NULL,6),
(4,''F',NULL),
(5,NULL,3);
I will like to create a new column as Nxt that returns a row that is not null from any of the columns either a string or integer.
The column Nxt will look like: M,10,6,F,3
I tried this:
select coalesce(Gender,Team) as value from table1;
It returns this error:
COALESCE types character varying and numeric cannot be matched
Try to cast the column as text
select coalesce(Gender,Team::text) as value from table1;

Quotation mark incorrect when using crosstab() in PostgreSQL

I have a table t1 as below:
create table t1 (
person_id int,
item_name varchar(30),
item_value varchar(100)
);
There are five records in this table:
person_id | item_name | item_value
1 'NAME' 'john'
1 'GENDER' 'M'
1 'DOB' '1970/02/01'
1 'M_PHONE' '1234567890'
1 'ADDRESS' 'Some Addresses unknown'
Now I want to use crosstab function to extract NAME, GENDER data, so I write a SQL as:
select * from crosstab(
'select person_id, item_name, item_value from t1
where person_id=1 and item_name in ('NAME', 'GENDER') ')
as virtual_table (person_id int, NAME varchar, GENDER varchar)
My problem is, as you see the SQL in crosstab() contains condition of item_name, which will cause the quotation marks to be incorrect.
How do I solve the problem?
To avoid any confusion about how to escape single quotes and generally simplify the syntax, use dollar-quoting for the query string:
SELECT *
FROM crosstab(
$$
SELECT person_id, item_name, item_value
FROM t1
WHERE person_id = 1
AND item_name IN ('NAME', 'GENDER')
$$
) AS virtual_table (person_id int, name varchar, gender varchar);
See:
Insert text with single quotes in PostgreSQL
And you should add ORDER BY to your query string. I quote the manual for the tablefunc module:
In practice the SQL query should always specify ORDER BY 1,2 to ensure
that the input rows are properly ordered, that is, values with the
same row_name are brought together and correctly ordered within the
row. Notice that crosstab itself does not pay any attention to the
second column of the query result; it's just there to be ordered by,
to control the order in which the third-column values appear across the page.
See:
PostgreSQL Crosstab Query
Double your single quotes to escape them:
select * from crosstab(
'select person_id, item_name, item_value from t1
where person_id=1 and item_name in (''NAME'', ''GENDER'') ')
as virtual_table (person_id int, NAME varchar, GENDER varchar)

How to insert default values in SQL table?

I have a table like this:
create table1 (field1 int,
field2 int default 5557,
field3 int default 1337,
field4 int default 1337)
I want to insert a row which has the default values for field2 and field4.
I've tried insert into table1 values (5,null,10,null) but it doesn't work and ISNULL(field2,default) doesn't work either.
How can I tell the database to use the default value for the column when I insert a row?
Best practice it to list your columns so you're independent of table changes (new column or column order etc)
insert into table1 (field1, field3) values (5,10)
However, if you don't want to do this, use the DEFAULT keyword
insert into table1 values (5, DEFAULT, 10, DEFAULT)
Just don't include the columns that you want to use the default value for in your insert statement. For instance:
INSERT INTO table1 (field1, field3) VALUES (5, 10);
...will take the default values for field2 and field4, and assign 5 to field1 and 10 to field3.
This works if all the columns have associated defaults and one does not want to specify the column names:
insert into your_table
default values
Try it like this
INSERT INTO table1 (field1, field3) VALUES (5,10)
Then field2 and field4 should have default values.
I had a case where I had a very simple table, and I basically just wanted an extra row with just the default values. Not sure if there is a prettier way of doing it, but here's one way:
This sets every column in the new row to its default value:
INSERT INTO your_table VALUES ()
Note: This is extra useful for MySQL where INSERT INTO your_table DEFAULT VALUES does not work.
If your columns should not contain NULL values, you need to define the columns as NOT NULL as well, otherwise the passed in NULL will be used instead of the default and not produce an error.
If you don't pass in any value to these fields (which requires you to specify the fields that you do want to use), the defaults will be used:
INSERT INTO
table1 (field1, field3)
VALUES (5,10)
You can write in this way
GO
ALTER TABLE Table_name ADD
column_name decimal(18, 2) NOT NULL CONSTRAINT Constant_name DEFAULT 0
GO
ALTER TABLE Table_name SET (LOCK_ESCALATION = TABLE)
GO
COMMIT
To insert the default values you should omit them something like this :
Insert into Table (Field2) values(5)
All other fields will have null or their default values if it has defined.
CREATE TABLE #dum (id int identity(1,1) primary key, def int NOT NULL default(5), name varchar(25))
-- this works
INSERT #dum (def, name) VALUES (DEFAULT, 'jeff')
SELECT * FROM #dum;
DECLARE #some int
-- this *doesn't* work and I think it should
INSERT #dum (def, name)
VALUES (ISNULL(#some, DEFAULT), 'george')
SELECT * FROM #dum;
CREATE PROC SP_EMPLOYEE --By Using TYPE parameter and CASE in Stored procedure
(#TYPE INT)
AS
BEGIN
IF #TYPE=1
BEGIN
SELECT DESIGID,DESIGNAME FROM GP_DESIGNATION
END
IF #TYPE=2
BEGIN
SELECT ID,NAME,DESIGNAME,
case D.ISACTIVE when 'Y' then 'ISACTIVE' when 'N' then 'INACTIVE' else 'not' end as ACTIVE
FROM GP_EMPLOYEEDETAILS ED
JOIN GP_DESIGNATION D ON ED.DESIGNATION=D.DESIGID
END
END

Select from insert

Is it possible to select from an insert statement? For example:
SELECT id FROM (INSERT INTO table (col1, col2) VALUES (val1, val2));
Where id is an autoincrementing primary key.
It's not possible in such way, because INSERT doesn't return virtual table for SELECT. However you could get id's actual value using currval(regclass) sequence function as:
SELECT currval('yourTableName_id_seq'::regclass);
currval
---------
1
(1 row)
EDIT:
Use RETURNING clause (available since PostgreSQL 8.2):
INSERT INTO yourTableName (col1, col2) VALUES ('aaa', 'bbb') RETURNING id;
id
----
2
(1 row)
"SELECT id FROM mytable WHERE id IS NULL;
"id" has to be an auto_increment column to make it work.
It will return the last_insert_id just as last_insert_id() is expected to do.
example:
mysql> INSERT INTO orders (customer_cust_id, orderdatetime, message, taxrate, shippingprice)
-> SELECT '1', NOW(), null, taxrate, shippingprice FROM customer
-> WHERE cust_id='1';"
http://dev.mysql.com/doc/refman/5.0/en/insert-select.html