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

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

Related

SQL to assign covid patients to hospitals

I have 2 tables:
CREATE TABLE remdesivir_inventory
(
hospital_id int,
stock int,
state varchar(2)
);
CREATE TABLE remdesivir_requests
(
patient_id int,
prescribed_qty int,
state varchar(2)
);
I want to write a SQL that inserts rows in the remdesivir_assignments table
Every patient whose request can be fulfilled (until the stock runs out) will have a representative row in
the remdesivir_assignments table.
Each patient can be assigned to only 1 hospital (ie. requests cannot be split)
The 'state' of the patient and the hospital must match
CREATE TABLE remdesivir_assignments
(
patient_id int,
hospital_id int
);
Example:
INSERT INTO remdesivir_inventory VALUES (1, 200, 'CA');
INSERT INTO remdesivir_inventory VALUES (2, 100, 'FL');
INSERT INTO remdesivir_inventory VALUES (3, 500, 'TX');
INSERT INTO remdesivir_requests VALUES (10, 100, 'CA');
INSERT INTO remdesivir_requests VALUES (20, 200, 'FL');
INSERT INTO remdesivir_requests VALUES (30, 300, 'TX');
INSERT INTO remdesivir_requests VALUES (40, 100, 'AL');
INSERT INTO remdesivir_requests VALUES (50, 200, 'CA');
In this scenario, the following rows will be inserted to the remdesivir_assignments table
(10, 1)
(30, 3)
You can use a cumulative sum and join:
select rr.*, ri.hospital_id
from (select rr.*,
sum(prescribed_qty) over (partition by state order by patient_id) as running_pq
from remdesivir_requests rr
) rr join
remdesivir_inventory ri
on ri.state = rr.state and
rr.running_pq <= ri.stock
Here is a db<>fiddle.

SQL query to display names of customers who have purchased some particular products

These are the tables I have created and inserted the values accordingly:
CREATE TABLE Customer
(
Customer_Id INTEGER IDENTITY (1,1) PRIMARY KEY,
Customer_Name VARCHAR(30) NOT NULL
)
CREATE TABLE Product
(
Product_Id INTEGER IDENTITY (1,1) PRIMARY KEY,
Product_Name VARCHAR(30)
)
CREATE TABLE Product_Purchase
(
Purchase_Id INTEGER IDENTITY (1,1) PRIMARY KEY,
Product_Id INTEGER NOT NULL,
Customer_Id INTEGER NOT NULL
)
INSERT INTO Customer (Customer_Name)
VALUES ('Sandip'), ('Ankit'), ('Ashok'), ('Raj')
INSERT INTO Product (Product_Name)
VALUES ('Milk'), ('Egg'), ('Butter'), ('Paneer'), ('Curd')
INSERT INTO Product_Purchase (Product_Id, Customer_Id)
VALUES (4, 1), (1, 1), (3, 1)
(4, 2), (1, 2),
(4, 3), (3, 3), (1, 3),
(1, 4), (2, 4), (3, 4), (4, 4)
So I have 3 tables Customers, Product and Product_Purchase with values inserted.
Now I want the list of customers who purchased only (Milk, Butter, and Paneer).
All purchased items
List of customers who purchased exactly (Milk, Butter, and Paneer) :
select cu.Customer_Name
from Customer as cu inner join dbo.Product_Purchase as pu
on pu.Customer_Id =cu.Customer_Id
where cu.Customer_Id not in
(select distinct pu.Customer_id from dbo.Product as pr inner join dbo.Product_Purchase as pu on pr.Product_Id=pu.Product_Id where Product_Name not in ('Milk', 'Butter','Paneer'))
group by cu.Customer_Name
having count(pu.Product_id)=3
select customer_name
from Customer C, Product_Purchase PP, Product P
where C.Customer_Id = PP.Customer_Id
and PP.Product_Id = P.Product_Id
and Product_name in ('Milk', 'Butter', 'Paneer');

Select prices/sizes from tables using joiner table

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

General database normalization

