Optimal way to store units of measure for Stock items - database

Assuming a schema structure as such.
-----------------------------------------
Stock (ID, Description, blah blah)
-----------------------------------------
StockBarcode (ID, StockID, Barcode, Price, blah blah)
-----------------------------------------
What is the optimal way of storing units of measure for your stock items? Given that the StockBarcode Price may be for 1 item, for 10 Items, for 10 grams or for 10 pounds?
Stock to StockBarcode is a one to many relationship. (Although Im sure you didnt need me telling you that)
Cheers :)

You might think about putting an additional UOM column on ALL tables where Qty fields are and additional Currency column on all money columns.
ALL entry screens should ask Qty and UOM.
Item table - add Inventory/Stocking UOM, Purchasing/Receive UOM,
Pricing UOM, Shipping/Send UOM, Production UOM, Component UOM columns
new UOM table - ID, Abbrev., Description, RegionID, UOMTypeID
new UOMRegion table - ID, Code, Description (example data - 1, UK,
United Kingdom;
2, US, United States;
3, INT, International)
new UOMType table - ID, Code, Description, DefaultUOMID (example
data - 1, V, Volume, 15;
2, A, Area, 45;
3, W, Weight, 32;
etc.)
new UOMConversionFactor table - ID, FromUOMID, ToUOMID,
ConversionFactor (example data - 1, 1, 1, 1;
2, 1, 3, 0.026021066655565;
3, 3, 1, 38.430399999999000)
(notes - Conversion from UOM to same UOM is 1. May put in table or
not.
Each record I usually have an implied column FromQty which is always 1.
Make sure ConversionFactor allows for HUGE numbers so final numbers turn out more accurate when large Qty are involved)
Thoughts - 1) some UOM are not specific enough such as "barrel" (there is a US dry goods barrel, a US barrel for cranberries, U.S. fluid barrel, U.K. a beer barrel, US beer barrel, oil barrel, etc.), 2) UOM is impacted by region if you are worrying about an international application (ie. a US cup is different from UK cup that is different than the international cup), 3) I can get a receive a cartoon of item X, store it in pallets, and ship it in eaches. 4) "kit" or "build" items can be raw materials in UOM plus various components in different UOM that ultimately results in a final product in a different UOM.

I'd be adding Qty and UOMID columns to the StockBarcode table and then a new table like
StockUOM (ID, Description)

If i understand your stock table correctly, it consists of the products that you are stocking to sell.
One option to consider is that instead of stock data maybe you should consider keeping the data based on what in stock keeping parlance, is referred to as SKU (stock keeping unit) Information.
Each SKU itself can be made of more than 1 item but as it cannot be sold that way, you dont have to concern yourself with those details in most scenarios. Details like price etc are all properties that are then associated with the SKU.
Eg: If a product say beer can be sold individually / 6 pack / 12 pack then it has 3 SKU's associated with it.
Then you have relationships :
Products --> SKU's which is 1:many
SKU --> StockBarCode which is 1:1 (assuming you have the same bar code for all the units of the same SKU - if not then it can be 1: Many as well)

Is there a unique StockBarcode for every UOM? For example, is there a barcode for grams, a barcode for pounds and a barcode for individual items? If so, Andrew's solution will work.
If not, you will need to create another table that contains the StockID, Qty and UOMID's.
StockUOM (ID, Description)
StockCount (ID, StockID, UOMID, Qty)
When you scan the barcode, you will need to enter in what UOM you are scanning for. Then the software can update the StockCount table based on item scanned. This could be a good fallback in case your items don't have more than one barcode, and you are stocking more than one UOM (common).

Related

For each maker who has models at least in one of the tables PC, Laptop, or Printer, determine the maximum price for his products. Output: maker;

