I have done rental systems for boats and reservation for rooms and tours apart.
But how could I build up a database which I could use for multiple type of items? It means the items can be rooms, trips, journeys, cars, boats, etc...
Thanks!
Use a table for your items with a reference to an ItemType table:
Item
ItemId ItemType HireDate Hirer
1 2 1 Jan 200
2 3 3 Jan 201
3 6 4 Jan 207
ItemType
ItemTypeID Desc
2 Boat
3 Car
4 Bike
In our rental system I can share the sort of table that we use.
You will need a reservation orders table, and a inventory index that gets updated after each order. Or else you will have terribly slow inventory lookups.
Reservation Orders:
ProductID StartDate EndDate Quantity
Then everytime an order is placed or you manually add or remove inventory, update the inventory index:
Inventory Index:
ProductID Date InventoryAvailable
You will have an entry for each date in your Inventory Index so it will get pretty large, but else there is no fast way to check inventory.
We use this in our rental system here: rental booking software example
I can share other tables we use if that will help, let me know.
Related
I have to build a cube based on date range records, and not sure about the best way to proceed.
Imagine say a cube of Cars and warranty periods. Each car has a start date, and an end of warranty periods. Then there may be extended warranty periods.. so imagine
CAR REG TYPE WARRANTY START WARRANTY END
CAR A PURCHASE 01/01/2016 31/01/2016
CAR A EXTENDED 01/01/2017 30/06/2017
CAR A EXTENDED 01/08/2017 30/01/2018 -- note, gap here
CAR B PURCHASE 01/01/2016 31/01/2016
CAR B EXTENDED 01/01/2017 30/06/2017
CAR B EXTENDED 01/08/2017 30/01/2018 -- note, gap here
So multiple items, with multiple date ranges. There is a main table (CARS) with car details (colour, model, etc).
Now I want to build a cube, which is reportable at month level, cars under warranty/warranty type, etc.
So plan 1 was to build a view which explodes the above out by a join to a date table, report by month, and feed this into a cube. But, the number of cars multiplied by the months covered leads to multi hundreds of milions of rows - which means sometimes the server runs out of TempDB space, and when it does run, the cube takes hours to build.
Is there a better way - such as a view for the car details, and then another view on the warranty table (how do I get SSAS to deal with months in a date range) - will the join in SSAS be more efficient than a join in a view in SQL?
Thanks all.
You can connect start and end columns to time dimension. And on the report you can use ":" operator to build date tange report.
More details you will find here: http://www.purplefrogsystems.com/blog/2013/04/mdx-between-start-date-and-end-date/
One approach which will work with drag-and-drop client tools like Excel or Power BI would be a many-to-many Date dimension. Since car A and B match, let's pretend there's a car C which has a warranty from 2015-07-30 to 2015-12-31.
Create a DimWarrantyDateRangeKey which represents a unique combination of dates during which a warranty is active. The surrogate key is WarrantyDateRangeKey. Certainly the ETL that builds this table will be a bit expensive, but given the size of your data I think it's a worthwhile investment that will produce better query performance than if your m2m bridge table has one row per active day per car.
Each car should be assigned one WarrantyDateRangeKey. Add the WarrantyDateRangeKey column to your fact tables...
CAR REG WarrantyDateRangeKey
A 1
B 1
C 2
m2mWarrantyDateRange
WarrantyDateRangeKey DateKey
1 20160101
1 20160102
1 ...
1 20170629
1 20170630
1 20170801
1 20170802
1 ...
1 20180129
1 20180130
2 20150701
2 20150702
2 ...
2 20151230
2 20151231
The tables relate together as follows...
FactTable -> DimWarrantyDateRange <- m2mWarrantyDateRange -> DimDate
Then in the cube you DimWarrantyDateRange should be a dimension, m2mWarrantyDateRange should be a measure group with a count measure. DimDate should be a dimension. Then you should relate DimDate to FactTable as a many-to-many (m2m) dimension using m2mWarrantyDateRange as the intermediate measure group.
Now in Excel or Power BI you should be able to filter to a particular date and it will filter down to the cars which had an active warranty on that day.
I will try to be as specific as possible, but I am having trouble conceptualizing the problem. As a hobby I am trying to design a NFL database that takes raw statistics and stores it for future evaluation for fantasy league analysis. One of the primary things I want to see is if certain players/teams perform well against specific teams and which defenses are suspect to either pass/run. The issue I am having is trying to design the schedule/event table. My current model is as follows.
TEAMS
TeamID, Team
SCHEDULE
ScheduleID, TeamID, OpponentID, Season, Week, Home_Away, PointsFor, PointsAgainst
In this scenario I will be duplicating every game, but when I use an event table where I use TeamAway and TeamHome I find my queries impossible to run since I have to query both AwayTeam and HomeTeam to find the event for a specific team.
In general though I cannot get a query to work where I have two relationships from a table back to one table, even in the schedule table my query does not work.
I have also considered dropping the team table and just storing NE, PIT, etc. for the Team and Opponent fields so I do not have to deal with the cross-relationships back to the team table.
How can I design this so I am not running queries for TeamID = OpponentID AND TeamID?
I am doing this in MS Access.
Edit
The issue I am having is when I query two table: Team (TeamID, Team) and Event(TeamHomeID, TeamAwayID), that had relationships built between the TeamID - TeamHomeID, and TeamID - TeamWayID I had issues building the query in ms Access.
The SQL would look something like:
SELECT Teams.ID, Teams.Team, Event.HomeTeam
FROM Teams INNER JOIN (Event INNER JOIN Result ON Event.ID = Result.EventID)
ON (Teams.ID = Result.LosingTeamID) AND (Teams.ID = Result.WinningTeamID)
AND (Teams.Team = Event.AwayTeam) AND (Teams.Team = Event.HomeTeam);
It was looking for teams that had IDs of both the losing team and the winning team (which does not exist).
I think I might have fixed this problem. I didn't realize the Relationships in database design are only default, and that within the Query builder I could change the joins on which a particular query is built. I discovered this by deleting all the AND portions of the SQL statement returned, and was able to return the name of all winnings teams.
This is an interesting concept - and good practice.
First off - it sounds like you need to narrow down exactly what kind of data you want so you know what to store. I mean, hell, what about storing the weather conditions?
I would keep Team, but I would also add City (because Teams could switch cities).
I would keep Games (Schedule) with columns GameID, HomeTeamID, AwayTeamID, ScheduleDate.
I would have another table Results with columns ResultID, GameID, WinningTeamID, LosingTeamID, Draw (Y/N).
Data could look like
TeamID | TeamName | City
------------------------
1 | PATS | NE
------------------------
2 | PACKERS | GB
GameID | HomeTeamID | AwayTeamID | ScheduleDate | Preseason
-----------------------------------------------------------
1 | 1 | 2 | 1/1/2016 | N
ResultID | GameID | WinningTeamID | LosingTeamID | Draw
------------------------------------------------------------
1 | 1 | 1 | 2 | N
Given that, you could pretty easily give any W/L/D for any Scheduled Game and date, you could easily SUM a Teams wins, their wins when they were home, away, during preseason or regular season, their wins against a particular team, etc.
I guess if you wanted to get really technical you could even create a Season table that stores SeasonID, StartDate, EndDate. This would just make sure you were 100% able to tell what games were played in which season (between what dates) and from there you could infer weather statistics, whether or not a player was out during that time frame, etc.
I'm creating a game that needs to save each players' statistics (games played, exp & gold gained, current gold) on a daily basis, as well as the all-time statistics.
My current approach is I have 3 tables:
table `stats_current` -> for storing player's stats on CURRENT DAY
player_id | games_played | gold_earned | current_gold
table `stats_all_time` -> player's stats accumulated from the very beginning
player_id | games_played | gold_earned | current_gold
table `stats_history` -> player's stats daily, one record for one day
player_id | date | games_played | gold_earned | current_gold
Each player has one record on stats_current, one record on stats_all_time, and limited records on stats_history table (for example, only last 30 days are recorded).
Then there's a daemon / cron job that do these operation on a daily basis:
For each players:
Search for its record on stats_current, get the values.
Insert new record to stats_history, values are from stats_current
Update record on stats_all_time, increment its values with values from stats_current
On stats_current, reset the values of games_played, gold_earned to 0. But leave the current_gold as it is.
Solutions for common tasks:
Get player X's current gold: retrieve current_gold from stats_current
Get player X's stats in last 7 days: select 6 records from stats_history, plus the today's record in stats_current
Get player X's total games played: retrieve values from stats_history
Questions:
Is this a viable approach?
What are the weaknesses?
Is there any way to optimize this solution?
Your approach fails to take advantage of the power of SQL
stats_history
To get today's stats, just use
SELECT * FROM stats_history WHERE Date = CURDATE() and PlayerId = PlayerId--Depending on your RDBMS you might need a different function to get the date.
To get all time stats just use
SELECT SUM(games_played) as games_played, SUM(gold_earned) as gold_earned FROM stats_history WHERE PlayerId = playerid
You could just pull current gold by selecting the top record from stats_history for that player, or by using any of a number of other RMDBS specific strategies (Over clause for SQL Server, Ordering the result set by date and adding current_gold for MySQL etc.)
Your approach is risky because if your Chron ever fails, the other two tables will be inaccurate. It's also uneccessary duplication of data.
So We have our basic tables for the categories, products, and variants of products
categories
id | name | active | parent_id
products
id | name | price | active
c_p_link
category_id | product_id
variants
id | product_id | price | price_override | active | stock
Which works great.
But I have two queries.
The first being how to structure the orders.
We have an orders table
id | customer_id | ordered | status
And we also have a order_products table
id | order_id ..?
this is the one I am curious about.
Say a customer orders 30 of a product. do we
Add 30 rows, and add the price for each individual item on each row.
Add one row, add the combined total onto the row
Add one row, add the individual price onto the row
The next part is, later we are expecting to add voucher support to the cart. e.g. 10% off, buy two, get one free etc. the overall design of this I am not too fussed about right now (this is a couple of months off at least). but I am wondering if that is going to affect which version of the order_products table I should choose?
Disclaimer: I have never written a database model dealing the "Shopping Carts" or "Orders"'
I think the price at time of purchase should be encoded into the purchase data: just like a paper receipt from a store. Let's call this total_price which represents each itemized "line" on the receipt and should not be confused with total_purchase_price.
That is, the amount charged is fixed. It doesn't matter if the product price changes later and changes to prices should not reflect in how much was [to be] paid.
Thus I would have these fields: product, unit_price, quantity, total_price. A computed column of say, base_total_price (unit_price * quantity) can be easily added if required.
Now, the total_price might be a computed value based on say base_total_price * precent_discount field: but, whatever it ends up being, I hold that total_price should exist and should be fixed at time of purchase. (This implies that, if it is a computed column, all inputs are also fixed at time of purchase.)
Addendum: As stated above, I've never designed a model like this before, but one thing I have observed at stores is discounts being applied as a negative cost itemized item. That is, items are bought "at full price" and then the register adds an entry to offset the cost per whatever promotional is occuring. I do not know the merits/reasoning of such an approach.
simply add quantity of product to your order_products table :)
I prefer the 3rd solution, i think it's the best for the performance of your database..
I need to develop a web app that allows companies to forecast financials.
the app has different screens, one for defining employee salaries, another for sales projections etc..
basically turn an excel financial forecast model into an app.
question is, what would be the best way to design the database, so that financial reports (e.g. a profit and loss statement or balance sheet) could be quickly generated?
assuming the forecast period is for 5 years, would you have a table
with 5 years*12 months = 60 fields per each row? is that performant
enough?
would you use DB triggers to recalculate salary expenses whenever a single employee data is changed?
I'd think it would be better to store each month's forecast in its own row in a table that looks like this
month forecast
----- --------
1 30000
2 31000
3 28000
... ...
60 52000
Then you can use the aggregate functions to calculate forecast reports, discounted cash flow etc. ( Like if you want the un-discounted total for just 4 years):
SELECT SUM(forecast) from FORECASTS where month=>1 and month<=48
For salary expenses, I would think that having a view that does calculations on the fly (or if you DB engine supports "materialized views" should have sufficient performance unless we're talking some giant number of employees or really slow DB.
Maybe have a salary history table, that trigger populates when employee data changes/payroll runs
employeeId month Salary
---------- ----- ------
1 1 4000
2 1 3000
3 1 5000
1 2 4100
2 2 3100
3 2 4800
... ... ...
Then again, you can do SUM or other aggregate function to get to the reported data.