how to retrieve data from multiple tables (postgresql) - postgresql

I have 4 different tables that are linked to each other in the following way (I only kept the essential columns in each table to emphasise the relationships between them):
create TABLE public.country (
country_code varchar(2) NOT NULL PRIMARY KEY,
country_name text NOT NULL,
);
create table public.address
(
id integer generated always as identity primary key,
country_code text not null,
CONSTRAINT FK_address_2 FOREIGN KEY (country_code) REFERENCES public.country (country_code)
);
create table public.client_order
(
id integer generated always as identity primary key,
address_id integer null,
CONSTRAINT FK_client_order_1 FOREIGN KEY (address_id) REFERENCES public.address (id)
);
create table public.client_order_line
(
id integer generated always as identity primary key,
client_order_id integer not null,
product_id integer not null,
client_order_status_id integer not null default 0,
quantity integer not null,
CONSTRAINT FK_client_order_line_0 FOREIGN KEY (client_order_id) REFERENCES public.client_order (id)
);
I want to get the data in the following way: for each client order line to show the product_id, quantity and country_name(corresponding to that client order line).
I tried this so far:
SELECT country_name FROM public.country WHERE country_code = (
SELECT country_code FROM public.address WHERE id = (
SELECT address_id FROM public.client_order WHERE id= 5
)
)
to get the country name given a client_order_id from client_order_line table. I don't know how to change this to get all the information mentioned above, from client_order_line table which looks like this:
id client_order_id. product_id. status. quantity
1 1 122 0 1000
2 2 122 0 3000
3 2 125 0 3000
4 3 445 0 2000
Thanks a lot!

You need a few join-s.
select col.client_order_id,
col.product_id,
col.client_order_status_id as status,
col.quantity,
c.country_name
from client_order_line col
left join client_order co on col.client_order_id = co.id
left join address a on co.address_id = a.id
left join country c on a.country_code = c.country_code
order by col.client_order_id;
Alternatively you can use your select query as a scalar subquery expression.

Related

How can i create auto increment column to my existing column?

I am working with Oracle 12c in which I have below table structure:-
CREATE TABLE patients (
patient_id Integer NOT NULL,
customer_id Integer NOT NULL,
title varchar(5) NOT NULL,
fname varchar(125) NOT NULL,
lname varchar(125) NOT NULL,
dob date NOT NULL,
is_medical_card NUMBER(1) NOT NULL CHECK (is_medical_card IN (0,1)),
scheme_number Integer NOT NULL,
status varchar(50) NOT NULL,
created_on date NOT NULL,
last_update_date date NOT NULL,
consent_flag NUMBER(1) NOT NULL CHECK (consent_flag IN (0,1)),
relationship varchar(50) NOT NULL
);
Where patient_id is my primary key so now I want to make it auto increment as well so please let me how can I do this so make it auto increment.
Thanks!
Need to create auto increment to existing column.
You might want to use Identities - Creating a table with an Identity gives you the chance to omit the ID values and let Oracle use a sequence on your desired column:
1. Let's Create the Table:
CREATE TABLE identities (
id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY,
description varchar2(100) NOT NULL
);
Table created.
2. You'll want to create a primary key to ensure uniqueness:
alter table identities add constraint id_pk primary key (ID);
Table altered.
3. Let's insert some data in different ways:
INSERT INTO identities (description)
VALUES('Insert Description omitting ID');
1 row created.
INSERT INTO identities (id,description)
VALUES(NULL,'Insert with explicit NULL value');
1 row created.
4. Save the work done
commit;
Commit complete.
5. Check the results
select * from identities;
ID DESCRIPTION
---------- ---------------------------------------------
1 Insert Description omitting ID
2 Insert with explicit NULL value
As you can see we dind't specify any number for the ID, but the Identity on the ID column did for us
Note: Mind that you can manually insert an ID, but this will mess up with the Identity as it'll normally do with a standard Sequence:
INSERT INTO identities (id,description)
VALUES(3,'Manually insert an ID value');
1 row created.
INSERT INTO identities (description)
VALUES('Test Nextval');
INSERT INTO identities (description)
*
ERROR at line 1:
ORA-00001: unique constraint (XXX.ID_PK) violated
This error, because it tries to insert a '3' into the ID that was manually inserted with the statement before.
Check the table:
select * from identities;
ID DESCRIPTION
---------- ---------------------------------------------
1 Insert Description omitting ID
2 Insert with explicit NULL value
3 Manually insert an ID value
Re-Run the "NEXTVAL" insert:
INSERT INTO identities (description)
VALUES('Test Nextval');
1 row created.
Re-Check the table:
select * from identities;
ID DESCRIPTION
---------- ---------------------------------------------
1 Insert Description omitting ID
2 Insert with explicit NULL value
3 Manually insert an ID value
4 Test Nextval
Hope this Helps.

