Database Normalization - mysql

I've been given the task of normalizing this set of data:
COURSE=(CourseID, CourseName, CourseDuration, CourseFee{
DelegateID, DelegateName, DelegateAddress, EventID, EventName, VenueID, VenueName, VenuePrice, BookingID, BookingType, BookingDate
})
The scenario is an IT company that runs short training courses at various hotels around the country, each event being hosted by one or more presenters (hence the BookingType - either Delegate or Presenter, if Presenter then no booking charge). An event is just an instance of a course running at a particular venue. VenuePrice refers to the cost for optional bed and breakfast at the hotel venue for the intervening nights
Here is what I have come up with for 3NF:
COURSE=(CourseID, CourseName, CourseDuration, CourseFee)
DELEGATE=(DelegateID, DelegateName, DelegateAddress)
EVENT=(EventID, VenueID*, CourseID*, EventName, EventDate)
BOOKING=(BookingID, DelegateID*, EventID*, BookingDate, BookingType)
VENUE=(VenueID, VenueName, VenuePrice)
I'd like to know if this is at all accurate, and if not, perhaps a guiding hand in the right direction?
Thanks

On what basis do you think that is in 3NF ?
Let's take a really simple example, Course. Where exactly if he Functional Dependency, on what key ? How can CourseName be dependent on CourseId, when CourseDuration and CourseFee are dependent on CourseName ?
Same with the rest of the tables; Event being a little more complex, has a few more errors.
You cannot normalise, or achieve 3NF, when your starting point is to stick an ID on everything that moves.
No. First normalise the data. Achieve 3NF. I can understand a CourseCode or ShortName as something the user may use to identify course, but no Id.
After that, if and only if you need to, add an Id column and the additional index.

Related

How do I make a query with variables submitted by the user, and save it?

I'm a database noob.
I'm building a database with a bunch of properties.
I want to make it so that the property owner (the user) can submit his/her property to my property table, by only calling a function/stored routine/stored procedure/prepared statement (I googled them, but having a hard time figuring out which I'm looking for).
Here is my insert query code:
INSERT INTO property
(property_owner_id, property_type_id, address, zip_code, area_m2, price_€)
VALUES
(/* Owner ID */,/* Property Type ID */,/* Address */,/* Zip Code */,/* Area */,/* Price */);
So I would want to create something like a submit function that I could store.
Then, the property owner would only need to do something like:
submit(his id, his property's type, address, zip code, area, price)
I'm searching for answers and I see stuff with "#", other times I don't.
I'm also don't quite yet understand the whole "function/stored routine/stored procedure/prepared statement" thing.
It also seems to change from language to language.
In some examples, I need to set the variable/parameter beforehand. In others I don't.
I just started learning SQL. I know how a database works in general.
I know how to create tables, update them, insert data, create primary and foreign keys.
I know how to query data using join, left join, cross join, etc. and the where, having, order by, group by, etc. commands.
But as of right now, I don't really understand how to make a reusable block of code where I can just call its name, and insert the variables/parameters only.
If it helps, I'm using MySQL Workbench 6.2.5 with UwAmp.
Thanks!
You probably already read about the stored procedures but maybe this link is helpful.
To your question:
You want a procedure that can accept a parameter when it is called.
CREATE PROCEDURE addtoproperty(
IN ownerid INT,
IN propertytype INT,
IN adress VARCHAR,
IN zipcode INT,
IN area VARCHAR,
IN price DECIMAL(15,2)
)
BEGIN
INSERT INTO property
(property_owner_id, property_type_id, address, zip_code, area_m2, price_€)
VALUES
(ownerid,propertytype, adress, zipcode, area, price);
END;
And you would use this procedure with following statement. You need to replace the text with the text with the actual values this time. So ownerid would be something like 12345.
CALL addtoproperty(ownerid,propertytype, adress, zipcode, area, price);

Best Practice for Master-Detail relationship when ALL combinations are required

I need to understand best practice for below scenario:
1) A school can have multiple classes (grades)
2) A school can have multiple events
3) Event is associated with minimum one class or it can be all school event (i.e. applicable for all classes in the school)
I have typical table structure
Event table (to store events), Class (to store class) and event_class (association table)
1) I insert a row inside 'event_class' table when an event gets associated with class
2) If it is a school event then, assuming a school has 20 classes, I insert 20 records inside 'event_class' table
In theory I know above is correct and would work.
My question is: In case when no of classes get increased from 20 to <>; what's should be the approach? If it is an all school event then shall I just store a flag at header level and use left/right join to get list of events? I am trying to understand what's normally practice.
Thanks in advance
Manisha
I don't know if this would count as 'Best' practice, but I've certainly seen it a lot in the wild, and therefore is probably at least 'Good' practice.
The Event table should probably have a 'type' column - defined by you, could be numeric or text ('class' or 'school', 0 or 1, etc.). Then, there would only be entries in the event_class table for events of type 'class'.
When retrieving class-specific data for class_id, your join logic would look like:
SELECT * FROM event, event_class
WHERE event_class.class_id = this_class.id
AND event.type = 'class'
AND event.id = event_class.event_id
If you wanted all the class AND school event data for class_id, it would look like:
SELECT * FROM event, event_class
WHERE (event_class.class_id = this_class.id
AND event.type = 'class'
AND event.id = event_class.event_id)
OR event.type = 'school'
Mind the parentheses on the second one to make sure the boolean logic works correctly. None of this is tested I'm afraid - just an idea. There are probably ways of optimising the joins, but for 20 classes, it's likely not worth the effort.

