SQL Database Relationship - database

I have a DB design question,
I'm trying to design a DB-relationship for companies that have users and need to pay a monthly fee according to a formula. A formula has a name and fee but are not linked to an user but to a company.
Example:
Name: Formula 1
Fee: 5,00
Name: Formula 2
Fee: 10,00
Company XYZ has 2 users with Formula 1 and 2 users with Formula 2. They would need to pay 30,00 .
My initial solution was to link Formula's to users:
But the problem I have discovered is that Formula's need to be linked to Companies and not to users. This because of the fact that different companies can have different fee's for the same formula.
Example:
Users from company A with Formula 1 need to pay 5 but users from company B with Formula 1 need to pay 10.
This is where I met an obstacle because I didn't seem to have fully trust in my database design, in which I attempted to link Formula_Type with a company (and seperate fee...).
My attempt was to use groups:
But I faced a problem here:
How or/and where would I split the fee of the Formula? Since they are depending on the company. Also, what foreign key would I use in the USER-table to link it to an Formula, or wouldn't this be possible in my case?
Is what I'm achieving even possible?

Try this on for size:
Company
ID -- PK
Name
etc.
User
ID -- PK
Company_ID -- FK to Company
Last_Name
First_Name
etc.
Formula
ID -- PK
Name
etc.
Formula_Company -- this allows company-specific fees for different formulae
ID -- PK
Formula_ID -- FK to Formula
Company_ID -- FK to Company
Fee
User_Formula_Company -- this identifies which fee a given user is charged
User_ID -- FK to User
Formula_Company_ID -- FK to Formula_Company

Related

How do I add multiple values in one column (I am using phpmyadmin)?

The case study is that a company name group is contracted with many energy companies worldwide. Now there are only 2 supervisors in the company group who have access to oversee the contracted companies.
Which means that 1 supervisor oversees many companies.
This is the contracted companies table:
Company table
the companies have the access to see which supervisor can see their company
This is the Supervisor Table
Supervisors Table
I dont know how to add multiple values in the companyID column in the supervisors table. This is my first time getting a big university project to be done in phpmyadmin.

Bank transactions table - can this be done better?

I am wondering what is the best way to make bank transaction table.
I know that user can have many accounts so I add AccountID instead of UserID, but how do I name the other, foreign account. And how do I know if it is incoming or outgoing transaction. I have an example here but I think it can be done better so I ask for your advice.
In my example I store all transactions in one table and add bool isOutgoing. So if it is set to true than I know that user sent money to ForeignAccount if it's false then I know that ForeignAccount sent money to user.
My example
Please note that this is not for real bank, of course. I am just trying things out and figuring best practices.
My opinion:
make the ID not null, Identity(1,1) and primary key
UserAccountID is fine. Dont forget to create the FK to the Accounts table;
You could make the foreignAccount a integer as well if every transaction is between 2 accounts and both accounts are internal to the organization
Do not create Nvarchar fields unless necessary (the occupy twice as much space) and don't create it 1024. If you need more than 900 chars, use varchar(max), because if the column is less than 900 you can still create an index on it
create the datetime columns as default getdate(), unless you can create transactions on a different date that the actual date;
Amount should be numeric, not integer
usually, i think, you would see a column to reflect DEBIT, or CREDIT to the account, not outgoing.
there are probably several tables something like these:
ACCOUNT
-------
account_id
account_no
account_type
OWNER
-------
owner_id
name
other_info
ACCOUNT_OWNER
--------------
account_id
owner_id
TRANSACTION
------------
transaction_id
account_id
transaction_type
amount
transaction_date
here you would get 2 records for transactions - one showing a debit, and one for a credit
if you really wanted, you could link these two transactions in another table
TRANSACTION_LINK
----------------
transaction_id1
transaction_id2
I'd agree with the comment about the isOutgoing flag - its far too easy for an insert/update to incorrectly set this (although the name of the column is clear, as a column it could be overlooked and therefore set to a default value).
Another approach for a transaction table could be along the lines of:
TransactionID (unique key)
OwnerID
FromAccount
ToAccount
TransactionDate
Amount
Alternatively you can have a "LocalAccount" and a "ForeignAccount" and the sign of the Amount field represents the direction.
If you are doing transactions involving multiple currencies then the following columns would be required/considered
Currency
AmountInBaseCcy
FxRate
If involving multiple currencies then you either want to have an fx rate per ccy combination/to a common ccy on a date or store it per transaction - that depends on how it would be calculated
I think what you are looking for is how to handle a many-tomany relationship (accounts can have multiple owners, owners can have mulitple accounts)
You do this through a joining table. So you have account with all the details needed for an account, you have user for all teh details needed for a user and then you have account USer which contains just the ids from both the other two tables.

