Merging columns from 2 different tables to apply aggregate function - postgresql

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');

Related

Postgres: change primary key in existing table

I have a situation with two tables where one has a foreign key pointing to the other table (simplified) schema:
CREATE TABLE table1 (
name VARCHAR(64) NOT NULL,
PRIMARY KEY(name)
);
CREATE TABLE table2 (
id SERIAL PRIMARY KEY,
table1_name VARCHAR(64) NOT NULL REFERENCES table1(name)
);
Now I regret using the name column as primary key in table1 - and would like to add integer serial key instead. Since I already have data in the database I guess I need to do this carefully. My current plan is as follows:
Drop the foreign key constraint: table2(name) with ALTER TABLE table2 DROP CONSTRAINT table2_table1_name_fkey;
Drop the primary key constraint on table1(name) with ALTER TABLE table1 DROP CONSTRAINT name_pkey;.
Add a unique constraint on table1(name) with ALTER TABLE table1 ADD UNIQUE(name);
Add a automatic primary key to table1 with ALTER TABLE table1 ADD COLUMN ID SERIAL PRIMARY KEY;.
Add a new column table1_id to table2 with ALTER TABLE table2 ADD COLUMN table1_id INT;
Update all rows in table2 - so that the new column (which will be promoted to a foreign key) gets the correct value - as inferred by the previous (still present) foreign key table1_name.
I have completed steps up to an including step 5, but the UPDATE (with JOIN?) required to complete 6 is beyond my SQL paygrade. My current (google based ...) attempt looks like:
UPDATE
table2
SET
table2.table1_id = t1.id
FROM
table1 t1
LEFT JOIN table2 t2
ON t2.table1_name = t1.name;
You do not need JOIN in UPDATE.
UPDATE
table2 t2
SET
table1_id = t1.id
FROM
table1 t1
WHERE
t2.table1_name = t1.name;

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 group by an attribute and order by date

I have two tables:
Medics
CREATE TABLE "medic" (
"id" BIGINT NOT NULL,
"name" CHARACTER VARYING(255) NOT NULL,
PRIMARY KEY ("id")
);
Comments
CREATE TABLE IF NOT EXISTS "comment" (
"id" BIGINT NOT NULL,
"medic_id" BIGINT NOT NULL,
"comment" CHARACTER VARYING(1024) NOT NULL,
"created_at" TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now(),
CONSTRAINT pk_comment PRIMARY KEY (id),
CONSTRAINT fk_comment_medic FOREIGN KEY (medic_id)
REFERENCES medic(id) ON UPDATE NO ACTION ON DELETE NO ACTION
);
Now I want to get medic_id, name, comments_count and all ordered by created_at
Here's what I've tried so far:
SELECT m.id, m.name, COUNT(c.id)
FROM COMMENT AS c
JOIN medic AS m ON m.id = c.medic_id
GROUP BY m.id, m.name, c.created_at
ORDER BY c.created_at DESC
But obviously this can't work because it makes no sense to group by date although I have to do it when I want to order by date.
Another appraoch was to work with window functions. Particularly rank() over (partition by m.id order by c.created_at desc). But in this case I lose the ordering over all records.
Here's some SQLFiddle.
I am using Postgres 9.3
I'm guessing you want to order by the most recent comment date:
SELECT m.id, m.name, COUNT(c.id)
FROM COMMENT c JOIN
medic m
ON m.id = c.medic_id
GROUP BY m.id, m.name
ORDER BY MAX(c.created_at) DESC;

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

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

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;