Database Table Design guidance

I am creating a table of pilots with the following fields, would there be any advantage to breaking the table down at each category shown into separate tables and link via the pilots primary key in a one to one relationship?
PILOT PROFILE
username
password
BASIC INFO
first_name
last_name
email
date_of_birth
address_line1
address_line2
town_city
county
postcode
country
tel_no
mobile_no
MEDICAL INFO
med_class
med_issued
med_special
med_verified
med_verified_date
med_verified_by
LICENCE INFO
licence_number
licence_type
licence_tailwheel
licence_retractableGear
licence_vpProp
licence_turboCharged
licence_cabinPressurisation
licence_sep_L
licence_sep_S
licence_mep_L
licence_mep_S
licence_tmg
licence_night
licence_imc
licence_ir
licence_fi
licence_fe
licence_other
licence_verified
licence_verified_date
licence_verified_by
FLIGHT EXPERIENCE
home_airport
hours_total
hours_pic
hours_in12months
hours_verified
hours_verified_date
hours_verified_by
PAYMENT
paid_date
No. Don't break up the row data in this context.
For reasons of performance at scale, sometimes it makes sense to break a table 1:1 into other tables, but given the information provided, this will absolutely not be necessary.
However, on a per-query basis, please do SELECT only the fields needed. Meaning: instead of using SELECT * at whim, use something like SELECT username, password FROM pilot. Side note: don't store your passwords clear text :)

How to do a MYSQL conditional select statement