do these tables fit the database First Normal Form?

table users
userId name company company_address url
1 Joe ABC Work Lane abc.com
2 Jake ABC Work Lane xyz.com
3 tom XYZ Job Street abcde.com
4 jim XYZ Job Street fexyz.com
the second table
id name favourite_food_1 favourite_food_2
1 Sam Curry Steak
2 Lucy Chicken Burgers
if the table don't fit for the 1NF,why? thank you.
The first table fits 1NF. The second does not - there's a repeating group with the two favorite food fields. Not everyone necessarily has two favorite foods (or any favorite foods at all, or has 3+ favorite foods), so those fields are nullable, and therefore causes the table to fail 1NF.
Doesn't 1NF only mean each value has to be atomic? In other words, every relational database table is in 1NF, since sets of values aren't allowed.
1NF sets the very basic rules for an organized database:
1: Eliminate duplicative columns from the same table.
2: Create separate tables for each group of related data and identify each row with a unique column (the primary key).
The problem with your Database tables is "Name"(duplicate column).
Every relational table always satisfies 1NF. A SQL table is in 1NF if it accurately represents a relation, i.e. it has unique column names and doesn't permit nulls or duplicate rows.

Basic table structure question

I have Brand and Company. 1 Company can have 1 or more Brands.
As an example, company has company_id, company_name. Similarly Brands has brand_id and brand_name. Now can i add the FK column company_id to brands also and the relationship is complete in 2 tables or do i need a 3rd table like Company_Brands which will have company_id, brand_id and the default PK?
I am not asking for an ideal text book way this should be done but in a high transaction environment where performance is important so less query stain and also where writes will be high along with data will change in tables as this is a user content site so information may not be accurate and thus edited constantly.
Just add the foreign key company_id to the brands table. You have described a 1 to many relationship i.e. 1 company can have many brands, but 1 brand cannot have many companies.
You would only need the junction table if you had a many to many relationship.

Normalizing a Table with Low Integrity

