Simple database for product order - sql

I want to make a simple database to order products, like chips/drinks (as full product, without any specific info about product just name and price for unit)
I designed this but I'm not sure if it's good:
**Person:**
id
username
name
password
phone
email
street
zip
**order:**
id
person_id
product_id
date
quantity (neccessary?)
status (done or not)
**product:**
id
name
price
relations:
[person] 1 --- 1 [order] 1 --- many [product]
(I'm not sure about relations and fields)

It seems that in your way you are going to end up in orders containing a single product (even if you use the quantity)
I would modify the Order table:
**order:**
id
person_id
date
status (done or not)
And I would add a new table:
**OrderDetails**
id
order_id
product_id
quantity
You may check out for db normalization. You should add columns to a table that are directly related to the table. For instance date in the order is valid, because it refers to the order it was made. On the other hand it wouldn't be valid in the person table (unless it was referring to the person join date). So, similarly the quantity refers to the product in the order (thus in OrderDetails) not in the Order or the Product.

You will probably need an intermediate table between order and product, so you can add many times same order to different products

Related

SQL query for joining two tables and pulling repeated entries

I'm trying to join two tables that handle customer purchases, or purchase-intents in order to figure out how many customers made a repeat purchase-intent.
Posting a stripped down version of the DB schema for the problem:
Bid
id
user_id
created_at
Order
id
bid_id (optional)
user_id
created_at
User
id
created_at
Bids signify purchase intent, while Orders signify purchases. A user could be associated with many Bids or Orders.
So a user can start with
- creating a Bid, which could then become fulfilled, and generates a Order, or
- the user could create an Order without an associating Bid.
Bids aren't directly associated with Orders because a Bid could generate many Orders (one to many).
I'm trying to write a SQL query that pulls users who first created a Bid which became a purchase (i.e have an Order record with a bid_id), and then created another Bid after that.
In English, customers who created a Bid, had the Bid fulfilled, and then placed a new Bid sometime after.
Currently not particular about whether they made a direct purchase (Order without Bid), or had an unfulfilled Bid in between the fulfilled Bid and the new Bid.
Main metric I'm trying to confirm is that they placed a new bid sometime after one of their prior bids got fulfilled.
In trying to solve this, I've been only able to get a list of users who placed more than one bid and had at least one fulfilled. But been unable to get the number of repeat users who first had a prior Bid fulfilled before placing a new bid
exists comes to mind for this type of query:
select o.*
from orders o
where bid_id is not null and
exists (select 1
from bids b
where b.user_id = o.user_id and b.created_at > o.created_at
);

How would I structure a database for taking sales orders?

I am helping someone build a grocery deliver service. Very simple site and order process. The problem I am having is knowing how to come up with schema for the orders. The order would have contact information, but would also need to have all of the products they ordered. Since it can/will be different for each persons order, how would I go about designing this? Would I just have a products ordered field with a string list of all the items? Or would I need multiple linked tables?
Thanks!
You need three tables:
a) Products - Contains the product details
b) Orders - Contains the order header, contact information etc
c) Product_Orders - is a table with two columns: product_id and order_id, that bind the orders to the products, and a quantity field and unit price.
You could have a customers table, but ideally information like the delivery address, etc, would be attached to the order so that if the user changes his address it will not affect your order history.
For the same reason unit price must be in Product_Orders, so that if a product price changes it does not affect previous orders.
Product table
with name and price of all products.
Customer table
with name, address, phone etc...
Order table
with entries for each order: order id, cusomer id, date etc
Order Items table
with all ordered items, with link to id in ordertable, product table, quantity, price, etc...

linking child tables of two related parent-child tables