Background
I'm faced with the following problem, relating to three tables
class_sectors table contains three categories of classes
classes table contains a list of classes students can attend
class_choices contains the first, second and third class choice of the student, for each sector. So for sector 1 Student_A has class_1 as first choihce, class_3 as second choice and class_10 as third choice for example, then for sector 2 he has another three choices, etc...
The class_choices table has these columns:
kp_choice_id | kf_personID | kf_sectorID | kf_classID | preference | assigned
I think the column names are self explanatory. preference is either 1, 2 or 3. And assigned is a boolean set to 1 once we have reviewed a student's choices and assigned them to a class.
Problem:
Writing an sql query that tells the students what class they are assigned to for each sector. If their class hasn't been assigned, it should default to show their first preference.
I have actually got this to work, but using two (very bloated??) sql queries as follows:
$choices = $db -> Q("SELECT
*, concat_ws(':', `kf_personID`, `kf_sectorID`) AS `concatids`
FROM
`class_choices`
WHERE
(`assigned` = '1')
GROUP BY
`concatids`
ORDER BY
`kf_personIDID` ASC,
`kf_sectorID` ASC;");
$choices2 = $db -> Q("SELECT
*, concat_ws(':', `kf_personID`, `kf_sectorID`) AS `concatids`
FROM
`class_choices`
WHERE
`preference` = '1'
GROUP BY
`concatids`
HAVING
`concatids` NOT IN (".iimplode($choices).")
ORDER BY
`kf_personID` ASC,
`kf_sectorID` ASC;");
if(is_array($choices2)){
$choices = array_merge($choices,$choices2);
}
Now $choices does have what I want.
But I'm sure there is a way to simplify this, merge the two SQL queries, and so it's a bit more lightweight.
Is there some kind of conditional SQL query that can do this???
Your solution uses two steps to enable you to filter the data as needed. Since you are generating a report, this is a pretty good approach even if it looks a bit more verbose than you might like.
The advantage of this approach is that it is much easier to debug and maintain, a big plus.
To improve the situation, you need to consider the data structure itself. When I look at the class_choices table, I see the following fields: kf_classID, preference, assigned which contain the key information.
For each class, the assigned field is either 0 (default) or 1 (when the class preference is assigned for the student). By default, the class with preference = 1 is the assigned one since you display it in the report when assigned=0 for all the student's class choices in a particular sector.
The data model could be improved by imposing a business rule as follows:
For preference=1 set the default value assigned=1. When the class selection process
takes place, and if the student gets assigned the 2nd or 3rd choice, then preference 1 is unassigned and the alternate choice assigned.
This means a bit more code in the application but it makes the reporting a bit easier.
The source of the difficulty is that the assignment process does not explicitly assign the 1st preference. It only updates assigned if the student cannot get the 1st choice.
In summary, your SQL is good and the improvements come from taking another look at the data model.
Hope this helps, and good luck with the work!

DynamicQuery: How to select a column with linq query that takes parameters

We want to set up a directory of all the organizations working with us. They are incredibly diverse (government, embassy, private companies, and organizations depending on them ). So, I've resolved to create 2 tables. Table 1 will treat all the organizations equally, i.e. it'll collect all the basic information (name, address, phone number, etc.). Table 2 will establish the hierarchy among all the organizations. For instance, Program for illiterate adults depends on the National Institute for Social Security which depends on the Labor Ministry.
In the Hierarchy table, each column represents a level. So, for the example above, (i)Labor Ministry - Level1(column1), (ii)National Institute for Social Security - Level2(column2), (iii)Program for illiterate adults - Level3(column3).
To attach an organization to an hierarchy, the user needs to go level by level(i.e. column by column). So, there will be at least 3 situations:
If an adequate hierarchy exists for an organization(for instance, level1: US Embassy), that organization can be added (For instance, level2: USAID).--> US Embassy/USAID, and so on.
How about if one or more levels are missing? - then they need to be added
How about if the hierarchy need to be modified? -- not every thing need to be modified.
I do not have any choice but working by level (i.e. column by column). I does not make sense to have all the levels in one form as the user need to navigate hierarchies to find the right one to attach an organization.
Let's say, I have those queries in my repository (just that you get the idea).
Query1
var orgHierarchy = (from orgH in db.Hierarchy
select orgH.Level1).FirstOrDefault;
Query2
var orgHierarchy = (from orgH in db.Hierarchy
select orgH.Level2).FirstOrDefault;
Query3, Query4, etc.
The above queries are the same except for the property queried (level1, level2, level3, etc.)
Question: Is there a general way of writing the above queries in one? So that the user can track an hierarchy level by level to attach an organization.
In other words, not knowing in advance which column to query, I still need to be able to do so depending on some conditions. For instance, an organization X depends on Y. Knowing that Y is somewhere on the 3rd level, I'll go to the 4th level, linking X to Y.
I need to select (not manually) a column with only one query that takes parameters.
=======================
EDIT
As I just said to #Mark Byers, all I want is just to be able to query a column not knowing in advance which one. Check this out:
How about this
Public Hierarchy GetHierarchy(string name)
{
var myHierarchy = from hierarc in db.Hierarchy
where (hierarc.Level1 == name)
select hierarc;
retuen myHierarchy;
}
Above, the query depends on name which is a variable. It mighbe Planning Ministry, Embassy, Local Phone, etc.
Can I write the same query, but this time instead of looking to much a value in the DB, I impose my query to select a particular column.
var myVar = from orgH in db.Hierarchy
where (orgH.Level1 == "Government")
select orgH.where(level == myVariable);
return myVar;
I don't pretend that select orgH.where(level == myVariable) is even close to be valid. But that is what I want: to be able to select a column depending on a variable (i.e. the value is not known in advance like with name).
Thanks for helping
How about using DynamicQueryable?
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
Your database is not normalized so you should start by changing the heirarchy table to, for example:
OrganizationId Parent
1 NULL
2 1
3 1
4 3
To query this you might need to use recursive queries. This is difficult (but not impossible) using LINQ, so you might instead prefer to create a parameterized stored procedure using a recursive CTE and put the query there.