TASK: for each maker who has models at least in one of the tables PC, Laptop, or Printer, determine the maximum price for his products. Output: maker; if there are NULL values among the prices for the products of a given maker, display NULL for this maker, otherwise, the maximum price.
This is task 41 from this website: http://www.sql-ex.ru/
Database description:
The database scheme consists of four tables:
Product (maker, model, type)
PC (code, model, speed, ram, hd, cd, price)
Laptop (code, model, speed, ram, hd, screen, price)
Printer (code, model, color, type, price)
The Product table contains data on the maker, model number, and type of product ('PC', 'Laptop', or 'Printer'). It is assumed that model numbers in the Product table are unique for all makers and product types. Each personal computer in the PC table is unambiguously identified by a unique code, and is additionally characterized by its model (foreign key referring to the Product table), processor speed (in MHz) – speed field, RAM capacity (in Mb) - ram, hard disk drive capacity (in Gb) – hd, CD-ROM speed (e.g, '4x') - cd, and its price.
The Laptop table is similar to the PC table, except that instead of the CD-ROM speed, it contains the screen size (in inches) – screen. For each printer model in the Printer table, its output type (‘y’ for color and ‘n’ for monochrome) – color field, printing technology ('Laser', 'Jet', or 'Matrix') – type, and price are specified.
I've tried several queries, most of them worked on the first database but not on the second one
Here's my last tried query:
SELECT DISTINCT
maker,
CASE
WHEN price IS NULL
THEN NULL
ELSE MAX(price) OVER (PARTITION by maker)
END max_p
FROM
(SELECT DISTINCT maker, price
FROM product
LEFT JOIN pc ON pc.model = product.model
WHERE product.model IN (SELECT model FROM pc)
UNION
SELECT DISTINCT maker, price
FROM product
LEFT JOIN laptop ON laptop.model = product.model
WHERE product.model IN (SELECT model FROM laptop)
UNION
SELECT DISTINCT maker, price FROM product
LEFT JOIN printer ON printer.model = product.model
WHERE product.model IN (SELECT model FROM printer)) as x
Your query returned the correct dataset on the first (available) database, but it returned incorrect dataset on the second checking database.
Wrong number of records (more by 3)
Most expensive product per maker, or null if any of the maker's prices are null:
SELECT
pro.maker,
NULLIF(MAX(COALESCE(pri.price, 922337203685477)), 922337203685477) as price
FROM
product pro
INNER JOIN
(
SELECT model, price FROM printer
UNION ALL
SELECT model, price FROM pc
UNION ALL
SELECT model, price FROM laptop
) pri
ON pri.model = pro.model
GROUP BY pro.maker
How it works:
Strip the laptop, pc and printer tables out to just model and price (including any null prices) and union then together to provide a consistent data set. Join this to product to get the maker. Group by the maker looking for the max price. Because we have to show null as the max price of there is any null price we COALESCE to make all the null prices a massively high price so they will surely be the max price, then we turn that massively high price back into null with NULLIF
NULLIF is proprietary. I could do the same thing with case when max(...) = 922... then null else max(...) end But it's more wordy
Trivia:
922337203685477 Is almost the max value for a MONEY; I omitted the fractional component
Also consider:
Two of the three hardware tables have a type column. It is not stated whether this relates to the type column of the product table. If it is you'll have to add it into the join. For the table that lacks a type, null it and make the join condition include "or pri.type is null"

Every customer, one item, multiple orders

