Database Design: Inventory with multilevel packaging - sql

I'm trying to develop a database for my inventory. But I have no idea how to keep track of multilevel packaging.
For example:
I currently have a products and positions table
products
Id | Name
================
1013 | Metal
1014 | Wood
positions
id | Name
================
1 | 1-1-1-1
2 | 1-1-1-2
And my inventory table I was thinking of doing something like this:
Let's say I stored 1 box with 1000 Metal and 1 box with 500 Wood at position 1-1-1-1
ItemId | ProductId | Quantity | PositionId
==========================================
1 | 1013 | 1000 | 1
2 | 1014 | 500 | 1
So I'll label those two boxes with a barcode 1 and 2 respectively, so if I scan them, I can check this table to see the product and quantity inside them.
But I can also put these 2 boxes (1 and 2) inside another box (let's call it box 3), which would generate a new barcode for it that, if scanned, will show both previous boxes and its items. And store this box 3 in another position
And I can also put this box 3 inside a pallet, generating a new code and so on. So basically I can multilevel package N times.
What is the best table structure to keep track of all of this? Thanks in advance for any help!

I would add another column to the products table, make it a BIT and maybe call it BOM, BillOfMaterials, or whatever makes sense to you
So your products Table would look like this
Then you could create another table called BillOfMaterials
Quantity is how many of your products are needed to make up your new product. So for this example 2 metal and 1 wood make a pencil.

I was able to make a good structure:
My products and positions are the same but I created a stock table like:
id | product_id | amount | parent_id | position_id
=====================================================
1 | 1013 | 1000 | 4 | 1
2 | 1013 | 1000 | 4 | 1
3 | 1014 | 500 | 4 | 1
4 | 1234 | NULL | NULL | 1
The 1234 (random id) is a box that contains 2000 metal and 500 wood. I dont save this box in the product table.
When I scan the box with id 3, I perform a recursive cte query:
with recursive bom as (
select *, 1 as level
from testing.stock
where id = '4' #scanned id
union all
select c.*, p.level + 1
from testing.stock c
join bom p on c.parent_id = p.id
)
select product_id as product, sum(amount), position_id
from bom b
left join testing.product pd on b.product_id = pd.id
where pd.id is not null
group by product_id, position_id
which returns:
sum | product | position
2000 | 1013 | 3
500 | 1014 | 3
to get by position I just run a variation of the above query. To perform an update I get the Ids inside that box and run a
update testing.stock set position = '2' where id in (#variation of above query)
I hope this helps someone. This works for N packaging level

Related

Split a row into multiple rows based on a column value

I am trying to split a record in a table to 2 records based on a column value. The input table displays the 3 types of products and their price. For a specific product (row) only its corresponding column has value. The other columns have Null.
My requirement is - whenever the product column value (in a row) is composite (i.e. has more than one product, e.g. Bolt + Brush), the record must be split into two rows - 1 row each for the composite product types.
So, in this example, notice how the 2nd row (in the input) gets split into 2 rows -> 1 row for "Bolt" and another for the "Brush", with their price extracted from their corresponding columns (i.e in this case, "Bolt" = $3.99 and "Brush" = $6.99)
Note: For composite product values there can be at most 2 products as shown in this example (e.g. Bolt + Brush)
CustId | Product | Hammer | Bolt | Brush
--------------------------------
12345 | Hammer | $5.99 | Null | Null
53762 | **Bolt+Brush** | Null | $3.99 | $4.99
43883 | Brush | Null | Null | $4.99
I have tried creating 2 predetermined records via UNION ALL using a CTE and then main_table Left Outer Join with CTE, so that the join yields 2 records instead.
#CustId | Product | Price #
12345 | Hammer | $5.99
**53762** | **Bolt** | $3.99
**53762** | **Brush** | $4.99
43883 | Brush | $4.99
This has to be solved by Spark-SQL only.
I think this will work:
select CustId, 'Hammer' as product, Hammer
from t
where Product like '%Hammer%'
union all
select CustId, 'Bolt' as product, Bolt
from t
where Product like '%Bolt%'
union all
select CustId, 'Brush' as product, Brush
from t
where Product like '%Brush%';
This would work also
select custid, product,
case when product like '%Hammer%' then hammer
when product like '%Bolt%' then bolt
else brush end as Price from
(select custid, explode(split(product,'\\+')) as product, hammer, bolt, brush
from t) x;

Database design for products with multiple units

I am designing a database for retail business using Sql server as backend. There are some products that can be sold in multiple units, for example, pencils can be sold in ea and dozen, paper can be sold in sheet, ream, and canton. Basically, each product can be sold in more than one unit.
The App needs to supports
Can receive products from suppliers in many unit. Sometime we might
order 1 pencil and the next time we order 2 boxes of pencil.
Can sell products in multiple unit, for example, we must be able to
sell 1 box and 2 pencils in the same bill.
App also need supports for FIFO or LIFO
Below is my initial design
Table: Products
ProductId | Barcode | Name | BaseUnitId
1 | XXXX | Pencil | 1
Table: Units
UnitId | Name
1 | Each / Pieces
2 | Box
Table: UnitConversion
ProductId | BaseUnitId | Multiplier | ToUnitId |
1 | 1 | 24 | 2 | // 24 pencils in a box
Table: Inventories
Id | ProductId | UnitId | Quantity
1 | 1 | 1 | 48 //In pieces
Table Invoices
Id | ProductId | UnitId | Quantity
1 | 1 | 2 | 1.5 //Sold/Purchased 1.5 boxes that means 18 pieces
Is there any flaws in my design? Is there anything that I miss? This can't be a new problem. Does anyone have any ideas (or examples)?
I have a few suggestions:
It seems like you could remove the UnitConversion table and just store the Multiplier value against the Unit record (so you'd store 1 against Each / Pieces and 24 against Box). Then the conversion would just be the 'From' unit's quantity divided by the 'To' unit's quantity.
Is is possible that different units would have different barcodes? If so, the barcode could be stored against the Unit record instead.
In your Inventories and Invoices tables, the ProductId column might be unnecessary as you could get this by joining Units table.
To support FIFO or LIFO, you're going to need to store more specific information about your stock, so there's some way of knowing the date it was booked in, the quantity remaining, and maybe some way of identifying that specific item (or group of items).
Hope this helps!
EDIT: Your Inventories table could look something like this:
Id | UnitId | [identifier] | CurrentQuantity | DateAdded
1 | 1 | ABC123 | 20 | 2017-01-10
2 | 1 | ABC124 | 96 | 2017-01-12
The [identifier] column (name's up to you!) would store some way of identifying the physical stock, this could be something that the users assign on receipt of the item, or maybe their suppliers would already have added some that could be used.
To implement FIFO in a scenario where someone wants to buy 24 pencils, you know you need to take 20 from the group of items labelled 'ABC123' and 4 labelled 'ABC124'.

Select nonexisting attributes

I've 3 tables named products, attributes and a relation table product_attributes. You can see them with example entries below.
products
id| product_name
1 | Product 1
2 | Product 2
attributes
id| attribute
1 | length
2 | width
3 | height
4 | weight
5 | color
product_attributes
id| product_id| attribute_id| value
1 | 1 | 1 | 120
2 | 1 | 2 | 60
3 | 2 | 1 | 100
4 | 2 | 3 | 40
5 | 2 | 5 | red
To show Product Details:
Product 1
Length: 120
Width: 60
Unlinked attributes for Product 1:
Height, weight, color
Product 2
Length: 100
Height: 40
Color: Red
Unlinked attributes for Product 2:
Width, Weight
The thing I want to do is mainly, when i select a product, get unlinked attributes.
For example, when I select Product 1, sql will return
Height, weight, color
I know it can be done with PHP but also know it can be done with SQL, too.
I just wanted to brainstorm a bit.
How it can be done with SQL? Especially in MySQL?
To fetch non-existing attributes for a specified product, i think you should use this query,
note that, without inner select query this query will fetch attributes whichs are not used for any of products.
SELECT attribute FROM attributes a
LEFT JOIN (SELECT * FROM product_attributes pa WHERE pa.product_id = 1 ) pa ON a.id = pa.id
WHERE pa.id IS NULL
I don't think it is the best way, but it should work.
SELECT attribute FROM attributes
WHERE id NOT IN (
SELECT attribute_id FROM product_attributes
WHERE product_id = '1')

Grouping issue in SSRS

So I have a table that has divisions and sub-divisions. The structure is as follows:
COMPANYID | DIVISIONID | DIVISION | PARENTID
1 | 1 | North | NULL
1 | 2 | South | NULL
1 | 3 | East | NULL
1 | 4 | West | NULL
1 | 5 | Sales | 1
1 | 6 | Service | 1
1 | 7 | Sales | 2
1 | 8 | Service | 2
1 | 9 | Sales | 3
1 | 10 | Service | 3
1 | 11 | Sales | 4
1 | 12 | Service | 4
The idea behind the table is that the data would indicate all of those divisions/subdivisions are the for the same company. Anything with a null PARENTID is a "Division" and anything that has a PARENTID is a "Sub-Division". There will be a max of 2 tiers (Division --> Subdivision). This table would create the following hierarchy:
Company 1
North
Sales
Service
South
Sales
Service
East
Sales
Service
West
Sales
Service
I am trying to make an SSRS report that shows details about the company including a table with all the divisions and sub-divisions. I am using the following query to retrieve a list of all the divisions and subdivisions:
SELECT division, parentid
FROM division
WHERE companyid=#companyid
#companyid is a parameter for the report. My thought process is that I want create a table that has a Parent group and a Detail group where the Parent group would be all those rows that have a null PARENTID and then the Detail group would list all rows whose PARENTID equals the divisionid of Parent row. I'm just not sure how to set that up in the groups. Filters? Separate queries? Do it in the SQL query?
Any and all help is greatly appreciated! Thanks!
For a simple hierarchy with a maximum of 2 levels you can just join the table on itself:
SELECT D1.CompanyId, D1.DivisionId, D1.Division, D2.Division AS Subdivision
FROM DivisionTable D1
INNER JOIN DivisionTable D2 ON D1.DivisionId = D2.ParentId AND D1.CompanyId = D2.CompanyId
WHERE D1.CompanyId = #CompanyId
If i understood your goal correctly, you want the report to display the information in hierarchy mode.
If so you can achieve it with setting a Recursive Hierarchy Group.
Reporting services has the ability to display "hierarchy" information in a "tree" layout
For more information look here
You don't have to create another query the first one that has the CompanyID And the ParentID is just fine
You just need your select statement as a dataset and SSRS can do the grouping for you. You can even tier on multiple levels or better yet collapse and expand your groupings based on filters.
How to get started:
Create your dataset as you have shown
Put in a table object from the toolbox
Only put in the columns of detail data
In the lower left of the 'Design' layout click the 'detail's and click 'add Group>Parent Group'. Select your grouping
If you want you can 'collapse' on demand the details columns. You can choose 'Visibility' on the detail grouping. 'When the report is initially run'>Choose 'Hide'. Check 'Display can be toggled by this report item': Select a cell reference to what you want to expand the details with.

Advance Query with Join

I'm trying to convert a product table that contains all the detail of the product into separate tables in SQL. I've got everything done except for duplicated descriptor details.
The problem I am having all the products have size/color/style/other that many other products contain. I want to only have one size or color descriptor for all the items and reuse the "ID" for all the product which I believe is a Parent key to the Product ID which is a ...Foreign Key. The only problem is that every descriptor would have multiple Foreign Keys assigned to it. So I was thinking on the fly just have it skip figuring out a Foreign Parent key for each descriptor and just check to see if that descriptor exist and if it does use its Key for the descriptor.
Data Table
PI Colo Sz OTHER
1 | Blue | 5 | Vintage
2 | Blue | 6 | Vintage
3 | Blac | 5 | Simple
4 | Blac | 6 | Simple
===================================
Its destination table is this
===================================
DI Description
1 | Blue
2 | Blac
3 | 5
4 | 6
6 | Vintage
7 | Simple
=============================
Select Data.Table
Unique.Data.Table.Colo
Unique.Data.Table.Sz
Unique.Data.Table.Other
=======================================
Then the dual part of the questions after we create all the descriptors how to do a new query and assign the product ID to the descriptors.
PI| DI
1 | 1
1 | 3
1 | 4
2 | 1
2 | 3
2 | 4
By figuring out how to do this I should be able to duplicate this pattern for all 300 + columns in the product. Some of these fields are 60+ characters large so its going to save a ton of space.
Do I use a Array?
Okay, if I understand you correctly, you want all unique attributes converted from columns into rows in a single table (detailstable) that has an id and a description field:
Assuming the schema:
datatable
------------------
PI [PK]
Colo
Sz
OTHER
detailstable
------------------
DI [PK]
Description
You can first get all of the unique attributes into its own table with:
INSERT INTO detailstable (Description)
SELECT
a.description
FROM
(
SELECT DISTINCT Colo AS description
FROM datatable
UNION
SELECT DISTINCT Sz AS description
FROM datatable
UNION
SELECT DISTINCT OTHER AS description
FROM datatable
) a
Then to link up the datatable to the detailstable, I'm assuming you have a cross-reference table defined like:
datadetails
------------------
PI [PK]
DI [PK]
You can then do:
INSERT INTO datadetails (PI, DI)
SELECT
a.PI
b.DI
FROM
datatable a
INNER JOIN
detailstable b ON b.Description IN (a.Colo, a.Sz, a.OTHER)
I reckon you want to split description table for different categories, like - colorDescription, sizeDescription etc.
If that is not practical then I would recommend having an extra column showing an category attribute:
DI Description Category
1 | Blue | Color
2 | Blac | Color
3 | 5 | Size
4 | 6 | Size
6 | Vintage | Other
7 | Simple | Other
And then have primary key in this table as combination of ID and Category column.
This will have less chances for injecting any data errors. It will be also easy to track that down.