SQL Database Roomrate Approaches - sql

I've developed a Hotel Management System some time ago. Difference is, that this system is used by several hotels and property owners who rent their homes in a single database. I can't just add prices to the roomtypes/accommodations as they might differ from one week or the other.
The approach i've used to overcome the issue of fixed stays (weekends, weeks, midweeks) for the mobile homes and the possibility to reserve hotelrooms (no-arrival days, minimum stays etc) is to store prices in a rate-table. Which is as follows:
tablename: Availability
int id
int roomTypeId
decimal rate
dateTime day
bit canArrive
int minimumStay
...
I am wondering, now the database is growing with more hotels and mobilehome's, if this approach is done properly or if there might be better ways instead of storing a rate for each roomtype and each date.

Sure!
It's important to note that, while rates can change daily, they usually don't (otherwise, any ads showing a rate would go rapidly obsolete - they could have been designed months before).
A simple, naive (first-iteration) design is as follows:
Hotel
=========
id -- autoincrement
name -- varchar(50)
contactInformation -- (address, phone, etc)
RoomType
==========
id -- autoincrement
description -- varchar(50)
HotelRoomTypeRate
==================
id -- autoincrement
hotelId -- fk reference to hotel.id
roomTypeId -- fk reference to roomType.id
rate -- decimal
effectiveOn -- date (this is 'business'/calendar day)
HotelRoom
===========
id -- autoincrement
hotelId -- fk reference to hotel.id
roomTypeId -- fk reference to roomType.id
status -- fk reference to status table, for things like 'under construction'
HotelCheckIn
==============
id -- autoincrement
hotelId -- fk reference to hotel.id
customerId -- fk reference to customer.id
hotelRoomId -- fk reference to hotelRoom.id
checkedInOn -- date (again, 'business'/calendar day)
HotelCheckOut
===============
id -- autoincrement
hotelCheckInId -- fk reference to hotelCheckIn.id
checkedOutOn -- date (again, 'business'/calendar day)
You would of course need to tweak this to suit your needs.

Related

Creating tables - integrity constraints

