Database Design: Order Items with quantities spanning multiple statuses - sql

I have a requirement to create a database that holds orders with items for each order.
This would be the traditional table setup
Order Table
Id (pk)
CustomerId (fk)
1
1
Item Table
Id (pk)
OrderId (fk)
StatusId (fk)
Quantity
1
1
1
1000
ItemStatus Table
(Don't worry about how the data knows which status is first, second, third, etc)
Id (pk)
Name
Description
IsStart
IsTerminal
1
New
For newly created items
1
0
2
Materials Ordered
For indicating that raw materials are ordered
0
0
3
Pre-Fabrication
For receiving materials and gathering other resources
0
0
4
In Work
For indicating that the assembly process is underway
0
0
5
Staging
For preparing the items for shipping
0
0
6
Shipped
For indicating that items are complete and no longer in the facility
0
1
However
I have the requirement to take the above quantity of 1000 and break it down by status as it pertains to the business workflow.
My initial implementation looked something like this, but I wanted to reach out and see if there is a better design.
Modified Item Table
Id (pk)
OrderId (fk)
1
1
QuantityBreakdown Table
Id (pk)
OrderItemId (fk)
StatusId (fk)
Quantity
1
1
2
200
2
1
3
200
3
1
4
400
4
1
5
200
Edit
Here are some examples in layman terms to help clarify expected solution. All scenarios will be simplified by only having a single item. Also, the handling of ordering materials is out of scope; I just need to know that the item is waiting.
In these examples, we will be handling the creation process of a burger (item #1). In more advanced scenarios, we could add another ITEM such as fries (that would be item #2)
Example 1
A restaurant order is created with 1 burger. All materials needed for the assembly of the burger are on-hand; therefore, the burger will progress through the statuses with all quantities (New => Prep => Cooking => Packaged => Delivered).
Example 2
A restaurant order is created with 2 burgers. Only enough materials for one burger are on-hand; therefore, the item quantity needs to be split. Since we don't want the customer waiting, the first burger will progress through the statuses with a quantity of 1. While the second burger will have to wait in a new status called Pending. Then once the materials are available, the second burger may continue the workflow.

Well I cannot just comment to ask for clarifications.
But I would have each OrderItem be its on distinct item in an order with its own Status (status in OrderItem). So if in fact you had 4 sets of the same item, each would have its own status.
You could always group-by if you want the total # of each OrderItemId

Related

Only Show unique Customers per date cohort for repeat purchase rate

Scenario:
I have a table that has all of the customer purchases by Month and each month has a period. Within that table I am showing the customers that have made purchases in each Month/Period. What I am trying to figure out is how to exclude any customer that made a purchase in the previous month so that the repeat purchases are only for unique customers. The data looks like the following:
customer_email
cohortMonth
month_number
orders_for_period
abc#gmail.com
10/2019
0
2
def#gmail.com
10/2019
0
1
ghi#gmail.com
10/2019
0
1
def#gmail.com
10/2019
1
1
abc#gmail.com
10/2019
1
1
def#gmail.com
10/2019
2
1
In the Table above for Month_number=0 we have 3 total customers and within this period customer abc#gmail.com was the only repeat customer because they have 2 orders. This would show as a 33% repeat purchase rate for month_number 0. For Month_number=1 we have 2 customers that have purchased again in the period but only def#gmail.com is unique as abc#gmail.com already made the purchase. This would then bring the repeat_rate to 66% as now 2 customers have comeback and purchased out of the 3 that originally purchased.
cohortMonth
month_number
repeat_purchase_rate
10/2019
0
33%
10/2019
1
66%
10/2019
2
66%
With every unique customer that purchases in the subsequent periods we want to add that to the total to understand the repeat rate at a cumulative level.
I have tried a ton of different ways to figure this out but backing out the customers that made purchases in the previous period and only showing the unique customers is where I am struggling at. Any help is greatly appreciated!
Side Note: Whenever I format a table it looks like how I want it to look in the preview but then when I review I get the error :"Your post appears to contain code that is not properly formatted as code. Please indent all code by 4 spaces using the code toolbar button or the CTRL+K keyboard shortcut. For more editing help, click the [?] toolbar icon."
I then indent and it breaks the way the table looks. Any help on that would be great as well. Thank you

SQL-Script to Automatically build pallets

I have code that I am using to call out how many pallets an item would be able to build. The issue I am having is I cannot figure out how to then automatically build pallets based on this number.
For example, if I have an item that can build 3.50 pallets, then I would want that to go into a new table that will then call out Pallet #1 and then a unique name, Pallet #2 then a unique name, Pallet #3 then a unique name. There will then be left over of .50.
I would want to then look in the category and find a ITEM that could fit with the .50 pallet item and try to make a whole pallet. In the table below, the remaining .50 of ITEM '1111' would be paired with ITEM '3333' which is .350 to then make Pallet #4, this process would then loop and find all the other combos based on the category to make the closest full pallet.
Ideally I would like the first pallets to be made on anything greater then 1, by giveng them a unique name, then using the leftovers to combine with other leftovers(items <1) to make other potentially full pallets, with a threshold of between 1 and .85:
ITEM CUBIC_INCHES PALLETS CATEGORY RANKING
1111 100,000 3.50 HIGH_TOP 1
2222 50,000 1.75 LOW_TOP 1
3333 10,000 .350 HIGH_TOP 2
4444 5,000 .175 LOW_TOP 2
My code that I am using to make this table is:
SELECT
ITEM
,CUBIC_INCHES
,CAST(CASE WHEN CUBIC_INCHES=0 THEN 0
ELSE (CUBIC_INCHES)/28,571.42 END AS DEC(38,4)) AS PALLETS
,CATEGORY
,RANK() OVER (PARTITION BY CATEGORY ORDER BY CUBIC_INCHES) AS RANKING
FROM TABLE_1

SQL - Tracking student exam records as they move between schools

I'd like to pick some of your glorious minds for an optimal solution to my dilemma.
Scenario:
Schools have children and children take tests.
The tests point to the child, not the school.
If the child moves school, the test records are taken to the new school and the previous school has no record of the test being done as they are linked to the child.
Obviously, this isn't ideal and is the result of the database not being designed with this in mind. What would the correct course of action be; I’ve currently identified the 3 possibilities listed below which would solve the current problem. However, i cannot be sure which is best for the issue at hand - and if any better solutions exist.
Have each test store the school & student within the test records (requiring current records to be updated & increasing the size of the database)
Create a new child record, duplicating the existing data for the new school with a new ID so the test remains linked to the previous school (complicating the ability to identify previous test scores)
Separately keep track of moves to other schools, then use this additional table to identify current and previous using the timestamps (increased complexity and computational requirements)
EDIT:
So i tried to use a basic example, but requests for the task at hand have been requested.
Here's the DB Schema for the tables (simplified for problem, note: Postnatal is not important):
Patients: ID, MidwifeID, TeamID
Midwives: ID
Groups: ID
GroupsMidwives: MidwifeID, GroupsID
PatientObservations: ID, MidwifeID, PatientID
Using a query as follows:
SELECT Some Information
from Postnatals
JOIN Midwives on Postnatals.MidwifeID = Midwives.ID
JOIN Patients on Patients.PatientID = Postnatals.PatientID
JOIN GroupsMidwives on GroupsMidwives.MidwifeID = Midwives.ID
JOIN Groups on Groups.ID = GroupsMidwives.GroupID
JOIN PatientObservations on PatientObservations.PatientID =
Postnatals.PatientID
WHERE groups.Name = ?
*some extra checks*
GROUP BY Midwives.Firstname, Midwives.Surname, Midwives.ID
However, in the event that a midwife is moved to a different team, the data associated with the previous team is now owned by the newly assigned team. As described in the example detailed previously.
Thus a modification (which modification is yet to be realised) is required to make the data submitted - prior to a team change - assigned to the previous team, as of current, because of the way the records are owned by the midwife, this is not possible.
You should below suggestion as per you concern.
Step 1 ) You need to create School Master Table
ID | School | IsActive
1 | ABC | 1
2 | XYZ | 1
Step 2 ) You need to create Children Master having school id as foreign key
ID | School | Children Name| IsActive
1 | 2 | Mak | 1
2 | 2 | Jak | 1
Step 3 ) You need to create test table having children id as foreign key
ID | Children_id | Test Name | IsActive
1 | 2 | Math | 1
2 | 2 | Eng | 1
Now whenever child moves school then make child record inactive and create another active record with new school. This will help you to bifurcate the old test and new test.
do let me know in case morehelp required