How to select last value insert in column ( like function LAST() for OracleDB)

I'm actually sutend and I'm setting up DB PostgreSQL for my AirsoftShop and some request on it. I need to find similar function as SELECT LAST(xx) FROM yy usable on SQL server and OracleDB i think. For return the last insert values in the column target by LAST().
I have this table :
CREATE TABLE munition.suivi_ammo (
type_ammo integer NOT NULL,
calibre integer NOT NULL,
event integer NOT NULL,
date_event date NOT NULL,
entrance integer NOT NULL,
exit integer NOT NULL,
inventory integer NOT NULL,
FOREIGN KEY (calibre) REFERENCES munition.index(numero),
FOREIGN KEY (event) REFERENCES munition.index(numero),
FOREIGN KEY (type_ammo) REFERENCES munition.index(numero)
);
and index for definition by number id :
CREATE TABLE munition.index (
numero integer NOT NULL,
definition text NOT NULL,
PRIMARY KEY (numero)
);
I want to select the last inventory insert in the table and calculate the current inventory according to the inflow and outflow made after my inventory
It's works when i do this type of request with specific date to be sure to only have the last one inventory, but I do not want to have to do it
SELECT index.definition,
Sum(suivi_ammo.inventory) + Sum(suivi_ammo.entrance) - Sum(suivi_ammo.exit) AS Stock
FROM munition.suivi_ammo
INNER JOIN munition.index ON suivi_ammo.type_ammo = index.numero
WHERE date_event < '03/05/2019' AND date_event >= '2019-04-10'
GROUP BY index.definition;
I also tried to used last_value() window function but doesn't work.
Thx !

Regarding single column or composite primary key