Create the following tables:
Customer
KNr (primary key)
Name (at most 15 characters)
City (at most 10 characters)
Country (at most 10 characters)
Balance (Type FLOAT)
Discount (Type FLOAT)
Products
PNr (greater than 1 and primary key)
Descr (not NULL, at most 10 characters and unique)
Weight (Type FLOAT)
Think about the integrity constraints for the columns Price, StorageLocation and Stock.
Orders
OrdNr (Type INTEGER, greater than 0 and primary key)
Mon (Type INTEGER, not NULL and between 1 and 12)
Day (Type INTEGER, not NULL and between 1 and 31)
PNr (Foreign Key)
KNr (Foreign Key)
The attributes Month, Day, Pnr and Knr must together be unique. Think about the integrity constraints for the columns Quantity, Sum and Status.
I have done the following :
For 1 :
CREATE TABLE Customer
(
KNr PRIMARY KEY,
Name CHAR(15),
City CHAR(10)
Country CHAR(10)
Balance FLOAT
Discount FLOAT
);
Is that correct?
For 2 :
CREATE TABLE Products
(
PNr PRIMARY KEY CHECK (PNr > 1) ,
Descr NOT NULL CHAR(10) UNIQUE.
Weight FLOAT
Price FLOAT CHECK (Price > 0) // Is checking if it is positive an integrity constraint?
StorageLocation CHAR(15) // What integrity constraint do we use here? If it is not Null for example?
Stock INTEGER // What integrity constraint do we use here? If it is not negative for example?
);
Is that correct?
For 3 :
CREATE TABLE Orders
(
BestNr INTEGER PRIMARY KEY CHECK (BestNr > 0) ,
Mon INTEGER NOT NULL CHECK(Mon >= 1 and Mon <=12)
Day INTEGER NOT NULL CHECK(Day >= 1 and Day <=31)
FOREIGN KEY (PNr) REFERENCES Customer (PNr),
FOREIGN KEY (KNr) REFERENCES Products (KNr)
Quantity INTEGER CHECK(Quantity >0) // It is the ordered quantity, or not? What integrity constraints can we consider?
Sum FLOAT // Is this the sum of invoices? Or what is this meant? What integrity constraints can we consider?
Status CHAR(20) // It is meant if is paid, delivered, etc? So this contains words, right? What integrity constraints can we consider?
UNIQUE (Mon, Day, Pnr, Knr)
);
Do we write that as in the last line that the attributes Month, Day, Pnr and Knr must together be unique ?
You are actually pretty close if viewed as logical model defining requirements. From a physical model however, the syntax is considerable off.
I will not do each table but just Orders, and I will slice and dice along the way, leaving some things you need to correct and some suggestions for your considerations.
First off If you want comment on your ddl you can do so, but they begin with -- instead of //. A better approach just use Comment On where they become part of the permanent record.
BestNr:
As a column name nothing wrong but is it clear what BestNr refers to, and what makes it better than any other number. Perhaps a better name would be Ord_nr. (But the is of course just an opinion). Declaring it as Primary comes with 2 automatic constraints: Not Null and Unique. Check constraint again there is nothing wrong. However a better process would be just tell the DBMS to generate identity column (see Create table ... generated ...).
Mon and Day:
Technically nothing wrong. However there is a data integrity hole as it still permits invalid date. The date Feb 30 would pass both your constraints. But it is still an invalid date. Other months have the same issue, day = 31 for a month with only 30 days passes the constraints but remains invalid. To ensure only valid dates just define a date column. This also eliminates the need for the check constraint. The month and date can be extracted when needed.
FOREIGN KEY (PNr) REFERENCES Customer (PNr): FOREIGN KEY (KNr) REFERENCES Products (KNr):
Your reference is backwards. PNr refers to Product, KNr to customer. However you must define them as columns then generate the FK. While nothing is wrong with these as columns names, are the descriptive of what they refer to. PNr perhaps, but not so KNr (unless Customer is always referred to as K...) Perhaps better prod_nr and cust_nr. (but perhaps no product reference at all - later).
Sum:
This column can easily be derived when needed, and will be difficult to keep current (what happens when another item is added to the Order, or Updated, or Deleted). Further this is a very poor choice for a column name as it is a SQL Standard reserved word (not by all RDBMS however, Postgres being one). Drop the column and derive it when needed.
Status:
You would want to constrain this to a set of predefined values. Either a CHECK constraint, an ENUM or a lookup (reference) table.
Normalization:
Consider normalizing a bit further. An order typically will contain multiple items (lines). These can/should be extracted into another table; call it Order_Lines and move PNr and Quantity into it.
Taking all the above into consideration arrive at:
-- method to constrain status
create type order_status as enum ('pending', 'picked', 'shipped', 'delivered', 'billed', 'paid', 'back ordered', 'on hold', 'canceled' ); -- or others
create table orders ( ord_nr integer generated always as identity primary key
, ord_dt date
, cust_nr integer references customers (cust_nr)
, status order_status -- questionable: Can it be derived?
, constraint one_per_cust_per_day unique (cust_nr, ord_dt) -- combine multiple orders for customer into 1 per day. ??
);
create table order_lines ( ord_ln_nr integer generated always as identity primary key -- optional
, ord_nr integer not null references orders(ord_nr)
, prod_nr integer not null references products(prod_nr)
, quantity integer not null check (quantity>0)
, price float -- Note1
, status order_status
, constraint one_ln_per_ord_prod unique ( ord_nr, prod_nr)
);
Note1: Normally do not copy columns from referenced tables. You normally avoid this as it creates duplicate data, just get the value through the reference. However, price tends to be a volatile column. If a price change occurs, we should not automatically apply that to existing orders. For this reason the Price from the Product will be copied when order is placed.