SQL group related rows in a list

I'm a bit stuck with this...
I have items table:
id | name
1 | item 1
2 | item 2
3 | item 3
4 | item 4
and related items table:
id | item_id | related_item_id
2 | 1 | 2
3 | 1 | 4
so this means that item 1 is related to items 2 and 4.
Now I'm trying to display these in a list where related items follow always the main item they are related to:
item 1
item 2
item 4
item 3
Then I can visually show that these items 2 and 4 are related to item one and draw something like:
item 1
-- item 2
-- item 4
item 3
To be honest, haven't got any ideas myself. I quess I could query for items which are not related to any other item and get a list of "parent items" and then query relations separately in a script loop. This is not definately the sexiest solution...
I am assuming that this question is about ordering the items list, without duplicates. That is, a given item does not have more than one parent (which I ask in a comment).
If so, you can do this with a left outer join and cleverness in the order by.
select coalesce(r.related_item_id, i.id) as item_id
from items i left join
related r
on i.id = r.related_item_id
order by coalesce(r.item_id, i.id),
(r.related_item_id is null) desc;
The left outer join identifies parents because they will not have any rows that match. If so, the coalesce() finds them and uses the item id.
In my opinion , rather than implementing this logic in a query , you should move it to your actual code.
assuming that item_ids are sequential, you can find the largest number of item_id, then in a loop
you can find related_item_id to each item_id and make a convenient data structure out of it.
This functionality comes under the category of hierarchical queries. In Oracle its handled by connect by clause not sure about mysql. But you can search "hierarchical queries mysql" to get the answer.