Is it better to have a single column primary key, or use composite primary keys . I have examples below:
create table emp(
subsidiary_id smallint ,
Employee_id int ,
emp_name varchar,
constraint emp_pkey primary key ( subsidiary_id , Employee_id )
Data will be line
subsidiary_id , employee_id
1, 1
1, 2
1, 3
2, 4
3, 5
2, 6
employee id cannot be same for any subsidiary , always unique but on my reports using both the column in where clause as subsdiary_id = 1 and employee_id = 1 or and so on
so want to know to better approach to create primary on employee_id column or both which one is better?
It sounds like a composite primary key is the right solution for you:
CREATE TABLE emp (
subsidiary_id smallint NOT NULL,
employee_id int NOT NULL,
emp_name text,
CONSTRAINT emp_pkey PRIMARY KEY (subsidiary_id, employee_id)
);
The index that is created for this constraint will also be useful for queries where only subsidiary_id appears in the WHERE condition.

"polymorphism" for FOREIGN KEY constraints

There is this field in a table:
room_id INT NOT NULL CONSTRAINT room_id_ref_room REFERENCES room
I have three 2 tables for two kinds of rooms: standard_room and family_room
How to do something like this:
room_id INT NOT NULL CONSTRAINT room_id_ref_room REFERENCES standard_room or family_room
I mean, room_id should reference either standard_room or family_room.
Is it possible to do so?
Here is the pattern I've been using.
CREATE TABLE room (
room_id serial primary key,
room_type VARCHAR not null,
CHECK CONSTRAINT room_type in ("standard_room","family_room"),
UNIQUE (room_id, room_type)
);
CREATE_TABLE standard_room (
room_id integer primary key,
room_type VARCHAR not null default "standard_room",
FOREIGN KEY (room_id, room_type) REFERENCES room (room_id, room_type),
CHECK CONSTRAINT room_type = "standard_room"
);
CREATE_TABLE family_room (
room_id integer primary key,
room_type VARCHAR not null default "family_room",
FOREIGN KEY (room_id, room_type) REFERENCES room (room_id, room_type),
CHECK CONSTRAINT room_type = "family_room"
);
That is, the 'subclasses' point at the super-class, by way of a type descriminator column (such that the pointed to base class is of the correct type, and that primary key of the super class is the same as the child classes.
Here's the same SQL from the accepted answer that works for PostGres 12.8. There's a few issues not only the CREATE_TABLE syntax mistake:
CREATE TABLE room (
room_id serial primary key,
room_type VARCHAR not null,
CONSTRAINT room_in_scope CHECK (room_type in ('standard_room','family_room')),
CONSTRAINT unique_room_type_combo UNIQUE (room_id, room_type)
);
CREATE TABLE standard_room (
room_id integer primary key,
room_type VARCHAR not null default 'standard_room',
CONSTRAINT roomid_std_roomtype_fk FOREIGN KEY (room_id, room_type) REFERENCES public."room" (room_id, room_type),
CONSTRAINT std_room_constraint CHECK (room_type = 'standard_room')
);
CREATE TABLE family_room (
room_id integer primary key,
room_type VARCHAR not null default 'family_room',
CONSTRAINT roomid_fam_roomtype_fk FOREIGN KEY (room_id, room_type) REFERENCES "room" (room_id, room_type),
CONSTRAINT fam_room_constraint CHECK (room_type = 'family_room')
);
NOTE: The SQL above uses constraints to enforce the child room_type values default to the parent tables' room_type values: 'standard_room' or 'family_room'.
PROBLEM: Since the child tables Primary Key's expect either the standard and family room Primary Key that means you can't insert more than one record in thsee two child tables.
insert into room (room_type) VALUES ('standard_room'); //Works
insert into room (room_type) values ('family_room'); //Works
insert into standard_room (room_id,pictureAttachment) VALUES (1,'Before Paint'); //Works
insert into standard_room (room_id,pictureAttachment) VALUES (1,'After Paint'); //Fails
insert into standard_room (room_id,pictureAttachment) VALUES (1,'With Furniture');
insert into family_room (room_id,pictureAttachment) VALUES (2, 'Beofre Kids'); //Works
insert into family_room (room_id,pictureAttachment) VALUES (2,'With Kids'); //Fails
To make the tables accept > 1 row you have to remove the Primary Keys from the 'standard_room' and 'family_room' tables which is BAD database design.
Despite 26 upvotes I will ping OP about this as I can see the answer was typed free hand.
Alternate Solutions
For smallish tables with less than a handful of variations a simple alterative is a single table with Bool columns for different table Primary Key fields.
Single Table "Room"
Id
IsStandardRoom
IsFamilyRoom
Desc
Dimensions
1
True
False
Double Bed, BIR
3 x 4
2
False
True
3 Set Lounge
5.5 x 7
SELECT * FROM Room WHERE IsStdRoom = true;
At the end of the day, in a relational database it's not very common to be adding Room Types when it involves creating the necessary related database tables using DDL commands (CREATE, ALTER, DROP).
A typical future proof database design allowing for more Tables would look something like this:
Multi Many-To-Many Table "Room"
Id
TableName
TableId
1
Std
8544
2
Fam
236
3
Std
4351
Either Standard or Family:
select * from standard_room sr where sr.room_id in
(select TableId from room where TableName = 'Std');
select * from family_room fr where fr.room_id in
(select id from room where TableName = 'Fam');
Or both:
select * from standard_room sr where sr.room_id in
(select TableId from room where TableName = 'Std')
UNION
select * from family_room fr where fr.room_id in
(select id from room where TableName = 'Fam');
Sample SQL to demo Polymorphic fields:
If you want to have different Data Types in the polymorphic foreign key fields then you can use this solution. Table r1 stores a TEXT column, r2 stores a TEXT[] Array column and r3 a POLYGON column:
CREATE OR REPLACE FUNCTION null_zero(anyelement)
RETURNS INTEGER
LANGUAGE SQL
AS $$
SELECT CASE WHEN $1 IS NULL THEN 0 ELSE 1 END;
$$;
CREATE TABLE r1 (
r1_id SERIAL PRIMARY KEY
, r1_text TEXT
);
INSERT INTO r1 (r1_text)
VALUES ('foo bar'); --TEXT
CREATE TABLE r2 (
r2_id SERIAL PRIMARY KEY
, r2_text_array TEXT[]
);
INSERT INTO r2 (r2_text_array)
VALUES ('{"baz","blurf"}'); --TEXT[] ARRAY
CREATE TABLE r3 (
r3_id SERIAL PRIMARY KEY
, r3_poly POLYGON
);
INSERT INTO r3 (r3_poly)
VALUES ( '((1,2),(3,4),(5,6),(7,8))' ); --POLYGON
CREATE TABLE flex_key_shadow (
flex_key_shadow_id SERIAL PRIMARY KEY
, r1_id INTEGER REFERENCES r1(r1_id)
, r2_id INTEGER REFERENCES r2(r2_id)
, r3_id INTEGER REFERENCES r3(r3_id)
);
ALTER TABLE flex_key_shadow ADD CONSTRAINT only_one_r
CHECK(
null_zero(r1_id)
+ null_zero(r2_id)
+ null_zero(r3_id)
= 1)
;
CREATE VIEW flex_key AS
SELECT
flex_key_shadow_id as Id
, CASE
WHEN r1_id IS NOT NULL THEN 'r1'
WHEN r2_id IS NOT NULL THEN 'r2'
WHEN r3_id IS NOT NULL THEN 'r3'
ELSE 'wtf?!?'
END AS "TableName"
, CASE
WHEN r1_id IS NOT NULL THEN r1_id
WHEN r2_id IS NOT NULL THEN r2_id
WHEN r3_id IS NOT NULL THEN r3_id
ELSE NULL
END AS "TableId"
FROM flex_key_shadow
;
INSERT INTO public.flex_key_shadow (r1_id,r2_id,r3_id) VALUES
(1,NULL,NULL),
(NULL,1,NULL),
(NULL,NULL,1);
SELECT * FROM flex_key;

postgres getting data where two column references same parent id

There are 3 table in postgres database
CREATE TABLE tab_name
(
name_id integer NOT NULL,
cust_name character varying NOT NULL, -- contains names like david,jones,athur
CONSTRAINT tab_name_pkey PRIMARY KEY (name_id)
)
CREATE TABLE tab_rel
(
rel_id integer NOT NULL,
rel_desc character varying NOT NULL,-- contains relation description father son, sister brother
CONSTRAINT tab_rel_pkey PRIMARY KEY (rel_id)
)
CREATE TABLE tab_rel_map
(
rel_id integer NOT NULL,
name_id1 integer NOT NULL,
name_id2 integer NOT NULL,
CONSTRAINT tab_rel_map_name_id1_fkey FOREIGN KEY (name_id1)
REFERENCES tab_name (name_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT tab_rel_map_name_id2_fkey FOREIGN KEY (name_id2)
REFERENCES tab_name (name_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT tab_rel_map_rel_id_fkey FOREIGN KEY (rel_id)
REFERENCES tab_rel (rel_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
I am trying to write function which take rel_id as input and should output cust_name respect to name_id1 and name_id2. Because both name_id1 and name_id2 references to same parent id I am not able to get respective name.
rel_id | relation | cust_name1 | cust_name2
------------------------------------------------
1 | Father son | David | Jones
You need to join to the same table twice - here's how you do that using aliases:
select
rm.rel_id,
r.rel_desc as relation,
n1.cust_name as cust_name1,
n2.cust_name as cust_name2
from tab_rel_map rm
join tab_rel r on r.id = rm.rel_id
left join tab_name n1 on n1.name_id = rm.name_id1
left join tab_name n2 on n2.name_id = rm.name_id2
where rm.rel_id = 1;