Looking up values from many tables based on value in each column - postgresql

I have several tables containing key value pairs for differint fields in my database. I also have a table that that contains the keys of these differint tables that should be selected as the value for that key. However, I can't figure out how to select these values from the multiple tables?
The tables
CREATE TABLE CHARACTERS(
ID INTEGER PRIMARY KEY,
NAME VARCHAR(64)
);
CREATE TABLE MEDIA(
ID INTEGER PRIMARY KEY,
NAME VARCHAR(64)
);
CREATE TABLE EPISODES(
ID INTEGER PRIMARY KEY,
MEDIAID INTEGER,
NAME VARCHAR(64)
);
-- Selecting from this table
CREATE TABLE APPS(
ID INTEGER PRIMARY KEY,
CHARID INTEGER,
EPISODEID INTEGER,
MEDIAID INTEGER
);
I am selecting from the APPS table, and I want to replace the value of the *ID columns with the value of the name in the accomping table's NAME column. I want this done for each row in the APPS table. Like so...
CHARID -> CHARACTERS.NAME
EPISODEID -> EPISODES.NAME
MEDIAID -> MEDIA.NAME
I have tried to use joins, but they don't do it for each row in the APPS table. I have 18 rows in the APPS table, but I only get back way less than I have in the table or way more than I have in the table. So how can I make it do it for each row in the APPS table?

You do by JOINing the tables together and selecting the desired columns from the individual tables:
SELECT c.name AS character_name, e.name AS episode, m.name AS media
FROM apps a
LEFT JOIN episodes e ON e.id = a.episodeid
LEFT JOIN media m ON m.id = a.mediaid
LEFT JOIN characters c ON c.id = a.charid;
If you want to present the rows in a specific order, you can specify that too as a final clause in the SELECT statement. You can use any field from the included tables; that field is not necessarily part of the columns selected:
ORDER BY a.id -- order by apps.id
or
ORDER BY e.id, c.name -- order first by episode id, then by character name
etc

Related

How to update table on postgres with join statement