Problem with database design for Hotel Booking [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 3 years ago.
Improve this question
I am currently designing (as an assignment for the course at the university) a database for online booking of hotels worldwide and I have stumbled into a problem.
Before describing it, here are the tables of the relational model so far :
GUEST
guest_ID varchar PK
email varchar
guest_password varchar
first_name varchar
last_name varchar
mobile_num varchar
member_status varchar
pref_language varchar
pref_currency_code varchar
HOTEL
hotel_ID varchar PK
hotel_name varchar
ratings_avg int
phone_num varchar
email varchar
currency_code varchar
street_name varchar
street_num varchar
zip_code varchar
city varchar
country varchar
ROOM
room_ID varchar PK
hotel_ID varchar FK to Hotel
room_name varchar
low_season_rate numeric
high_season_rate numeric
max_persons int
BOOKING
guest_ID varchar FK to Guest
room_ID varchar FK to Room
check_in date
check_out date
(a combination of the first 4 as PK)
persons_num int
PAYMENT
guest_ID varchar FK to Guest
room_ID varchar FK to Room
date_paid timestamp
amount numeric
EVALUATION
guest_ID varchar FK to Guest
hotel_ID varchar FK to Hotel
eval_date date
(a combination of the first 3 as PK)
rating int
guest_comment text
I came up with this design thinking as follows:
The guests will book rooms, which belong to hotels, pay for the rooms and then, if they want, after their stay, evaluate the hotel.
So I considered Booking and Payment as relations between the Guest and the Room and the Evaluation as a relation between the Guest and the Hotel.
The problem this design seems to have is that the Payment and Evaluation are completely cut off from the Booking, so their tables can fill even without a preexisting booking having taken place.
And the way I see it now, a Guest pays for a Stay(=Booking) in a Room and evaluates a Stay(=Booking) in a Hotel, so I am thinking that these tables should refer to the Booking table.
But Booking is a relation, could I form a relation between an entity and another relation? Or is there another solution that I am missing?
I would welcome any thought about the subject.
First, your Payment and Evaluation are not "cut off" from Booking. You can make the relation across more than just one join. Secondly, remember there is an application on top of the database that will enforce some logic, such as not making bookings without a guest and not making guests without a booking.
If for some reason your requirements dictate that you enforce everything strictly in the database with keys, constraints, and triggers you will need to work on your schema a bit. I have a few suggestions on that below:
I like identity keys on my tables so that the database doesn't have to enforce combo key relations, I do this in the application layer (it gives me more freedom to make unexpected changes to schema and application later on).
You may want to take the rates out of the ROOM table. If they are updated, they are no longer applicable to previous BOOKINGs for the same hotel and room.
Payment and Evaluation only need a direct relation to a BOOKING. You can't have either without an actual stay.
imo a unique BOOKING is a HOTEL and ROOM for date range, so BOOKING should have a fk to HOTEL and ROOM.
How you handle GUEST relating to BOOKING is a little tricky. GUESTs being able to have more than one BOOKING is expected (imo) so you could put a fk to GUEST on BOOKING so that a BOOKING is now unique to a HOTEL, ROOM, GUEST, and date range.
But, if a BOOKING can have more than 1 GUEST, you need to make a relational lookup. Just GUESTid and BOOKINGid, so you can have a many::many and still relationally connect GUESTs to BOOKINGs
As long as you can make relational connections across all of your tables with joins, they do not need to all be directly related to all of the others.

Can a table title be a Primary Key?

I'm trying to retrofit some tables to an existing database. The existing database has equipment numbers and I'm trying to add in tables with more information on that equipment. Ideally, I'd like to make the table titles the ID numbers and set those as the PK of that table, making the ID numbers in the equipment list the FK.
Is it possible to set the table title as the PK? Here's an example of one of the tables, and I'd like to make "E0111" the PK.
CREATE TABLE E0111(
EQUIPMENT Varchar(200),
MAINTENANCE varchar(200),
CYCLE varchar(200)
);
No you can't do this because the primary key needs to be unique for every row of your table. If you "could" use the table name as the primary key it would be the same for every row.
You should use a unique column in your table as the primary key.
Also, I have no idea how you could achieve this with SQLite or any DBMS.
First thing before I even get anywhere near answering the question about the table names being primary keys, we need to take a step back.
You should NOT have a table for each piece of equipment.
You need an Equipment table, that will store all of your pieces of Equipment together. I assume you have that already in the existing database.
Hopefully it is keyed with a Unique Identifier AND an Equipment Number. The reason for having a separate Unique Identifier, is that your database server uses this for referential integrity and performance - this is not a value that you should show or use anywhere other than inside the database and between your database and whatever application you are using to modify the database. It should not typically be shown to the user.
The Equipment Number is the one you are familiar with (ie 'E0111'), because this is shown to the User and marked on reports etc. The two have different purposes and needs, so should not be combined into a single value.
I will take a stab at what your Equipment table may look like:
EquipmentId int -- database Id - used for primary key
EquipmentName Varchar(200) -- human readable
EquipmentDescription Text
PurchaseDate DateTime
SerialNumber VarChar(50)
Model Varchar(200)
etc..
To then add the Maintenance Cycle table as you propose above it would look like:
MaintenanceId int -- database Id - used for primary key this time for the maintenance table.
EquipmentId int -- foreign key - references the equipment table
MaintenanceType Varchar(200)
DatePerformed DateTime
MaintenanceResults VarChar(200)
NextMaintenanceDate DateTime
To get the results about the Maintenance Cycle for all equipment, you then JOIN the tables on the 2 EquipmentIds, ie
SELECT EquipmentName, EquipmentDescription, SerialNumber, MaintenanceType DatePerformed
FROM Equipment
JOIN MaintenanceCycle ON Equipment.EquipmentId = Maintenance.EquipmentId
WHERE EquipmentName = 'E0111'
You cannot make the name of the table a primary key.
All primary keys should be unique and a table column not table name. This is the general rule of thumb for a priamry key. There are plenty of resources on the internet about Primary keys.
Here are just afew:
http://www.w3schools.com/sql/sql_primarykey.asp
http://database-programmer.blogspot.co.uk/2008/01/database-skills-sane-approach-to.html
The name of a table should be descriptive of what is hold within it. See that data table as a drawer where you shall label what it contains.
In my humble point of view, the ID of an equipement shall only be labeled as-is on the equipement in question. Otherwise, in your database, it shall be the table Equipments that prevails with the ID of each piece of equipment you have.
Then, if you have other equipment-related information to save, add another table with the kind of information it shall contains, with the ID of the related equipment for which this information is saved.
For example, let's say we hold a maintenance schedule over your equipment.
Equipments
-----------------------------------------------------
Id | Description | Location | Brand | Model
-----------------------------------------------------
1 | Printer/Copier| 1st Floor | HP | PSC1000
Maintenances
---------------------------------------------------------
Id | Description | Date | EquipmentId | EmployeeId
---------------------------------------------------------
Note that the EmployeeId column shall be there only if one requires to know who did what maintenance and when, for instance.
MaintenanceCycles
--------------------------------------------
Id | Code | Description | EquipementId
--------------------------------------------
1 | M | Monthly | 1
This way, every equipment can have its cycle, and even multiple cycles per equipment if required. This lets you the flexibility that you need for further changes.

Database design for flagging system

I have a database which captures information relating to a patient for a medical practice. This information is spread across several tables:
Patient - For contact information
PatientMedicalHistory - For medical conditions unrelated to the current problem
PatientEpisode - Financial information for the current visit
PatientEpisodeReason - Stuff relating to why the patient is here today
I want to introduce a flag system, so that any messages will appear when bringing up the patient details. So for example, if the patient has had a heart attack previously this would need to be flagged (that info would be in PatientMedicalHistory).
My current approach is to set up a flag lookup table which defines the flag type, and the table/column that the flag is referring to and what the value would be in order to raise that flag:
CREATE TABLE FlagType
(
ID INT PRIMARY KEY IDENTITY,
TypeName NVARCHAR(300) NOT NULL,
Colour NVARCHAR(100) NOT NULL,
Urgency INT NOT NULL
)
CREATE TABLE Flag
(
ID INT PRIMARY KEY IDENTITY,
FlagTypeID INT NOT NULL REFERENCES FlagType(ID),
TableName NVARCHAR(300) NOT NULL,
FieldName NVARCHAR(300) NOT NULL,
FlagValue NVARCHAR(300) NOT NULL
)
This seemed all very well, but then trying to write either a) a stored procedure that doesn't resemble a mess or b) a LINQ query that doesn't kill performance seems difficult.
Is there any alternatives to this? The issue is that the flag could be defined on any column in any of the tables above. This totals about 80 columns in total.
You can add three fields to PatientMedicalHistory table: organ, disease, criticality.
When a patient comes in with a problem, you can pull patient history based on the same organ or disease presented in the current episode where the criticality meets a certain threshold. What you are doing here is classifying your patient history data so it relates to the current episode data.