I have two parent child tables and a couple of supporting tables. I am struggling to figure out the right relationships between them.
Orders, a table that holds a list of Orders including:Order Id, Supplier, Order Date etc..
OrderDetails, a Table holds a list of products that have been ordered, and is a child of Orders, linked on OrderId. Key fields are the ProductId and the Quantity Ordered. Each order containes one or many OrderDetails that outline the products and quantities being ordered.
Shipments, a table that holds a list of Shipments, including Tracking Number and Shipper. Links to Orders through Order Id. An Order may have multiple shipments. For example, I ordered 100 lightbulbs. They were dispatched in 5 shipments.
ShipmentDetails, holds a list of product and shipped quantities and is linked to the Shipments table by ShippingId. Each Shipment may have multiple ShipmentDetails. i.e. One shipment may have 30 Lightbulbs and 10 Door Knobs.
Products is a table that holds a list of Products that need to be both ordered and shipped.
So the logic is that I enter details about the products that are to be ordered in Products. For example, 100 CREE LED Lightbulbs, and 50 Door Handles.
When I place an order, I create an Order in Orders.
i.e. amazon.com, order #45454.
Then I add child rows to that order in OrderDetails. i.e. 30 CREE LED Lightbulbs.
When the order Ships, I create an entry in the Shipments table. i.e. Tracking #46464646464, linked to OrderId in Orders. And then I enter what is in that shipment in ShipmentDetails. For example, only 20 or the 30 CREE LED Lightbulbs may be associated with this Shipping entry.
I am struggling with figuring out how to relate the Shipping Detail records to the Order Detail Table. Lets say the Shipping Detail Table has 4 fields.
ShippingID - a link to the Shipments table parent.
TrackingNum - a field that holds a tracking number.
Product - this should be a drop down, that says, the shipment I belong to is related to a defined Order (because ShipmentDetail is a child of a Shipment record which in turn holds a key to the Orders table, which in turn has OrderDetails that reference products).
Quantity - this should have a default (overridable) value that is the "Quantity Ordered" number from the OrdersDetail record that matches the Order Id and Product Id. Where OrderId is in the Shipments table (linked to the Orders table) and ProductID comes from #3 above. The reason it must be overridable is that a shipment may be a partial shipment.
I am stuck with the preceding #3 and #4. I hope I have explained this in a vaguely understandable way! The pictures below may help!
The ask:
What is the correct join between the ShipmentDetails and the OrderDetail Table
How do I create a field in the ShipmentDetail table with a default value pulled from the Quantities Field of the OrderDetail table, where
Shipments!OrderId = Orders!Id and ShipmentDetail!ProductID = OrderDetails!Product ID
I am working in MS Access 2016 - but I suspect this is a fairly generic SQL question...rather than MS Access specific.
I think the naming is confusing.
What I would rather have is (each first id is autoincrement, forgot how to say this on access):
// Product doesn't contain any information about quantity/price etc ...
create table Products(product_id int, name text, description text ...);
// Orders:
create table Orders(order_id int, client_name text, description text ...);
create table OrderDetails(order_detail_id int, order_id int, product_id int, quantity double, unit_price double, ...);
// Shipments
create table Shipments(shipment_id int, company text, description text ...);
create table ShipmentDetails(shipment_detail_id int, shipment_id int, product_id int, quantity double, price double default 0, ...);
// inserting shipments per product (defaulting to total quantity per product), assuming we have shipment_id SID
insert into ShipmentDetails(shipment_id, order_id, product_id, quantity)
select SID, order_id, product_id, SUM(quantity)
from OrderDetails
group by order_id, product_id;
Then you can of course have some filters (date, customer etc ...).
For the first question, I am not clear what exaclty you want to return.
Here is a comparison of quantities:
select t.order_id, t.product_id, sum(t.quantity) as product_quantity, sum(u.quantity) as shipment_quantity
from OrderDetails t inner join ShipmentDetails u
on t.order_id = u.order_id and t.product_id = u.product_id;

SQL Access with Table

