Select prices/sizes from tables using joiner table - sql

I am trying to create a product page that shows the product info, as well as prices and sizes. Each product will have multiple prices and sizes.
I have multiple tables (products, sizes, prices) and I am using a "joiner" table that only has those tables as foreign keys. This is where I am getting the most confused.
SCHEMA
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name TEXT,
description TEXT,
);
CREATE TABLE sizes (
id SERIAL PRIMARY KEY,
height INT,
width INT,
garmentSize TEXT
);
CREATE TABLE prices (
id SERIAL PRIMARY KEY,
price INT
);
CREATE TABLE product_price_size ( --joiner table
id SERIAL PRIMARY KEY,
productId INTEGER REFERENCES products(id),
priceId INTEGER REFERENCES prices(id),
sizeId INTEGER REFERENCES sizes(id)
);
INSERT INTO products (name, description)
VALUES ('Take A Hike', 'Take A Hike vinyl decal');
INSERT INTO products (name, description)
VALUES ('Wanderlust', 'Wanderlust vinyl decal');
INSERT INTO prices (price) VALUES (4);
INSERT INTO prices (price) VALUES (6);
INSERT INTO prices (price) VALUES (8);
INSERT INTO sizes (height, width, garmentSize) VALUES (3, 3, null);
INSERT INTO sizes (height, width, garmentSize) VALUES (4, 5, null);
INSERT INTO sizes (height, width, garmentSize) VALUES (7, 2, null);
INSERT INTO product_price_size (productId, priceId, sizeid) VALUES (1, 1, 1);
INSERT INTO product_price_size (productId, priceId, sizeid) VALUES (1, 2, 2);
INSERT INTO product_price_size (productId, priceId, sizeid) VALUES (1, 3, 3);
INSERT INTO product_price_size (productId, priceId, sizeid) VALUES (2, 2, 3);
INSERT INTO product_price_size (productId, priceId, sizeid) VALUES (2, 2, 2);
INSERT INTO product_price_size (productId, priceId, sizeid) VALUES (2, 1, 3);
I need to get each product to show multiple sizes that has a price associated with it, and I am not sure how get that done. I am using Angular to get to "mysite.com/products/2" which in this example is getting the 2nd product from the products table, so if I currently go to the products page, an error will occur because there are currently three "product.id"s that reference product id 2.
QUERY
SELECT products.name, prices.price, sizes.height, sizes.width FROM products
INNER JOIN product_price_size ON products.id = product_price_size.productId
INNER JOIN prices ON prices.id = product_price_size.priceId
INNER JOIN sizes ON sizes.id = product_price_size.sizeId
I think I might need a subquery, but I'll admit I am lost on this.
Please see SQL Fiddle here
I hope this isn't too confusing, and any thoughts or even better ideas for the way I'm doing it are very appreciated. Thanks!!
Keith

Related

How to create a column at psql after join two tables and multiply a quantity by row categories?

I've created two tables and inserted values, that's ok:
CREATE TABLE categories (
id numeric PRIMARY KEY,
name varchar
);
CREATE TABLE products (
id numeric PRIMARY KEY,
name varchar(50),
amount numeric,
price numeric(7,2),
id_categories numeric REFERENCES categories (id)
);
INSERT INTO categories (id, name)
VALUES
(1, 'wood'),
(2, 'luxury'),
(3, 'vintage'),
(4, 'modern'),
(5, 'super luxury');
INSERT INTO products (id, name, amount, price, id_categories)
VALUES
(1, 'Two-doors wardrobe', 100, 800, 1),
(2, 'Dining table', 1000, 560, 3),
(3, 'Towel holder', 10000, 25.50, 4),
(4, 'Computer desk', 350, 320.50, 2),
(5, 'Chair', 3000, 210.64, 4),
(6, 'Single bed', 750, 460, 1);
By now, I have to display a sum based on categories repetition, like this:
I am trying it, but I've been facing errors such as aggregate function not allowed in where:
ALTER TABLE
categories
ADD COLUMN
sum INT;
SELECT
categories.name, categories.sum
FROM
categories
JOIN
products
ON categories.id = id_categories
WHERE
categories.sum = products.amount * COUNT(categories.name);
You have to add group by categories.name column to your query.
SELECT
categories.name, sum(products.amount) AS category_sum
FROM
categories
JOIN
products
ON categories.id = id_categories
GROUP BY
categories.name
Returns:
name
category_sum
luxury
350
modern
13000
vintage
1000
wood
850