I have three tables on postgresql DB, and tried to update table but I failed to get result what I want. Please help me with getting valid result.
The first table is "employee".
On this table, the first three characters of "employee_id" mean employee type.
For example, employee_id="AA1-11111" is a member of employee_type="AA1".
employee_id
department
AA1-11111
A
AA1-22222
B
AB1-11111
A
The second table is "assessment".
On this table, assessment criteria is defined for (employee_type, department).
For example, an employee of employee_type="AA1" and department="A" will be evaluated by assessment_criteria="XX1X".
employee_type
department
assessment_criteria
AA1
A
XX1X
AA1
B
XX1Y
AA2
A
XX2X
The third table is "employee_assessment". On this table assessment_criteria for each employee is defined. (This table is calculated from "employee" and "assessment" by night batch processing.)
employee_id
department
assessment_criteria
AA1-11111
A
XX1X
AA1-22222
B
XX1Y
AB1-11111
A
Null
What I want to do is... to update "employee_assessment" table when "assessment" table is updated.
When "assessment" table is updated as like below...
employee_type
department
assessment_criteria
AA1
A
XX1X
AA1
B
NEW
AA2
A
Null
I want to update "employee_assessment" table like this.
employee_id
department
assessment_criteria
AA1-11111
A
XX1X
AA1-22222
B
NEW
AB1-11111
A
Null
I tried
UPDATE
employee_assessment
SET assessment_criteria=employee_assessment.assessment_criteria
FROM employee
LEFT JOIN (SELECT employee_id, LEFT(employee_id,3) as emp_type, department as emp_department from employee) as t1
ON
employee.employee_id=t1.employee_id
and
employee.department=t1.emp_department
left join assessment
on
t1.emp_type=assessment.employee_type
and
t1.emp_department=assessment.department;
But I got this result.
employee_id
department
assessment_criteria
AA1-11111
A
XX1X
AA1-22222
B
XX1X
AB1-11111
A
XX1X
My query seems to be wrong.
The actual cause of the problem is that you schema isn't properly normalized. Therefore you should solve this by fixing and normalizing your schema. Then you can simply use a view, that is "updated" automatically.
First have tables for the types and departments (unless you have that already (that's unclear)).
CREATE TABLE type
(id serial,
name varchar(64),
PRIMARY KEY (id));
CREATE TABLE department
(id serial,
name varchar(64),
PRIMARY KEY (id));
Then, in the table for the employees, just reference the types and departments. Don't have a column that actually are two columns, i.e. the type id has to have its own column and must not be concatenated to any other.
CREATE TABLE employee
(id serial,
type integer,
department integer,
given_name varchar(64),
surname varchar(64),
PRIMARY KEY (id),
FOREIGN KEY (type)
REFERENCES type
(id),
FOREIGN KEY (department)
REFERENCES department
(id));
In the table for the assessments reference the types and departments too.
CREATE TABLE assessment
(id serial,
type integer,
department integer,
name varchar(64),
criteria varchar(64),
PRIMARY KEY (id),
FOREIGN KEY (type)
REFERENCES type
(id),
FOREIGN KEY (department)
REFERENCES department
(id));
Now you can create view for the employee assessments that joins the data from the other tables and is always up to date. There's no need for any manual UPDATE.
CREATE VIEW employee_assessment
AS
SELECT e.id employee_id,
e.department employee_department,
a.criteria assessment_criteria
FROM employee e
LEFT JOIN assessment a
ON a.type = e.type
AND a.department = e.department;
A view also has the advantage that it cannot contain inconsistent data as the table you have now could.

How to get multiple table data in one query in posgresql JSONB data type

How can I Fetch table data in one query? I have below tables:
Tabel Name: calorieTracker
Creat Table calorieTracker(c_id serial NOT NULL PRIMARY KEY, caloriesConsumption jsonb);
INSERT INTO public."calorieTracker" ("caloriesConsumption")
VALUES ('[{"C_id":"1",,"calorie":88,"date":"19/08/2020"},{"C_id":2,"date":"19/08/2020","calorie":87}]');
Table Name: watertracker
create table watertracker(wt_id serial not null primary key, wt_date varchar, wt_goal float,wt_cid int);
INSERT INTO public.watertracker (wt_id,wt_date,wt_goal,wt_cid)
VALUES (2,'2020-08-19',5.5,2);
What I am looking here I want to write query where date is 19/08/2020(in calorieTracker table and water tracker table) and wt_cid is 2(water tracker table) and c_id is 2(calorieTracker table) then return data.
As you have not mentioned what output you want, so i am assuming you want JSON object from caloriesConsumption which matches the condition mentioned in the question:
based on above assumption try this query:
with cte as (
select
c_id,
jsonb_array_elements("caloriesConsumption") "data"
from "calorieTracker"
)
select
t1.*
from cte t1 inner join watertracker t2
on t2.wt_cid=cast(t1.data->>'c_id' as int)
and t2.wt_date=t1.data->>'date'
if you want the result from watertracker then just replace t1.* with t2.*.

Retrieve values from 2 tables given a value in a third map/join table

I have a table for lawyers:
CREATE TABLE lawyers (
id SERIAL PRIMARY KEY,
name VARCHAR,
name_url VARCHAR,
pic_url VARCHAR(200)
);
Imagine the whole table looks like this:
And a table for firms:
CREATE TABLE firms (
id SERIAL PRIMARY KEY,
name VARCHAR,
address JSONb
);
Whole table:
Then to map many to many relationship I'm using a map table lawyers_firms:
CREATE TABLE lawyers_firms (
lawyer_id INTEGER,
firm_id INTEGER
);
I'm not sure how to retrieve values from lawyersand from firmsgiven a lawyers_firms.firm_id.
For example:
1. SELECT name, name_url and pic_url FROM lawyers.
2. also SELECT name and address FROM firms.
3. WHERE `lawyers_firms.firm_id` = 1.
Try this:
SELECT l.name, l.name_url, l.pic_url, f.name, f.address
FROM lawyers l
inner join lawyers_firms lf
on lf.lawyer_id = l.id
inner join firms f
on f.id = lf.firm_id
WHERE lf.firm_id = 1;

Merging columns from 2 different tables to apply aggregate function

I have below 3 Tables
Create table products(
prod_id character(20) NOT NULL,
name character varying(100) NOT NULL,
CONSTRAINT prod_pkey PRIMARY KEY (prod_id)
)
Create table dress_Sales(
prod_id character(20) NOT NULL,
dress_amount numeric(7,2) NOT NULL,
CONSTRAINT prod_pkey PRIMARY KEY (prod_id),
CONSTRAINT prod_id_fkey FOREIGN KEY (prod_id)
REFERENCES products (prod_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Create table sports_Sales(
prod_id character(20) NOT NULL,
sports_amount numeric(7,2) NOT NULL,
CONSTRAINT prod_pkey PRIMARY KEY (prod_id),
CONSTRAINT prod_id_fkey FOREIGN KEY (prod_id)
REFERENCES products (prod_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
I want to get the Sum and Average sales amount form both the tables(Only for the Selected Prod_id). I have tried the below code but it's not producing any value.
select sum(coalesce(b.dress_amount, c.sports_amount)) as total_Amount
from products a JOIN dress_sales b on a.prod_id = b.prod_id
JOIN sports_sales c on a.prod_id = c.prod_id and a.prod_id = ANY( {"123456","456789"}')`
Here 1000038923 is in dress_sales table and 8002265822 is in sports_sales.
Looks like your product can exist in only one table (dress_sales or sports_sales).
In this case you should use left join:
select
sum(coalesce(b.dress_amount, c.sports_amount)) as total_amount,
avg(coalesce(b.dress_amount, c.sports_amount)) as avg_amount
from products a
left join dress_sales b using(prod_id)
left join sports_sales c using(prod_id)
where
a.prod_id in ('1', '2');
If you use inner join (which is default) the product row will not appear in the result set as it will not be joined with either dress_sales or sports_sales.
If you have a product that appears in both tables you can use a subquery that can handle both dress_amount and sports_amount values.
select sum(combined.amount), avg(combined.amount)
from
(select prod_id, dress_amount as amount from dress_sales
union all
select prod_id, sports_amount as amount from sports_sales) combined
where
combined.prod_id in ('1','2');

How to implicitly insert SERIAL ID via view over more than one table

I have two tables, connected in E/R by a is-relation. One representing the "mother table"
CREATE TABLE PERSONS(
id SERIAL NOT NULL,
name character varying NOT NULL,
address character varying NOT NULL,
day_of_creation timestamp NOT NULL DEFAULT current_timestamp,
PRIMARY KEY (id)
)
the other representing the "child table"
CREATE TABLE EMPLOYEES (
id integer NOT NULL,
store character varying NOT NULL,
paychecksize integer NOT NULL,
FOREIGN KEY (id)
REFERENCES PERSONS(id),
PRIMARY KEY (id)
)
Now those two tables are joined in a view
CREATE VIEW EMPLOYEES_VIEW AS
SELECT
P.id,name,address,store,paychecksize,day_of_creation
FROM
PERSONS AS P
JOIN
EMPLOYEES AS E ON P.id = E.id
I want to write either a rule or a trigger to enable a db user to make an insert on that view, sparing him the nasty details of the splitted columns into different tables.
But I also want to make it convenient, as the id is a SERIAL and the day_of_creation has a default value there is no actual need that a user has to provide those, therefore a statement like
INSERT INTO EMPLOYEES_VIEW (name, address, store, paychecksize)
VALUES ("bob", "top secret", "drugstore", 42)
should be enough to result in
PERSONS
id|name|address |day_of_creation
-------------------------------
1 |bob |top secret| 2013-08-13 15:32:42
EMPLOYEES
id| store |paychecksize
---------------------
1 |drugstore|42
A basic rule would be easy as
CREATE RULE EMPLOYEE_VIEW_INSERT AS ON INSERT TO EMPLOYEE_VIEW
DO INSTED (
INSERT INTO PERSONS
VALUES (NEW.id,NEW.name,NEW.address,NEW.day_of_creation),
INSERT INTO EMPLOYEES
VALUES (NEW.id,NEW.store,NEW.paychecksize)
)
should be sufficient. But this will not be convenient as a user will have to provide the id and timestamp, even though it actually is not necessary.
How can I rewrite/extend that code base to match my criteria of convenience?
Something like:
CREATE RULE EMPLOYEE_VIEW_INSERT AS ON INSERT TO EMPLOYEES_VIEW
DO INSTEAD
(
INSERT INTO PERSONS (id, name, address, day_of_creation)
VALUES (default,NEW.name,NEW.address,default);
INSERT INTO EMPLOYEES (id, store, paychecksize)
VALUES (currval('persons_id_seq'),NEW.store,NEW.paychecksize)
);
That way the default values for persons.id and persons.day_of_creation will be the default values. Another option would have been to simply remove those columns from the insert:
INSERT INTO PERSONS (name, address)
VALUES (NEW.name,NEW.address);
Once the rule is defined, the following insert should work:
insert into employees_view (name, address, store, paychecksize)
values ('Arthur Dent', 'Some Street', 'Some Store', 42);
Btw: with a current Postgres version an instead of trigger is the preferred way to make a view updateable.