It wont let me upload image but columns are OrderID, CustomerName, CustomerAddress, ProductNumber, SellDate, ProductDescription
I am trying to teach myself SQL. Could someone please help me identify a few things?
1) I want to write a SQL statement that retrieves the customer name and address of the customer that placed order 7.
Is this right?
Select CustomerName, Address
From Order
Where OrderID = ‘7’
2)Next I want to write an SQL statement that adds a new order to the Order table.
Is this right?
INSERT INTO order(OrderID, CustomerName, CustomerAddress, ProductNumber, SellDate, ProductDescription)
VALUES (8, 'Ben C', '12 Kents Road', 01/15/2012, Clay :));
3) What is wrong with this data model and how would you redesign it? I really need help here. Does it need to be sorted? How could I describe a new high level design?
4) How would I move this data from an old model to a new model?
5)Using the new data model, I need to write a JOIN that retrieves the customer name and address of the customer that placed order 7. I have not gotten here yet because I am not sure why the old data model is bad.
First, you need to answer a question:
Can a customer place more than one order? If your answer is 'yes', would you like to have a customer catalog?
In this scenario, you need to normalize your database. First of all, you need to separate the data into logical sets; in this case, Customers, Products and Orders... I will asume that an order can have one or more products.
Then, design your tables (I will use MySQL style for the code):
Your customers catalog:
create table tbl_customers (
customerId int not null primary key,
customerName varchar(100),
customerAdress varchar(200)
);
Your products catalog:
create table tbl_products (
productNumber int not null primary key,
productName varchar(100),
);
Your orders catalog:
create table tbl_orders (
orderId int not null primary key,
orderDate date,
customerId int unsigned not null
);
For each order, you will need to know how many 'units' of which products you will be ordering:
create table tbl_orders_products (
orderProductId int not null primary key,
orderId int not null,
productNumber int not null,
units int,
);
After this, you will populate your tables with your data, and then you can perform whichever query fits you.
A few notes:
tbl_orders is related with tbl_customers... your customer's data will have to be inserted in tbl_customers before he can place an order.
Before you insert the order's details, you will need to create the order
Before you insert the order's details, you will need to populate tbl_products
This is just a way to solve it.
Hope this helps you
Now, if you want to move to this model, you have some work to do:
Populate your products catalog: insert into tbl_products values (1,'productA'), (2, 'productB'), ...
Populate your customers catalog
Then you can start placing your orders. I'll asume that you have the following customers:
customerId | customerName | customerAdress
---------------------------------------------
1 | John Doe | 31 elm street
2 | Jane Doe | 1223 park street
... and products:
productNumber | productName
------------------------------
1000 | Pencil
2000 | Paper clip
3000 | Bottled water
Now, placing an order is a two-step process: first, create the order record, and then insert the order details:
The order (Customer John Doe): insert into tbl_orders values (1, '2012-10-17', 1);
The order details (one pencil, ten paper clips): insert into tbl_orders_products values (1, 1, 1000, 1), (2, 1, 2000, 10);
Now, to select the customer for order seven (as stated in your question), you can use the following query:
select c.*
from tbl_customers as c
inner join tbl_orders as o on c.customerId = o.customerId
where o.orderId = 7;
This is just a start point. You should look for good books or online tutorials (w3 tutorials can be a good online 'place' to start).
Although I don't quite like MS Access, it's a good program to learn the basics of sql, and there're a lot of books and learning resources for it. Note: I don't like Access, and I don't mean to advertise it, but it might be a good learning tool.
First you need to normalise, there 's a lot of stuff around that, but loads of tutorials that try and take some common sense and make it really obscure
Looking at your column names I see three tables
Customers(CustomerID, CustomerName, CustomerAddress)
CustomerOrders(OrderID, CustomerID, SellDate, ProductNumber) Try not to name your tables and columns and such the same as Sql keywords.
Products(ProductNumber, ProductDescription)
Normalisation says things like, you should be able to uniquely identify any records in the table, you had that with OrderId. When I split the tables up I added CustomerID, because you could have more than one customer with the same name.
Another simple rule is in your structure, if you had more than one order for a customer, you would be storing their name and address more than once, which is wasteful, but the real problem, is what if that customer changes address? How do you find which rows to change, you could do Where name = "Fred" and Address = "Here", but you don't know if you have more than one customer called Fred with an address of Here.
So you first query would be a join
Select Customers.CustomerName,Customers.CustomerAddress From Customers
Inner join CustomerOrders On Customers.CustomerID = CustomerOrders.CustomerID
Where CustomerOrders.OrderID = 7
Or if you want to skip past learning joins for now, you could do it with two queries
Select CustomerID From CustomerOrders Where OrderID = 7
then
Select CustomerName,CustomerAddress From Customers Where CustomerID = ?
You should be using joins, but you might find sub-query a little easier to get your head round. You can do both queries at once with
Select CustomerID From CustomerOrders
Where CustomerID In (Select CustomerID From CustomerOrders Where OrderID = 7)
Don't know far you've got with sql table creation, but Primary and Foreign keys is two things to look at. That will let you put some rules in the database. A primary Key on CustomerOrders will stop you having two orders with the same ID, which would be bad.
A foreign Key would stop you creating a CustomerOrder for a customer that did not exist, and one to products for a product that doesn't.
Assuming you went down that route and you were looking to write an application to order things.
You'd probably have a function to maintain Customers which would add them with something like
Insert Into Customers(CustomerID,CustomerName,CustomerAddress) Values(1,'Fred Bloggs','England')
And one For Products
Insert Into Products(ProductNumber,ProductDescription) Values(1,'A thingamabob')
Then you'd choose a customer, so you have it's id, select a product so you have it's number, so you don't need to mess with CustomerName, CustomerAddress or ProductDescription
and
Insert Into CustomerOrders(OrderID,CustomerID,ProductNumber,SellDate) Values(1,1,1,'20121017')
Note the date format, if you are going to pass dates as strings to sql, (another topic this) do them in YYYYMMDD, when you get them back out with a select, you'll get them in the format your machine/database is set up for which in your case seems to be mm/dd/yyyy. The problem is I deduced that because I know there are only twelve months in the year. (One of the things that makes me a senior dev :) If your example selldate had been 1/5/2012, I'd have read that as the 1st May, because I'm configured for English. Avoid that ambiguity at all costs it will reach out and hurt you on a regular basis.
PS the way you did it 1/15/2012 would be treated as a mathematical expression as in 1 divided by 15 ...
So the reason you couldn't write a join is basically you only had one table. Join is join one table to another. Well actually it's a bit more complex than that, but that's a good way past where you are in the learning curve.
As for moving the data, be quicker to start again I should think. Unlikely you have created two different customers with the same name, but the queries to move the data, would have to take into account that you could have.
To move the data, assuming CustomerID is an Identity (Autonumber) column
Something like
Insert into Customers(CustomerName,CustomerAddress)
Select Distinct CustomerName,CustomerAddress From [Order]
Would do the job for Customers.
Then for products
Insert into Products(ProductDescription)
Select Distinct ProductDescription From [Order]
Then
Insert into CustomerOrders(OrderID,CustomerID,ProductNumber,SellDate)
Select old.OrderID,c.CustomerID,p.ProductNumber,old.SellDate
From [Order] old
Inner Join Products p On p.ProductDesription = old.ProductDescription
Inner Join Customers c On c.CustomerName = old.CustomerName And c.CustomerAddress = old.CustomerAddress
might do CustomerOrders I think
A simple tip. When modelling a data solution, try to write down simple sentences that describe the scenario. For example (ok, it is just a basic one):
An order is made up of many order lines
An order line refers a product
A customer create many orders
Here, the nouns describe the entities of your scenario. Then, for each entity, try to describe its property:
An order is characterized by a unique serial number, a date, a total. It refers a customer.
An order line refers to a product, and is characterized by a quantity, a unit price, a sub total
A customer....
An so on.
Well, in your model you roughly have to create a table for each entity. The table fields are taken from the property of each entity. For each field remeber to define the proper data type.
Ok, this is NOT a modelling tutorial, but it is a starting point, just to approach the solution.