Find all records in a table where the most recent record in a join table has a column which is not equal to one is this table?

Image the tables:
CREATE TABLE items (
id INT,
price INT
);
CREATE TABLE item_price_history (
id INT,
item_id INT,
price INT
);
With the following data:
INSERT INTO items (id, price) VALUES (1, 199);
INSERT INTO items (id, price) VALUES (2, 159);
INSERT INTO items (id, price) VALUES (3, 129);
INSERT INTO items (id, price) VALUES (4, 119);
INSERT INTO item_price_history (id, item_id, price) VALUES (1, 1, 249);
INSERT INTO item_price_history (id, item_id, price) VALUES (2, 1, 239);
INSERT INTO item_price_history (id, item_id, price) VALUES (3, 1, 229);
INSERT INTO item_price_history (id, item_id, price) VALUES (4, 1, 199);
INSERT INTO item_price_history (id, item_id, price) VALUES (5, 2, 299);
INSERT INTO item_price_history (id, item_id, price) VALUES (6, 2, 259);
INSERT INTO item_price_history (id, item_id, price) VALUES (7, 2, 159);
INSERT INTO item_price_history (id, item_id, price) VALUES (8, 2, 109);
INSERT INTO item_price_history (id, item_id, price) VALUES (9, 3, 129);
INSERT INTO item_price_history (id, item_id, price) VALUES (10, 4, 159);
INSERT INTO item_price_history (id, item_id, price) VALUES (11, 4, 119);
INSERT INTO item_price_history (id, item_id, price) VALUES (13, 4, 99);
Now I would like to find all the items for which the items.price is not equal to the most recent item_price_history.price (id DESC) in item_price_history. In this case that should produce item with id 3 and 4. As their prices does NOT match the most recent price in item_price_history.
I did the following which works:
SELECT items.id, items.price i_price, item_price_history.id, item_price_history.price as ih_price
FROM items
LEFT JOIN item_price_history ON item_price_history.item_id = items.id
AND item_price_history.id = (SELECT MAX(id) FROM item_price_history WHERE item_id = items.id)
WHERE items.price != item_price_history.price
LIMIT 100
However I have a table of about 2 million rows in items and 20 million rows in item_price_history which I need to scan through, so performance is important.
How would I write such a query in a more performant way e.g by using DISTINCT ON or something?
https://www.db-fiddle.com/f/wPkdHvvzS2tmZq2vKUwgNi/2
Here is a query to do it in bulk with DISTINCT ON:
SELECT items.id, items.price i_price, item_price_history.id, item_price_history.price as ih_price
from items
join (select distinct on (item_id) id, item_id, price from item_price_history order by item_id desc, id desc) item_price_history
on item_price_history.item_id = items.id
WHERE items.price != item_price_history.price;
It should be well supported by an index on item_price_history (item_id , id, price); which will let it do a presorted index-only scan.

Can I reference this table?