Suppose that I have a table of products that I sell to my customers.
Each record has a productID and productName.
I can sell more than 1 product to each customer, but I want to allow customers to only order certain products.
What would those tables look like?
This is what I have so far:
PRODUCTS
+------------+-------------+
| PROD_ID | PROD_NAME |
+------------+-------------+
CUSTOMER
+------------+-------------+
| CUST_ID | CUST_NAME |
+------------+-------------+
ORDERS
+------------+-------------+
| ORDER_ID | CUST_ID |
+------------+-------------+
I wrote and tested this with PostgreSQL, but the principles are the same for any SQL dbms.
Tables for products and customers are straightforward.
create table products (
prod_id integer primary key,
prod_name varchar(35) not null
);
insert into products values
(1, 'Product one'), (2, 'Product two'), (3, 'Product three');
create table customers (
cust_id integer primary key,
cust_name varchar(35) not null
);
insert into customers values
(100, 'Customer 100'), (200, 'Customer 200'), (300, 'Customer 300');
The table "permitted_products" controls which products each customer can order.
create table permitted_products (
cust_id integer not null references customers (cust_id),
prod_id integer not null references products (prod_id),
primary key (cust_id, prod_id)
);
insert into permitted_products values
-- Cust 100 permitted to buy all three products
(100, 1), (100, 2), (100, 3),
-- Cust 200 permitted to buy only product 2.
(200, 2);
Customer 300 has no permitted products.
create table orders (
ord_id integer primary key,
cust_id integer not null references customers (cust_id)
);
insert into orders values
(1, 100), (2, 200), (3, 100);
The table "order_line_items" is where the "magic" happens. The foreign key constraint on {cust_id, prod_id} prevents ordering products without permissions.
create table order_line_items (
ord_id integer not null,
line_item integer not null check (line_item > 0),
cust_id integer not null,
prod_id integer not null,
foreign key (ord_id) references orders (ord_id),
foreign key (cust_id, prod_id) references permitted_products (cust_id, prod_id),
primary key (ord_id, line_item)
);
insert into order_line_items values
(1, 1, 100, 1), (1, 2, 100, 2), (1, 3, 100, 3);
insert into order_line_items values
(2, 1, 200, 2);
insert into order_line_items values
(3, 1, 100, 3);
You can start an order for customer 300 . . .
insert into orders values (4, 300);
. . . but you can't insert any line items.
insert into order_line_items values (4, 1, 300, 1);
ERROR: insert or update on table "order_line_items" violates foreign key constraint "order_line_items_cust_id_fkey"
DETAIL: Key (cust_id, prod_id)=(300, 1) is not present in table "permitted_products".

Check if matching child records exist before saving parent records

We basically have a set of child records in which we will use to create a new parent/child record(s) but need to first verify that a parent record doesn't already exist containing the same child records. Here are the details:
We have 3 tables, one is basically a linking table between the parent and children records.
Table A (parent table)
Id
Name
Desc
Table B (linking table between tables A and C)
Id
TableAId
TableCId
Table C (child table)
Id
StartPosition
EndPosition
Percentage
So with that structure, here is an example of a complete record, the parent table it one-to-many relation with child table:
Table A
(1, 'Sample', 'N/A')
Table B
(1, 1, 1)
(2, 1, 2)
(3, 1, 3)
Table C
(1, 1, 3, 0.50)
(2, 4, 5, 0.30)
(3, 6, 9, 0.20)
So we then pass in an xml string which we parse and throw into a temp table. The contents of the temp table are that of Table C, without the specific Id.
Then before we save any new records, we need to check if there is an existing Table A record which has both the same number of child records and that those child records match the 3 columns in our temp table (no ID match possible).
Hopefully this is explained well enough, I have done many searches and can't find anything specific to this issue.
What you're looking for is called a relational division. The article "Divided We Stand: The SQL of Relational Division" provides a nice summary of various techniques for using SQL to perform a relational division. For your case, you want the technique listed under "Exact Division":
CREATE TABLE tableA (
Id int PRIMARY KEY,
Name varchar(25),
[Desc] varchar(255)
);
INSERT INTO tableA
(Id, Name, [Desc])
VALUES
(1, 'Sample 1', 'Should match the XML'),
(2, 'Sample 2', 'Partial match (should be excluded)'),
(3, 'Sample 3', 'Has extra matches (should be excluded)');
GO
CREATE TABLE tableB (
Id int PRIMARY KEY,
TableAId int,
TableCId int
);
INSERT INTO tableB
(Id, TableAId, TableCId)
VALUES
(1, 1, 1),
(2, 1, 2),
(3, 1, 3),
(4, 2, 1),
(5, 2, 2),
(6, 3, 1),
(7, 3, 2),
(8, 3, 3),
(9, 3, 4);
GO
CREATE TABLE tableC (
Id int PRIMARY KEY,
StartPosition int,
EndPosition int,
Percentage decimal(3,2)
);
INSERT INTO tableC
(Id, StartPosition, EndPosition, Percentage)
VALUES
(1, 1, 3, 0.50),
(2, 4, 5, 0.30),
(3, 6, 9, 0.20),
(4, 10, 12, 0.10);
GO
-- this represents the temp table holding the XML data
-- we want to match Sample 1
CREATE TABLE xmlData (
StartPosition int,
EndPosition int,
Percentage decimal(3,2)
);
INSERT INTO xmlData
(StartPosition, EndPosition, Percentage)
VALUES
(1, 3, 0.50),
(4, 5, 0.30),
(6, 9, 0.20);
GO
SELECT
b.TableAId
FROM
tableB AS b
INNER JOIN
tableC AS c
ON
b.TableCId = c.Id
LEFT OUTER JOIN
xmlData AS x
ON
c.StartPosition = x.StartPosition AND
c.EndPosition = x.EndPosition AND
c.Percentage = x.Percentage
GROUP BY
b.TableAId
HAVING
COUNT(c.Id) = (SELECT COUNT(*) FROM xmlData) AND
COUNT(x.StartPosition) = (SELECT COUNT(*) FROM xmlData);
GO
DROP TABLE xmlData;
DROP TABLE tableC;
DROP TABLE tableB;
DROP TABLE tableA;
GO