Advanced (?) SQL Joins?

I am a bit lost as to how to explain this, so I will try to give an example of some tables (+ data) and then the result that I am after (all my table columns are NOT NULL):
Table: Customers
Id int primary key
Name varchar(250)
Table: Stats (Date, CustomerId is the primary key)
Date (date)
CustomerId (int) - foreign key to Customers table
Earning (money)
Table: Bonus
Id int primary key
CustomerId int - foreign key to Customers table
Date date
Amount money
Table: Payments
Id int primary key
DateFrom date,
DateTo date,
CustomerId bigint - foreign key to Customers table
Table: CampaignPayment
Id int primary key
PaymentId int - foreign key to payments table
Quantity int
UnitPrice money
Table: BonusPayment
Id int primary key
PaymentId int - foreign key to payments table
Amount money
The idea here is that everytime a customer does something that is supposed to earn them money, it goes into the stats table. Customers can also receive different kinds of bonuses which goes into the bonus table. Every so often I need to create an invoice for the customers (Payments table) which will list the stuff from the stats table + the bonus table within the specified time period and that will generate the invoice (that is the payments table defines who the invoice is for, which period and the campaignpayment and bonuspayment table defines what is being paid and why).
Now - I need to be able to join all these tables up to be able to get an output of the following:
CustomerId | CustomerName | PaymentId | Amount | BonusAmount | DateFrom | DateTo
Amount is the summed Amount ( SUM(Quantity * UnitPrice) ) from the CampaignPayment table, and BonusAmount is the summed Amount ( SUM(Amount) ) from the BonusPayment table. DateFrom and DateTo is from the Payments table.
The trick is that for every customer within a given month where every single day of that month is not covered, I want a row with the following data:
CustomerId | CustomerName | NULL | (Stats.Earning - Amount Earned from possible payments within the month) | (Bonus.Amount - Amount Earned possible bonuses that is in payments within the month) | First day of month | Last day of month
I may need a bit more of complex logic as to how to calculate the amount and bonus amount within these "empty" rows but as for now, that is what I need to begin with.
How would I go about this? I know how to get the "initial" bit done, but how would I go about adding in these "empty" rows? I hope I explained the problem well enough in detail and that you can see the idea here - if not let me know and I will try to explain further.
The database is MS SQL Server 2008.
EDIT: Also alternatively an "empty" row for every customer per month is also and acceptable solution.
I'd make an auxiliary table with "every single day of that month" to ease identifying if "every single day of the month is not covered" (a somewhat ambiguous spec, but the aux table should help whether you mean "no day is covered" or "some days are not covered" and whether a day is considered "covered" if it has either a bonus or stats, or if it needs to have both to be considered "covered" -- these ambiguities are why I'm not going to even try and sketch the SQL using this aux table;-). Then I'd UNION the "empty rows" to the "initial bit" that you already know how to get done -- seems a perfect task for UNION!-)