I am trying to show amount paid for each tutor sorted by month and then by tutor id. I have the first part correct and can sort by month but cannot sort by tutor id because it is from a different table.
Here is the script for my tables:
create table match_history
(match_id number(3),
tutor_id number(3),
student_id number(4),
start_date date,
end_date date,
constraint pk_match_history primary key (match_id),
constraint fk1_match_history foreign key (tutor_id) references tutor(tutor_id),
constraint fk2_match_history foreign key (student_id) references student(student_id));
create table tutor_report
(match_id number(3),
month date,
hours number(3),
lessons number(3),
constraint pk_tutor_report primary key (match_id, month),
constraint fk1_tutor_report foreign key (match_id) references match_history(match_id));
insert into tutor values (100, '05-JAN-2017', 'Active');
insert into tutor values (101, '05-JAN-2017', 'Temp Stop');
insert into tutor values (102, '05-JAN-2017', 'Dropped');
insert into tutor values (103, '22-MAY-2017', 'Active');
insert into tutor values (104, '22-MAY-2017', 'Active');
insert into tutor values (105, '22-MAY-2017', 'Temp Stop');
insert into tutor values (106, '22-MAY-2017', 'Active');
insert into student values (3000, 2.3);
insert into student values (3001, 5.6);
insert into student values (3002, 1.3);
insert into student values (3003, 3.3);
insert into student values (3004, 2.7);
insert into student values (3005, 4.8);
insert into student values (3006, 7.8);
insert into student values (3007, 1.5);
insert into match_history values (1, 100, 3000, '10-JAN-2017', null);
insert into match_history values (2, 101, 3001, '15-JAN-2017', '15-MAY-2017');
insert into match_history values (3, 102, 3002, '10-FEB-2017', '01-MAR-2017');
insert into match_history values (4, 106, 3003, '28-MAY-2017', null);
insert into match_history values (5, 103, 3004, '01-JUN-2017', '15-JUN-2017');
insert into match_history values (6, 104, 3005, '01-JUN-2017', '28-JUN-2017');
insert into match_history values (7, 104, 3006, '01-JUN-2017', null);
insert into tutor_report values (1, '01-JUN-2017', 8, 4);
insert into tutor_report values (4, '01-JUN-2017', 8, 6);
insert into tutor_report values (5, '01-JUN-2017', 4, 4);
insert into tutor_report values (4, '01-JUL-2017', 10, 5);
insert into tutor_report values (1, '01-JUL-2017', 4, 2);
This is what I have so far:
Select (hours * 10) as amount paid from tutor_report group by month, tutor_id
however obviously I cannot just say tutor_id at the end.
You can join match_history to get the tutor_id.
But your statement and the query don't match. If you want to sort use ORDER BY.
SELECT tr.hours * 10 amount_paid
FROM tutor_report tr
INNER JOIN match_history mh
ON mh.match_id = tr.match_id
ORDER BY tr.month,
mh.tutor_id;
If you want to aggregate, hours needs to be argument to some aggregation function. Maybe you're after the sum of hours?
SELECT sum(tr.hours) * 10 amount_paid
FROM tutor_report tr
INNER JOIN match_history mh
ON mh.match_id = tr.match_id
GROUP BY tr.month,
mh.tutor_id;
If you are grouping based on columns on two tables,you need to join them on the matching Id and then use group by
Select (hours * 10) as amount paid
from tutor_report a
join match_history b on a. match_id = b.match_id
group by month, tutor_id

Running an SQL Script file and having issues printing the Current_Orders table cant understand the errors