I've been handed a table with about 18000 rows. Each record describes the location of one customer. The issue is, that when the person created the table, they did not add a field for "Company Name", only "Location Name," and one company can have many locations.
For example, here are some records that describe the same customer:
Location Table
ID Location_Name
1 TownShop#1
2 Town Shop - Loc 2
3 The Town Shop
4 TTS - Someplace
5 Town Shop,the 3
6 Toen Shop4
My goal is to make it look like:
Location Table
ID Company_ID Location_Name
1 1 Town Shop#1
2 1 Town Shop - Loc 2
3 1 The Town Shop
4 1 TTS - Someplace
5 1 Town Shop,the 3
6 1 Toen Shop4
Company Table
Company_ID Company_Name
1 The Town Shop
There is no "Company" table, I will have to generate the Company Name list from the most descriptive or best Location Name that represents the multiple locations.
Currently I am thinking I need to generate a list of Location Names that are similar, and then and go through that list by hand.
Any suggestions on how I can approach this is appreciated.
#Neall, Thank you for your statement, but unfortunately, each location name is distinct, there are no duplicate location names, only similar. So in the results from your statement "repcount" is 1 in each row.
#yukondude, Your step 4 is the heart of my question.
Please update the question, do you have a list of CompanyNames available to you? I ask because you maybe able to use Levenshtein algo to find a relationship between your list of CompanyNames and LocationNames.
Update
There is not a list of Company Names, I will have to generate the company name from the most descriptive or best Location Name that represents the multiple locations.
Okay... try this:
Build a list of candidate CompanyNames by finding LocationNames made up of mostly or all alphabetic characters. You can use regular expressions for this. Store this list in a separate table.
Sort that list alphabetically and (manually) determine which entries should be CompanyNames.
Compare each CompanyName to each LocationName and come up with a match score (use Levenshtein or some other string matching algo). Store the result in a separate table.
Set a threshold score such that any MatchScore < Threshold will not be considered a match for a given CompanyName.
Manually vet through the LocationNames by CompanyName | LocationName | MatchScore, and figure out which ones actually match. Ordering by MatchScore should make the process less painful.
The whole purpose of the above actions is to automate parts and limit the scope of your problem. It's far from perfect, but will hopefully save you the trouble of going through 18K records by hand.
I've had to do this before. The only real way to do it is to manually match up the various locations. Use your database's console interface and grouping select statements. First, add your "Company Name" field. Then:
SELECT count(*) AS repcount, "Location Name" FROM mytable
WHERE "Company Name" IS NULL
GROUP BY "Location Name"
ORDER BY repcount DESC
LIMIT 5;
Figure out what company the location at the top of the list belongs to and then update your company name field with an UPDATE ... WHERE "Location Name" = "The Location" statement.
P.S. - You should really break your company names and location names out into separate tables and refer to them by their primary keys.
Update: - Wow - no duplicates? How many records do you have?
I was going to recommend some complicated token matching algorithm but it's really tricky to get right and if you're data does not have a lot of correlation (typos, etc) then it's not going to give very good results.
I would recommend you submit a job to the Amazon Mechanical Turk and let a human sort it out.
Ideally, you'd probably want a separate table named Company and then a company_id column in this "Location" table that is a foreign key to the Company table's primary key, likely called id. That would avoid a fair bit of text duplication in this table (over 18,000 rows, an integer foreign key would save quite a bit of space over a varchar column).
But you're still faced with a method for loading that Company table and then properly associating it with the rows in Location. There's no general solution, but you could do something along these lines:
Create the Company table, with an id column that auto-increments (depends on your RDBMS).
Find all of the unique company names and insert them into Company.
Add a column, company_id, to Location that accepts NULLs (for now) and that is a foreign key of the Company.id column.
For each row in Location, determine the corresponding company, and UPDATE that row's company_id column with that company's id. This is likely the most challenging step. If your data is like what you show in the example, you'll likely have to take many runs at this with various string matching approaches.
Once all rows in Location have a company_id value, then you can ALTER the Company table to add a NOT NULL constraint to the company_id column (assuming that every location must have a company, which seems reasonable).
If you can make a copy of your Location table, you can gradually build up a series of SQL statements to populate the company_id foreign key. If you make a mistake, you can just start over and rerun the script up to the point of failure.
Yes, that step 4 from my previous post is a doozy.
No matter what, you're probably going to have to do some of this by hand, but you may be able to automate the bulk of it. For the example locations you gave, a query like the following would set the appropriate company_id value:
UPDATE Location
SET Company_ID = 1
WHERE (LOWER(Location_Name) LIKE '%to_n shop%'
OR LOWER(Location_Name) LIKE '%tts%')
AND Company_ID IS NULL;
I believe that would match your examples (I added the IS NULL part to not overwrite previously set Company_ID values), but of course in 18,000 rows you're going to have to be pretty inventive to handle the various combinations.
Something else that might help would be to use the names in Company to generate queries like the one above. You could do something like the following (in MySQL):
SELECT CONCAT('UPDATE Location SET Company_ID = ',
Company_ID, ' WHERE LOWER(Location_Name) LIKE ',
LOWER(REPLACE(Company_Name), ' ', '%'), ' AND Company_ID IS NULL;')
FROM Company;
Then just run the statements that it produces. That could do a lot of the grunge work for you.

Resources