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".
Related
I have 2 tables that look like this where I want to query how many scholarships (from Tuition table) each department (from Student table) has distributed:
I am thinking a join is necessary but am not sure how to do so.
Create tables
create table students (
sid int auto_increment primary key,
name varchar(100),
email varchar(100),
department varchar(100)
);
create table tutions (
id int auto_increment primary key,
sid int,
cost int,
scholarships int,
duedate timestamp default current_timestamp
);
Sample data
insert into students (name, email, department)
values
('John Doe', 'john#abc.xyz', 'B'),
('Jane Doe', 'jane#abc.xyz', 'A'),
('Jack Doe', 'jack#abc.xyz', 'C'),
('Jill Doe', 'jill#abc.xyz', 'B');
insert into tutions (sid, cost, scholarships)
values
(1, 1000, 2),
(2, 1000, 1),
(3, 1000, 7),
(4, 1000, 2);
Query (department-wise total scholarships)
SELECT department, sum(scholarships) as scholarships
FROM students s
JOIN tutions t ON s.sid = t.sid
GROUP BY department
Output
Running SQL Fiddle
Not sure It's something you want? And not sure scholarships is a number or name of scholarship? So I doubt it's a name as varchar string type.
### dummy record
CREATE TABLE students (
psu_id INTEGER PRIMARY KEY,
firstname VARCHAR NOT NULL,
lastname VARCHAR NOT NULL,
email VARCHAR NOT NULL,
department VARCHAR NOT NULL
);
CREATE TABLE tuition (
tuition_id INTEGER PRIMARY KEY,
student_id INTEGER NOT NULL,
semeter_cost INTEGER NOT NULL,
scholarships VARCHAR NOT NULL,
due_date DATE NOT NULL
);
INSERT INTO students VALUES (1, 'John', 'Hello', 'Jonh#email.com', 'Engineering');
INSERT INTO students VALUES (2, 'Bella', 'Fuzz', 'Bella#email.com', 'Computer');
INSERT INTO students VALUES (3, 'Sunny', 'World', 'Sunny#email.com', 'Science');
INSERT INTO tuition VALUES (1, 1, 4000, 'first_class_en', '2022-05-09' );
INSERT INTO tuition VALUES (2, 2, 3000, 'nobel', '2022-05-09' );
INSERT INTO tuition VALUES (3, 3, 5000, 'hackathon', '2022-05-09' );
INSERT INTO tuition VALUES (4, 1, 4500, 'second_class_en', '2022-05-09' );
-----------------
### query
SELECT s.department, count(t.scholarships)
FROM students s
JOIN tuition t
ON s.psu_id = t.student_id
GROUP BY s.department
### output
department, total_scholarships
Computer|1
Engineering|2
Science|1
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
I'll first explain the data model then the desired results and what I have tried.
I have vehicles and sales tables:
CREATE TABLE VEHICLE
(
ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
BRAND INT NOT NULL,
MODEL VARCHAR(255),
VERSION VARCHAR(255),
UNIQUE(BRAND, MODEL, VERSION),
FOREIGN KEY(BRAND) REFERENCES BRAND(ID)
)
CREATE TABLE SALES
(
ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
VEHICLE_ID INT NOT NULL,
DATE DATE NOT NULL,
SALE INT NOT NULL,
CREATED_DATE DATETIME NOT NULL DEFAULT GETDATE(),
FOREIGN KEY (VEHICLE_ID) REFERENCES VEHICLE(ID)
)
This way I can insert several entries for the same vehicle for the same date (when I want to update, I insert a new row)
INSERT INTO SALES (VEHICLE_ID, DATE, SALE, USER_ID)
VALUES (1, '2018-01-01', 2, 3) -- then later i update by inserting a new row
(1, '2018-01-01', 4, 3)
I want to retrieve the last sale inserted for a specific date range (using the DATE), then filter for a specific BRAND, or model or version.
I got it working by doing this
SELECT
S.DATE AS date, SUM(S.SALE_PROJECTION) AS saleProjection
FROM
SALE_PROJECTION S,
(SELECT MAX(ID) AS id
FROM SALE_PROJECTION
WHERE DATE >= CAST(#dateStart AS DATE)
AND DATE <= CAST(#dateEnd AS DATE)
GROUP BY DATE, VEHICLE_ID) S_M,
VEHICLE V
WHERE
1 = 1
AND S.ID = S_M.ID
AND S.VEHICLE_ID = V.ID
AND V.BRAND = 1
AND V.MODEL = 'A6'
AND V.VERSION = '1.0'
GROUP BY S.DATE
ORDER BY DATE
The problem is i want to get the sales for the brand 1 that has the least specificity, meaning:
If i have 3 vehicles:
(1, 'A3', '1.0'),
(1, 'A3', '2.0'),
(1, 'A3', null),
(1, null, null);
if i insert a sale (1, 2018-01-01, 2, 3)
if i insert a sale (2, 2018-01-01, 3, 3) -- the sum for 2018-01-01 would be 5
but then insert a sale for (2, 2018-01-01, 3, 3) -- the sum for 2018-01-01 has to be 3, because it's the last inserted with the least specifity
But the oposite must be true as well
if i insert a sale (3, 2018-01-01, 4, 3)
then insert a sale for (1, 2018-01-01, 1, 3)
then insert a sale for (2, 2018-01-01, 1, 3)
the sum for 2018-01-01 has to be 2, because it's the last inserted
The most general combination of Brand, Model, Version has to "hide" the most specific.
Do i need to change my data model? or this is possible?
I can give more examples if needed.
Thanks in advance
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');
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