I have to prepare and execute a script file to create and populate a relational database in Oracle with data about customers, current orders, and products. These are the relationships: Each customer can place any number of current orders and each current order is placed by one customer.Each current order is for one product but each product can be in any number of
current orders.
I can't figure out the errors when running this script. The Customer and Products table are successfully filled, but the Current_Orders table is not. There are a few error codes that appear when I run it but I am not understanding them as I am fairly new to SQL. Any help would be appreciated. Thanks! ORA-00001, ORA-00955, ORA-02449.
drop table Customer;
drop table Current_Orders;
drop table Products;
create table Customer
(Customer_Number integer not null,
Customer_Name char(20) not null,
Customer_City char(20) not null,
PRIMARY KEY(Customer_Number));
create table Products
(Product_Number integer not null,
Product_Description char(20) not null,
Unit_Price integer not null,
PRIMARY KEY (Product_Number));
create table Current_Orders
(Order_Number integer not null,
Order_Date date not null,
Shipping_Method char(20) not null,
Product_Number integer not null,
Quantity_Ordered integer not null,
Customer_Number integer not null,
PRIMARY KEY(Order_Number), FOREIGN KEY(Customer_Number)
references Customer(Customer_Number) ON DELETE CASCADE,
FOREIGN KEY(Product_Number) references Products(Product_Number)
ON DELETE CASCADE);
insert into Customer
values (1, 'Khizur Sheikh', 'Milpitas');
insert into Customer
values (2, 'Fatima Sheikh', 'Fremont');
insert into Customer
values (3, 'Syed Sheikh', 'Davis');
insert into Customer
values (4, 'Mohammed Sheikh', 'Oakland');
insert into Customer
values (5, 'Ali Sheikh', 'Dublin');
insert into Products
values (6, 'iphone', 300);
insert into Products
values (7, 'ipad', 400);
insert into Products
values (8, 'imac', 500);
insert into Products
values (9, 'ipod', 600);
insert into Products
values (10, 'ihome', 700);
insert into Products
values (11, 'apple', 800);
insert into Products
values (12, 'banana', 900);
insert into Products
values (13, 'orange', 1000);
insert into Products
values (14, 'grape', 1100);
insert into Products
values (15, 'avocado', 1200);
insert into Products
values (16, 'bread', 1300);
insert into Products
values (17, 'muffin', 1400);
insert into Products
values (18, 'cheese', 1500);
insert into Products
values (19, 'milk', 1600);
insert into Products
values (20, 'brownies', 1700);
insert into Products
values (21, 'candy', 1800);
insert into Products
values (22, 'soup', 1900);
insert into Products
values (23, 'strawberry', 11000);
insert into Products
values (24, 'cookies', 50);
insert into Products
values (25, 'chocolate', 10);
insert into Current_Orders
values (26, '22-oct-2017', 'truck', 6, 1, 1);
insert into Current_Orders
values (27, '22-nov-2017', 'ship', 7, 1, 2);
insert into Current_Orders
values (28, '22-dec-2017', 'train', 8, 3, 3);
insert into Current_Orders
values (29, '22-jan-2017', 'truck', 9, 2, 4);
insert into Current_Orders
values (30, '22-feb-2017', 'train', 10, 1, 5);
insert into Current_Orders
values (31, '22-mar-2017', 'truck', 12, 4, 2);
insert into Current_Orders
values (32, '22-apr-2017', 'plane', 17, 7, 4);
insert into Current_Orders
values (33, '22-may-2017', 'train', 19, 1, 5);
insert into Current_Orders
values (34, '22-jun-2017', 'ship', 22, 3, 2);
insert into Current_Orders
values (35, '22-jan-2017', 'ship', 21, 4, 3);
commit;
All the commands in your script work fine while running for the first time. However Issue seems to occur when you run the DROP TABLE statements on second and successive executions when table exists and has data.
drop table Customer;
drop table Current_Orders;
Error report - ORA-02449: unique/primary keys in table referenced by
foreign keys
02449. 00000 - "unique/primary keys in table referenced by foreign keys"
*Cause: An attempt was made to drop a table with unique or
primary keys referenced by foreign keys in another table.
*Action: Before performing the above operations the table, drop the
foreign key constraints in other tables.
I see you have defined a FOREIGN KEY on the column Customer_Number
FOREIGN KEY(Customer_Number)
references Customer(Customer_Number) ON DELETE CASCADE
So, when you try to DROP from Table Customer before Current_Orders , you are basically deleting parent record before deleting the child record which is not allowed. ORA-00955 and ORA-00001 occur as a consequence.
So, follow this order to drop tables in your script.
drop table Current_Orders;
drop table Products;
drop table Customer;

How to add a column to a table based on information found in other columns