My question is actually healthcare related, but I'm going to provide a couple of mock customer,sales driven questions to make this question more practical for most users on this site.
Say I have one table with all the following columns:
CustomerID
CustomerName
OrderID
OrderDate
Region
ItemID
ItemName
SalespersonID
SalespersonName
Since everything is in the same table, I don't believe that any joins are necessary.
Question
Let's say that I run a store that pays its Sales people in the commission that requires customers make appointments. The commission is based on the number of times a customer has returned to that sales person.
How can I find out which customers have only had appointments with the same sales person and have never seen a different sales person?
Alternatively, I can provide another approach to solving this problem.
Is there a way to find out if there are customers that have only bought one item multiple times for separate orders?
For example, if Customer A, Jennifer Smith, really likes one specialty product, Extra Blue Caviar, and Jennifer buys Extra Blue Caviar every three months and has never bought another item in the store. Also, let's say Customer B, John Smith, buys Extra Fragrant Jasmine Rice every week and has never bought another item in the store.
How do I get a list of Jennifer Smith and John Smith along with Extra Blue Caviar and Extra Fragrant Jasmine Rice based on the criteria that they have only ever bought one item from the store multiple times?
Thank you so much!
For option #2
create table #t ( c_ID int, c_Name varchar(20),
o_ID int, o_date datetime, r int,
i_ID int, i_name varchar(20),
s_ID int, s_Name varchar(20))
;with cte as ( select
c_ID,
c_Name,
i_ID,
i_name,
count(distinct i_id) over (partition by c_ID) num_diffent_items,
count(*) over (partition by c_ID, i_ID) number_of_purchases
from #t)
select distinct
c_ID,
c_Name,
i_ID,
i_name,
number_of_purchases
from cte where num_diffent_items = 1

Normalisation / 3nf in database

I have this table (Day, Employee no, Employee Name, Client no, Client name, Product no, Product Description, Unit Price, Quantity Sold, Total Sales)
Example data :
22nd, e123, Dave, c1264, Sheila, p15462, Hoover, 4.50, 5, --
----, ----, -----, c64402, Jovek, p673431, Kettle, 2.25, 10, 45
----, e4215, Johnny, c785, Keith, p15462, Hoober, 4.50, 2, 9
23rd, e123, Dave, c64402, Jovek, p673431, Kettle, 2.25, 20, --
So basically, there is only data for the day if its a new day entry, and there is only a total for total sales, after all the sales that an employee has made.
So far, I have got :
Product(ProdNo#,ProductDesc,Price)
Client(ClientNo#, ClientName)
Employee(EmployeeNo#, EmployeeName)
However, I'm unsure what to do regarding the individual sales and total sales for every day?
Thanks for any help!
This is typical example for the use of a star- or snowflake schema.
The tables Product, Customer, Employee are called dimension tables.
The fact table has foreign keys to the dimension table and contains a quantity, e.g. the concrete sale.
Sales(ProdNo#, ClientNo#, EmployeeNo#, Sale)
The primary key of is a compound key of ProdNo#, ClientNo#, EmployeeNo#.
Total sales would not be modelled explicitly but queried via aggregation, e.g.
SELECT P.ProdNo, SUM(S.sale)
FROM Products P, Sales S
WHERE P.Price > 1000 AND S.ProdNo = P.ProdNo
GROUP BY P.ProdNo
The RDBMS or data warehouse will optimize the query, so total sales do need to be modeled explicitly (no denormalization).

How can I associate a single record with one or more PKs

If I had a single record that represented, say, a sellable item:
ItemID | Name
-------------
101 | Chips
102 | Candy bar
103 | Beer
I need to create a relationship between these items and one or more different types of PKs. For instance, a company might have an inventory that included chips; a store might have an inventory that includes chips and a candy bar, and the night shift might carry chips, candy bars, and beer. The is that we have different kinds of IDs: CompanyID, StoreID, ShiftID respectively.
My first though was "Oh just create link tables that link Company to inventory items, Stores to inventory items, and shifts to inventory items" and that way if I needed to look up the inventory collection for any of those entities, I could query them explicitly. However, the UI shows that I should be able to compile a list arbitrarily (e.g. show me all inventory items for company a, all west valley stores and Team BrewHa who is at an east valley store) and then display them grouped by their respective entity:
Company A
---------
- Chips
West Valley 1
-------------
- Chips
- Candy Bar
West Valley 2
-------------
- Chips
BrewHa (East Valley 6)
--------------------
- Chips
- Candy Bar
- Beer
So again, my first though was to base the query on the provided information (what kinds of IDs did they give me) and then just union them together with some extra info for grouping (candidate keys like IDType+ID) so that the result looked kind of like this:
IDType | ID | InventoryItemID
------------------------------
1 |100 | 1
2 |200 | 1
2 |200 | 2
2 |201 | 1
3 |300 | 1
3 |300 | 2
3 |300 | 3
I guess this would work, but it seems incredibly inefficient and contrived to me; I'm not even sure how the parameters of that sproc would work... So my question to everyone is: is this even the right approach? Can anyone explain alternative or better approaches to solve the problem of creating and managing these relationships?
It's hard to ascertain what you want as I don't know the purpose/use of this data. I'm not well-versed in normalization, but perhaps a star schema might work for you. Please keep in mind, I'm using my best guess for the terminology. What I was thinking would look like this:
tbl_Current_Inventory(Fact Table) records current Inventory
InventoryID INT NOT NULL FOREIGN KEY REFERENCES tbl_Inventory(ID),
CompanyID INT NULL FOREIGN KEY REFERENCES tbl_Company(ID),
StoreID INT NULL FOREIGN KEY REFERENCES tbl_Store(ID),
ShiftID INT NULL FOREIGN KEY REFERENCES tbl_Shift(ID),
Shipped_Date DATE --not really sure, just an example,
CONSTRAINT clustered_unique CLUSTERED(InventoryID,CompanyID,StoreID,ShiftID)
tbl_Inventory(Fact Table 2)
ID NOT NULL INT,
ProductID INT NOT NULL FOREIGN KEY REFERENCES tbl_Product(ID),
PRIMARY KEY(ID,ProductID)
tbl_Store(Fact Table 3)
ID INT PRIMARY KEY,
CompanyID INT FOREIGN KEY REFERENCES tbl_Company(ID),
RegionID INT FOREIGN KEY REFERENCES tbl_Region(ID)
tbl_Product(Dimension Table)
ID INT PRIMARY KEY,
Product_Name VARCHAR(25)
tbl_Company(Dimension Table)
ID INT PRIMARY KEY,
Company_Name VARCHAR(25)
tbl_Region(Dimension Table)
ID PRIMARY KEY,
Region_Name VARCHAR(25)
tbl_Shift(Dimension Table)
ID INT PRIMARY KEY,
Shift_Name VARCHAR(25)
Start_Time TIME,
End_Time TIME
So a little explanation. Each dimension table holds only distinct values like tbl_Region. Lists each region's name once and an ID.
Now for tbl_Current_Inventory, that will hold all the columns. I have companyID and StoreID both in their for a reason. Because this table can hold company inventory information(NULL StoreID and NULL shiftID) AND it can hold Store Inventory information.
Then as for querying this, I would create a view that joins each table, then simply query the view. Then of course there's indexes, but I don't think you asked for that. Also notice I only had like one column per dimension table. My guess is that you'll probably have more columns then just the name of something.
Overall, this helps eliminate a lot of duplicate data. And strikes a good balance at performance and not overly complicated data structure. Really though, if you slap a view on it, and query the view, it should perform quite well especially if you add some good indexes.
This may not be a perfect solution or even the one you need, but hopefully it at least gives you some ideas or some direction.
If you need any more explanation or anything else, just let me know.
In a normalized database, you implement a many-to-many relationship by creating a table that defines the relationships between entities just as you thought initially. It might seem contrived, but it gives you the functionality you need. In your case I would create a table for the relationship called something like "Carries" with the primary key of (ProductId, StoreId, ShiftId). Sometimes you can break normalization rules for performance, but it comes with side effects.
I recommend picking up a good book on designing relational databases. Here's a starter on a few topics:
http://en.wikipedia.org/wiki/Entity%E2%80%93relationship_model
http://en.wikipedia.org/wiki/Database_normalization
You need to break it down to inventory belongs to a store and a shift
Inventory does does not belong to a company - a store belongs to a company
If the company holds inventory directly then I would create a store name warehouse
A store belongs to a region
Don't design for the UI - put the data in 3NF
Tables:
Company ID, name
Store ID, name
Region ID, name
Product ID, name
Shift ID, name
CompanyToStore CompanyID, StoreID (composite PK)
RegionToStore RegionID, StoreID (composite PK)
Inventory StoreID, ShiftID, ProductID (composite PK)
The composite PK are not just efficient they prevent duplicates
The join tables should have their own ID as PK
Let the relationships they are managing be the PK
If you want to report by company across all shifts you would have a query like this
select distinct store.Name, Product.Name
from Inventory
join Store
on Inventory.StoreID = Store.ID
join CompanyToStore
on Store.ID = CompanyToStore.StoreID
and CompanyToStore.CompanyID = X
store count in a region
select RegionName, count(*)
from RegionToStore
join Region
on Region.ID = RegionToStore.RegionID
group by RegionName

Best structure for inventory database

I want to create a small database for my inventory but I have some problems on picking a structure. The inventory will be updated daily at the end of the day.
The problem I am facing is the following.
I have a table for my products, having an
id, name, price, quantity.
Now I have another table for my sales, but there is my problem. What kind of fields do I need to have. At the end of the day I want to store a record like this:
20 product_x $ 5,00 $ 100,-
20 product_y $ 5,00 $ 100,-
20 product_z $ 5,00 $ 100,-
20 product_a $ 5,00 $ 100,-
-------------------------------------------------
$ 400,-
So how do I model this in a sales record. Do I just create a concatenated record with the product id's comma separated.
Or is there another way do model this the right way.
This is a model which supports many aspects,
Supports Sites, Locations and Warehouses etc.
Supports Categorization and Grouping
Support Generic Product (Ex. "Table Clock" and specific product "Citizen C123 Multi Alarm Clock" )
Also support Brand Variants (by various manufacturers)
Has CSM (color / size / model support) Ex. Bata Sandles (Color 45 Inch Blue color)
Product Instances with serials (such as TVs , Refrigerators etc.)
Lot control / Batch control with serial numbers.
Pack Size / UOM and UOM Conversion
Manufacturer and Brands as well as Suppliers
Also included example transaction table (Purchase order)
There are many other transaction types such as Issues, Transfers, Adjustments etc.
Hope this would help. Please let me know if you need further information on each table.
Cheers...!!!
Wajira Weerasinghe.
Sites
id
site_code
Site_name
Warehouse
id
site_id
warehouse_code
warehouse_name
Item Category
id
category_code
category_name
Item Group
id
group_code
group_name
Generic Product
id
generic_name
Product
id
product_code
category_id
group_id
brand_id
generic_id
model_id/part_id
product_name
product_description
product_price (current rate)
has_instances(y/n)
has_lots (y/n)
has_attributes
default_uom
pack_size
average_cost
single_unit_product_code (for packs)
dimension_group (pointing to dimensions)
lot_information
warranty_terms (general not specific)
is_active
deleted
product attribute type (color/size etc.)
id
attribute_name
product_attribute
id
product_id
attribute_id
product attribute value (this product -> red)
id
product_attribute_id
value
product_instance
id
product_id
instance_name (as given by manufacturer)
serial_number
brand_id (is this brand)
stock_id (stock record pointing qih, location etc.)
lot_information (lot_id)
warranty_terms
product attribute value id (if applicable)
product lot
id
lot_code/batch_code
date_manufactured
date_expiry
product attribute value id (if applicable)
Brand
id
manufacturer_id
brand_code
brand_name
Brand Manufacturer
id
manufacturer_name
Stock
id
product_id
warehouse_id, zone_id, level_id, rack_id etc.
quantity in hand
product attribute value id (if applicable) [we have 4 red color items etc.]
Product Price Records
product_id
from_date
product_price
Purchase Order Header
id
supplier_id
purchase_date
total_amount
Purchase Order Line
id
po_id
product_id
unit_price
quantity
Supplier
id
supplier_code
supplier_name
supplier_type
product_uom
id
uom_name
product_uom_conversion
id
from_uom_id
to_uom_id
conversion_rule
I'd have a table with a row per item per day - store the date, the item ID, the quantity sold, and the price sold at (store this even though it's also in the product table - if that changes, you want the value you actually sold at preserved). You can compute totals per item-day and totals per day in queries.
Tables:
create table product (
id integer primary key,
name varchar(100) not null,
price decimal(6,2) not null,
inventory integer not null
);
create table sale (
saledate date not null,
product_id integer not null references product,
quantity integer not null,
price decimal(6,2) not null,
primary key (saledate, product_id)
);
Reporting on a day:
select s.product_id, p.name, s.quantity, s.price, (s.quantity * s.price) as total
from product p, sale s
where p.id = s.product_id
and s.saledate = date '2010-12-5';
Reporting on all days:
select saledate, sum(quantity * price) as total
from sale
group by saledate
order by saledate;
A nice master report over all days, with a summary line:
select *
from (
(select s.saledate, s.product_id, p.name, s.quantity, s.price, (s.quantity * s.price) as total
from product p, sale s
where p.id = s.product_id)
union
(select saledate, NULL, 'TOTAL', sum(quantity), NULL, sum(quantity * price) as total
from sale group by saledate)
) as summedsales
order by saledate, product_id;
Try modelling your sales as a transaction - with a "header", i.e. who sold to, when sold, invoice # (if applicable), etc. and "line items", i.e. 20 * product_x # $5 = $100. The safest approach is to avoid relying upon prices etc. from the products table - as these will presumably change over time, and instead copy much of the product information (if not all) into your line item - so even when prices, item descriptions etc. change, the transaction information remains as was at the time the transaction was made.
Inventory can get quite complex to model. First you need to understand that you need to be able to tell the value of the inventory onhand based on what you paid for it. This means you cannot rely on a product table that is updated to the current price. While you might want such a table to help you figure out what to sell it for, there are tax reasons why you need to know the actual vlaue you paid for each item in the warehouse.
So first you need the product table (you might want to make sure you have an updated date column in this, it can be handy to know if your prices seem out of date).
Then you need a table that stores the actual warehouse location of each part and the price at purchase. If the items are large enough, you need a way to individually mark each item, so that you know what was taken out. Usually people use barcodes for that. This table needs to be updated to record that the part is no longer there when you sell it. I prefer to make the record inactive and have a link to my sales data to that record, so I know exactly what I paid for and what I sold each part for.
Sales should have at least two tables. One for the general information about the sale, the customername (there should also be a customer table most of the time to get this data from), the date, where it was shipped to etc.
Then a sales detail table that includes a record for each line item in the order. Include all the data you need about the part, color, size, quantity, price. This is not denormalizing, this is storing historical data. The one thing you do not want to do is rely on the prices in the product table for anything except the inital entry to this table. You do not want to do a sales report and have the numbers come out wrong becasue the product prices changed the day before.
Do not design an inventory database without consulting with an accountant or specialist in taxes. You also should do some reading on internal controls. It is easy to steal from a company undetected that has not done their work on internal controls in the database.
I think you need a table with fields showing the transaction properties per customer
OR
a table with fields - date, product(foreign), quantity - this way you'll have no problem with new products
Try multiple tables with links
table_products
id
name
table_product_sales
id
product_id
quantity
price_per
transaction_time AS DATETIME
SELECT table_product_sales.*, table_product.name
FROM table_product
JOIN table_product_sales
ON table_product_sales.product_id = table_product.id
GROUP BY DATE(transaction_time)
Haven't tried it but will something like that work? That allows you to keep each transactions separate so you can query things like average number sold per sale, total sold per date, total sales each day, etc.

Resources