SQL "GROUP BY" issue

I'm designing a shopping cart. To circumvent the problem of old invoices showing inaccurate pricing after a product's price gets changed, I moved the price field from the Product table into a ProductPrice table that consists of 3 fields, pid, date and price. pid and date form the primary key for the table. Here's an example of what the table looks like:
pid date price
1 1/1/09 50
1 2/1/09 55
1 3/1/09 54
Using SELECT and GROUP BY to find the latest price of each product, I came up with:
SELECT pid, price, max(date) FROM ProductPrice GROUP BY pid
The date and pid returned were accurate. I received exactly 1 entry for every unique pid and the date that accompanied it was the latest date for that pid. However, what came as a surprise was the price returned. It returned the price of the first row matching the pid, which in this case was 50.
After reworking my statement, I came up with this:
SELECT pp.pid, pp.price, pp.date FROM ProductPrice AS pp
INNER JOIN (
SELECT pid AS lastPid, max(date) AS lastDate FROM ProductPrice GROUP BY pid
) AS m
ON pp.pid = lastPid AND pp.date = lastDate
While the reworked statement now yields the correct price(54), it seems incredible that such a simple sounding query would require an inner join to execute. My question is, is my second statement the easiest way to accomplish what I need to do? Or am I missing something here? Thanks in advance!
James
The reason you get an arbitrary price is that mysql cannot know which columns to select if you GROUP BY something. It knows it needs a price and a date per pid and can fetch the latest date as you requested with max(date) but chooses to return a price that is most efficient for him to retrieve - you didn't provide an aggregate function for that column (your first query is not valid SQL, actually.)
Your second query looks OK, but here is a shorter alternative:
SELECT pid, price, date
FROM ProductPrice p
WHERE date = (SELECT MAX(date) FROM ProductPrice tmp WHERE tmp.pid = p.pid)
But if you access the latest price a lot (which I think you do), I would recommend adding the old column back to your original table to hold the newest value, if you have the option of altering the database structure again.
I think you broke your database schema.
To circumvent the problem of old invoices showing inaccurate pricing after a product's price gets changed, I moved the price field from the Product table into a ProductPrice table that consists of 3 fields, pid, date and price. pid and date form the primary key for the table.
As you have pointed out you need to keep a change history of prices. But you can still keep the current price in the products table in addition to that new table. That would make your life much easier (and your queries faster).
You cannot solve your problem with the GROUP BY clause, because for each group of pid MySQL will simply fetch the first pid, the maximum date and the first price found (which is not what you need).
You may either use a subquery (which can be inefficient):
SELECT pid, date, price
FROM ProductPrice p1
WHERE date = ( SELECT MAX(p2.date)
FROM ProductPrice p2
WHERE p1.pid = p2.pid)
or you can simply join the table with itself:
SELECT p1.pid, p1.date, p1.price
FROM ProductPrice p1
LEFT JOIN ProductPrice p2 ON p1.pid = p2.pid
AND p1.date < p2.date
WHERE p2.pid IS NULL
Take a look at this section of MySQL docs.
You might wanna try this:
SELECT pid, price, date FROM ProductPrice GROUP BY pid ORDER BY date DESC
Group has some obscure functionality, I'm too always unsure if it's the right field...but it should be the first in the resultset.
Here is another -possibly inefficient- one:
SELECT pid, substring_index( group_concat( price order by date desc ), ',', 1 ) , max(date)
FROM ProductPrice
GROUP BY pid
I think that the key here is simple sounding query - you can see what you want but computers ain't human and so to produce the desired result from set based operations you have to be explicit as in the second query.
The inner query identifies the last price for each product, then the outer query lets you get the value for the last price - that's about as simple as it can get.
As an aside, if you have an invoicing system, you really ought to store the price for the product (and the tax rates as well as the "codes") with the invoice i.e. the invoice tables should contain all the necessary financial information to reproduce the invoice. In general, you do not want to rely on being able to look up a price (or a tax rate) in a mutable table even allowing for the system introduced as above. Regardless of this have the pricing history has its own merits.
i faced same problem in one of my project i used subquery to fetch date and then compare it but it makes system slow when data increases. so, its better to store latest price in your Products table in addition to the new table you have created to keep history of price changes.
you can always use any of query ppl suggested to get latest price of product on particular date. but also you can add one field in the same table is it latest. so for one date you can make flag true once. and you can always find product's latest price for particular date by one simple query.