Multiple entries with the same reference in a table with SQL

In a unique table, I have multiple lines with the same reference information (ID). For the same day, customers had drink and the Appreciation is either 1 (yes) or 0 (no).
Table
ID DAY Drink Appreciation
1 1 Coffee 1
1 1 Tea 0
1 1 Soda 1
2 1 Coffee 1
2 1 Tea 1
3 1 Coffee 0
3 1 Tea 0
3 1 Iced Tea 1
I first tried to see who appreciated a certain drink, which is obviously very simple
Select ID, max(appreciation)
from table
where (day=1 and drink='coffee' and appreciation=1)
or (day=1 and drink='tea' and appreciation=1)
Since I am not even interested in the drink, I used max to remove duplicates and keep only the lane with the highest appreciation.
But what I want to do now is to see who in fact appreciated every drink they had. Again, I am not interested in every lane in the end, but only the ID and the appreciation. How can I modify my where to have it done on every single ID? Adding the ID in the condition is also not and option. I tried switching or for and, but it doesn't return any value. How could I do this?
This should do the trick:
SELECT ID
FROM table
WHERE DRINK IN ('coffee','tea') -- or whatever else filter you want.
group by ID
HAVING MIN(appreciation) > 0
What it does is:
It looks for the minimum appreciation and see to it that that is bigger than 0 for all lines in the group. And the group is the ID, as defined in the group by clause.
as you can see i'm using the having clause, because you can't have aggregate functions in the where section.
Of course you can join other tables into the query as you like. Just be carefull not to add some unwanted filter by joining, which might reduce your dataset in this query.