I am trying to get for a specific period of time the quantity of products sold. For this example : the products table has a quantity field which increments whenever a certain product is sold. The orders table has a start date and end date. Is there a way i can hold a reference of the products table (like an object) in my orders table so i can see the quantity sold for each product every start - end date ? A quick example : I am having 2 products:
1 bike 25000 3 and 2 sportbike 30000 5 sold for my date. So an order would be something like : 1 05.07.2015 05.07.2015 and those products.
CREATE TABLE products (
product_no integer PRIMARY KEY,
name varchar(20),
price float,
quantity integer,
);
CREATE TABLE sells (
sell_id integer PRIMARY KEY,
start_date date,
end_date date
);
//New idea :
CREATE TABLE products (
product_no integer PRIMARY KEY,
name varchar(20),
price float
);
CREATE TABLE sells (
sell_id integer PRIMARY KEY,
date date,
quantity integer,
product_id integer FOREIGN KEY
);
Unless an order is always for a single product, I think you will need a third table. To me a minimal *) data model for this situation would look like this:
CREATE TABLE products (
product_no integer PRIMARY KEY,
name varchar(20) not null,
price float,
quantity_on_stock integer not null
);
CREATE TABLE orders (
order_id integer PRIMARY KEY,
order_date date not null
);
CREATE TABLE orderlines (
order_id integer not null REFERENCES orders.order_id,
product_no integer REFERENCES products.product_no,
price integer,
quantity integer not null,
PRIMARY KEY(order_id, product_no)
);
Then, a query to get sales in a certain period could look like this:
select
p.product_no,
p.name,
sum(ol.quantity)
from
products p
inner join orderlines ol on ol.product_no = p.product_no
inner join orderlines o on o.order_id = ol.order_id
where
ol.order_date between :start_date and :end_date
*) I say minimal, but it's actually less than minimal. You'd probably want to store invoices too, or at least some kind of indication that says whether an order is actually payed for and delivered, since sometimes orders are cancelled, or are just waiting open.
You definitely want a sales table showing what was sold and when, rather than a table showing the running inventory.
It is much easier in SQL to aggregate detail to create running totals than it is to reconstruct detail from running totals.
So, if you happen to have a sales table containing, among other things, columns for product_id, quantity, and ts (the timestamp of the sale), then you can get a summary of sales by product_id and sales date like this.
SELECT product_id,
DATE(ts) AS salesdate,
SUM(quantity) AS quantity
FROM sales
WHERE ts >= :start_date
AND ts < :end_date + INTERVAL 1 DAY
GROUP BY DATE(ts), product_id
One of the things that's cool about this is you can add a column for transaction_type to this table. If a customer returns a product you tag it return and use a negative quantity. If your shop receives a shipment of products you tag it restock and use a negative quantity. Then, if you want to show net sales -- sales less returned products -- it's a small change to the query.
SELECT product_id,
DATE(ts) AS salesdate,
SUM(quantity) AS quantity
FROM sales
WHERE ts >= :start_date
AND ts < :end_date + INTERVAL 1 DAY
AND transaction_type IN ('sale', 'return')
GROUP BY DATE(ts), product_id
If you want to know how much stock you had on on hand for each product for a particular end_date, you do this (starting from the beginning of time).
SELECT product_id,
SUM(quantity) AS quantity
FROM sales
GROUP BY product_id
In actual inventory / sale systems, it's common to take inventory once a year, close out the previous year's sales table, and start a new one with a starting value for each product. it's also common to organize sales by sales_order and sales_detail, so you can track the sale of multiple items in each transaction.
Related
I am creating an accounting module with laravel and mysql. For that, I've master table called 'bills' which has some columns regarding invoice informations and id as primary key. Now I've two different tables called 'payments' having bill_id as foreign key and 'debitnotes' also having bill_id as foreign key.
Now I want to show payment history of particular bill which is paid. In order to that, I've to retrieve information from both payments table and debitotes table based on bill id at once! Now, my question is: How can I achieve this without writing multiple queries?
bills table has columns:
id
bill_number
vendor_id
amount
amount_payable
date
payments table has columns:
id
bill_id
transaction_number
amount
date
vendor_id
debitnotes table has columns:
id
bill_id
debit_note_number
amount
vendor_id
date
I want to show both payments and debitnotes of particular bills in single page. How can i achieve this?
Use a union:
select
'bill' source,
id,
bill_number number,
vendor_id,
amount,
amount_payable,
date
from bills
union all
'payment',
id,
transaction_number,
vendor_id,
-amount amount, -- assuming payments cause the balance to lower
null,
date
from payments
union all
'debitnote',
id,
debit_note_number,
vendor_id,
amount,
null,
date
from debitnotes
You should delete the vendor_id column from payments and debitnotes because it is redundant (or denormalized); it can be found by joining to the bill table.
You should use join clause, and for payments and debitnotes, you can prepend the table name.
Here is an example from documentation:
$users = DB::table('users')
->join('contacts', 'users.id', '=', 'contacts.user_id')
->join('orders', 'users.id', '=', 'orders.user_id')
->select('users.*', 'contacts.phone', 'orders.price')
->get();
the following table is supposed to reflect the storage location (stockId) of
purchases, where a purchase is received in parts at different times. (I omitted the date column.)
CREATE TABLE stock (purchaseNo INT NOT NULL, quantity INT NOT NULL, stockId INT NOT NULL)
Let's say purchase 10 has arrived in three parts and is stored at two different locations (5 and 6).
INSERT INTO stock VALUES(10, 2000, 5)
INSERT INTO stock VALUES(10, 3000, 5)
INSERT INTO stock VALUES(10, 1000, 6)
Now I would like to select those stocks having more than 2000 kg (quantity) available. This can be done with
SELECT stockId AS id, SUM(quantity) AS q
FROM stock
WHERE purchaseNo=10
GROUP BY stockId
HAVING q>2000
But since I am only interested in the stockId, I would like to prevent the quantity to be returned as well. Is this possible?
One of my failing attempts is
SELECT stockId AS id
FROM stock
WHERE purchaseNo=10 AND (SELECT SUM(quantity) FROM stock WHERE purchaseNo=10 AND stockId=id) >2000
with or without GROUP BY the following error is returned:
ERROR 1054 (42S22): Unknown column 'id' in 'where clause'
You could pass the SUM to HAVING and skip it from the results.
SELECT stockId AS id
FROM stock
WHERE purchaseNo=10
GROUP BY stockId
HAVING SUM(quantity)>2000;
SQLFiddle example.
Have you tried something like this?
SELECT distinct S1.stockId AS S1.id from stock S1
inner join(
SELECT S2.stockId AS S2.id, SUM(S2.quantity) AS S2.q
FROM stock S2
WHERE S2.purchaseNo=10
GROUP BY S2.stockId
HAVING S2.q>2000)
on S1.stockId=S2.stockId
Using the data and schema from this site: Using the data from this site: https://www.sqlservertutorial.net/sql-server-sample-database/
I am given the prompt:
Create a stored procedure called placeOrder() that can be called to insert a new order in the database. It will receive a customerId as an INT, a productId as an INT and a qty as an INT and return (as anoutput parameter) the order_id of the new row created in table orders.
This stored procedure will find the store with the largest stock of that particular product and assign that store to the order. The order_status should be set to 1 (i.e. Pending), the current system date (see function CURDATE) will be assigned to order_date, column required_date will be 7 days from the current system date (see function ADDDATE) and the column staff_id will be assigned for anyone that works in the selected store (per previous requirement). Since the order_id column is not an auto-incremented column you need to calculate the value for it. You can use max(order_id) to find out the highest order_id in the table.
The order_item row shall be set to the productId and qty passed to the stored procedure. The item_id shall be set to 1 (since this order will only have one item).The list price should be retrieved from the products table using the passed productId. The discount value should be set to 0
If I'm understanding the prompt correctly, I need to first the store id which has the most of a particular product. Once I have that store, I need to insert a new row of data into the table "orders" with the essential data being order_id, customer_id, order_status, order_date, required, date, and staff_id. I do NOT understand what the last part of the question is asking/how to go about solving.
Here's my current code, but I'm almost positive it's chalk full of errors and notes and missing pieces so please help me out where you can:
DELIMITER //
CREATE procedure placeOrder (IN customerID INT, IN productID INT, IN QTY INT, OUT order_id INT)
BEGIN
DECLARE customerID INT;
DECLARE produtcID INT;
DECLARE quantity INT;
SELECT customer_id INTO customerID from customers where customer_id = customerID;
SELECT product_id INTO productID from order_items where product_id = productID;
SELECT quantity INTO qty from order_items where quantity = qty;
/**find store id with max stocks of product**/
select st.store_name, sk.store_id from stocks as sk
INNER JOIN
stores as st
ON sk.store_id = st.store_id
WHERE max(sk.quantity)
GROUP by sk.product_id;
select st.store_id from stores as st
INNER JOIN orders as o
ON st.store_id= o.store_id
Insert into orders (order_id, customer_id, order_status, order_date, required_date, staff_id)
WHERE order_status = '1',
AND order_date = select(curdate()),
AND required_date = adddate('order_date' +7),
AND staff_id = /**ANYONE from store listed in precious query (how do I relate these two queries)**
END
Don't re-declare function parameters.
You don't need to use a SELECT query to set variables that are already set in parameters.
You're not getting the store with the maximum quantity correctly. Use ORDER BY sk.quantity DESC LIMIT 1
You need to use INTO <variable> in a query to set variables from a query. If you don't do this, the result of the query will be turned into the result of the procedure, which isn't desired here.
You don't use WHERE in an INSERT statement. WHERE is used for finding existing rows that match a condition, but INSERT is for creating new rows. You use VALUES() to list all the values that should be assigned to the specified columns.
CREATE procedure placeOrder (IN customerID INT, IN productID INT, IN QTY INT, OUT orderId INT)
BEGIN
DECLARE topStoreId INT;
DECLARE staffId INT;
/**find store id with max stocks of product**/
select sk.store_id
from stocks as sk
INNER JOIN stores as st
ON sk.store_id = st.store_id
WHERE sk.product_id = productID
ORDER BY sk.quantity DESC
LIMIT 1
INTO topStoreId;
/* Pick an arbitrary staff from the selected store */
SELECT staff_id
FROM staffs
WHERE store_id = topStoreId
LIMIT 1
INTO staffId;
SELECT MAX(order_id)
FROM orders AS o
INTO orderId;
Insert into orders (order_id, customer_id, order_status, order_date, required_date, staff_id, store_id)
VALUES (orderId, customerId, 1, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 1 WEEK), staffId, topStoreId);
END
I Have 2 tables, one for Product sales and another for Product orders from suppliers.
Sales table contains : (ProductID, Sale date, customer ID, etc)
Orders table contains (ProductID, Order date, Supplier ID etc).
Each product on sales table was sold many times and each product of Orders table was ordered many times.
For each product ID, I want to query the sales tables to return only rows where the sale dates of that product are higher than the earliest order date for that product. For example, there may be a product that was sold even before the very first time it was ordered from the suppliers, for each productID, I want to eliminate those rows with such dates and only keep those that came after the earliest order from suppliers. Any ideas on how to go about doing that in SQL, MySQL or NetezzaSQL? Thanks !
Sales Table:
ProductID | Sale_date | CustomerID
Orders Table :
ProductID | Order_Date | SupplierID
Any help or tips would be appreciated!
select s.*, o.*
from sales s
inner join (select product.id,min(order_date) min_order_date, supplier_id from orders) o
on s.product_id = o.product_id and s.sale_date > o.min_order_date
If I understood correctly the above should do the trick!
In any case dont hesitate to comment here with more questions :)
Ted
Two tables: products, all IDs are unique, and stock, where there can be same ID several times. I need to compare the quantities where the quantity in product table doesn't match the total quantity in the stock table.
products
ID quantity
1 4
2 6
3 2
stock:
ID quantity
1 1
1 3
2 5
3 2
How can I get a result where there is a single ID per row? Expected result:
ID quantity as products quantity as stock
2 6 5
You can have a subquery that computes the total stocks, and LEFT JOIN it to your products table:
SELECT
products.ID,
products.quantity AS `quantity as products`,
total_stock.quantity AS `quantity as stock`
FROM
products
LEFT JOIN
-- We compute total quantities from `stock`
(SELECT
stock.ID, sum(stock.quantity) AS quantity
FROM
stock
GROUP BY
stock.ID
) AS total_stock
ON total_stock.ID = products.ID
WHERE
-- We want to find only discrepancies.
-- We use NOT <=> to safely check nulls.
NOT (total_stock.quantity <=> products.quantity)
ORDER BY
products.ID ;
I've assumed this schema (with a REFEFENCES constraint):
CREATE TABLE products
(
ID INTEGER PRIMARY KEY,
quantity INTEGER NOT NULL
) ;
CREATE TABLE stock
(
ID INTEGER NOT NULL REFERENCES products(ID),
quantity INTEGER NOT NULL
) ;
You can find your example data (together with a few extra data to care for nulls) and this solution at dbfiddle here
You can also change the WHERE clause to:
-- We use coalesce to convert nulls to 0 (we assume *don't know* means *don't have*)
coalesce(total_stock.quantity,0) <> coalesce(products.quantity, 0)
depending on your use-case.
dbfiddle here
References:
MySQL NULL-safe equal
MySQL COALESCE