I would like to be able to add a column to a table, but I would need this new column to take in values of other columns in the table. Here is my code:
CREATE TABLE grocery_price (id INTEGER PRIMARY KEY, name TEXT, price FLOAT);
INSERT INTO grocery_price VALUES(1, 'Bananas', 1.50);
INSERT INTO grocery_price VALUES(2, 'Peanut Butter', 1.00);
INSERT INTO grocery_price VALUES(3, 'Dark Chocolate Bars', 1.50);
INSERT INTO grocery_price VALUES(4, 'Ice cream', 2.50);
INSERT INTO grocery_price VALUES(5, 'Cherries', 0.50);
INSERT INTO grocery_price VALUES(6, 'Chocolate syrup', 1.25);
SELECT * FROM grocery_price
CREATE TABLE Grocery (id INTEGER PRIMARY KEY, name TEXT, quantity INTEGER, aisle INTEGER);
INSERT INTO Grocery VALUES(1, 'Bananas', 4, 7);
INSERT INTO Grocery VALUES(2, 'Peanut Butter', 1, 2);
INSERT INTO Grocery VALUES(3, 'Dark Chocolate Bars', 2, 2);
INSERT INTO Grocery VALUES(4, 'Ice cream', 1, 12);
INSERT INTO Grocery VALUES(5, 'Cherries', 6, 2);
INSERT INTO Grocery VALUES(6, 'Chocolate syrup', 1, 4);
SELECT * FROM Grocery
I want to create a new column in Grocery called total price that would take the values found in grocery_price.price and Grocery.quantity and multiply them together. Can I do this without having to convert the values from one column to another? Thanks.
I suggest you pick more meaningful names for your tables and normalize them (don't duplicate data when possible). Also link tables by having foreign keys. After doing these things your sql will look like this.
CREATE TABLE product (id INTEGER PRIMARY KEY, name TEXT, price FLOAT);
INSERT INTO product VALUES(1, 'Bananas', 1.50);
INSERT INTO product VALUES(2, 'Peanut Butter', 1.00);
INSERT INTO product VALUES(3, 'Dark Chocolate Bars', 1.50);
INSERT INTO product VALUES(4, 'Ice cream', 2.50);
INSERT INTO product VALUES(5, 'Cherries', 0.50);
INSERT INTO product VALUES(6, 'Chocolate syrup', 1.25);
SELECT * FROM product;
CREATE TABLE product_inventory (
id INTEGER PRIMARY KEY,
product_id INTEGER REFERENCES product (id),
quantity INTEGER,
aisle INTEGER,
total_price FLOAT);
INSERT INTO product_inventory VALUES(1, 1, 4, 7);
INSERT INTO product_inventory VALUES(2, 2, 1, 2);
INSERT INTO product_inventory VALUES(3, 3, 2, 2);
INSERT INTO product_inventory VALUES(4, 4, 1, 12);
INSERT INTO product_inventory VALUES(5, 5, 6, 2);
INSERT INTO product_inventory VALUES(6, 6, 1, 4);
update product_inventory as pi1 set total_price = (select p.price * pi.quantity as total from product p join product_inventory pi on p.id = pi.product_
id where p.id = pi1.id );
SELECT * FROM product_inventory;
The update statement is setting the total_price field in the product_inventory table.
select g.id, g.name, g.quantity, g.aisle, (g.quantity*gp.price) AS total_price
from grocery g left join grocery_price gp
on g.id=gp.id;
Add the new column to the table.
alter TABLE Grocery add totalPrice FLOAT;
Update the table by creating a join of both the tables and setting the total price to the product of quantity and price.
UPDATE Grocery gr
INNER JOIN grocery_price gp ON gr.id = gp.id
SET gr.totalPrice = CAST(gr.quantity as float) * gp.price;
Do you want to create a computed column or just populate the total column as a one time activity?
For a one time activity after creating the column, you can just update it:
Update Grocery
Set total = cast(g.quantity as float)*p.price
From grocery g
Inner join grocery_price p
On g.name = p.name...
Looks like you do not have a common reference column in grocery_price table...
If this is a one time activity you can execute an UPDATE:
UPDATE Grocery tb_gr
INNER JOIN grocery_price tb_gp ON tb_gr.id = tb_gp.id
SET tb_gr.totalPrice = tb_gr.quantity * tb_gp.price;
If the values of the Table will be periodically refreshed you can create a view on this table and execute the UPDATE.
The best solution is to use an update/view as others have mentioned .Just an alternate would be this :
select g.id,g.name,g.quantity,g.aisle,price*quantity as total_price
into #temp
from grocery_price p join grocery g
on p.id=g.id;
drop table grocery;
select * into grocery from #temp;
You should not do this
Yes, you could create a new column and update it to contain the information you want, but a far better approach would be to leave independent data stay independent.
As it is, your data isn't even independent, since you're repeating the name. So drop that column from your second table.
Use a view
Then, if you need to present all information in a single table, use a view.
CREATE VIEW groceries_with_totals
AS
SELECT
gp.id,
gp.name,
gp.price,
g.quantity,
g.quantity * gp.price AS total_price,
g.aisle
FROM grocery_price gp
JOIN grocery g ON gp.id = g.id;
I'm assuming here that grocery_price.id references grocery.id; you should add a foreign key constraint reflecting that relation.