Database design for unilateral relationship between contractors and companies - database

Companies and contractors can have contacts, those contacts can be both companies and contractors, and that relationship is unilateral (no need for the other party to accept).
I was thinking of doing something like this:
Contacts:
ContractorId
CompanyId
ContactContractorId
ContactCompanyId
Type
1
NULL
15
NULL
3
NULL
22
12
NULL
1
NULL
44
NULL
22
2
ContactTypes:
Id
Type
CompanyToContractor
1
CompanyToCompany
2
ContractorToContractor
3
ContractorToCompany
4
Is this a good approach, or should I create four different tables, one for each type of relationship?

If your model doesn't require companies and contractors to be handled differently, use a generic table that covers both. Otherwise you could do first 3 columns of the above like this:
contact: contact_id
company: company_id, ...
company_contact: company_id, contact_id
contractor: contractor_id, ...
contractor_contact: contractor_id, contact_id
and for the last 2 columns:
contact_company: contact_id, company_id
contact_contractor: contract_id, contractor_id
which would enable referential integrity.
If you don't care about the foreign keys, use unique key across both company and contractor tables. For example a sequence or an uuid. This would would allow you your contractor to have reference to either table table and eliminate the company_contact and contractor_contact. Similar, you can condense contact_company and contact_contractor into 1 table.

Related

What normal form is pivoted/widened data in?

There are many questions that have something to the effect of "What normal form are these data in?" and I admittedly have not combed through every one of them to see if "These data" they are referring to are pivoted. I'm asking this because I think this would be useful for those searching for this for those familiar with this terminology.
Lets say that I have a table with the columns:
personid*, email1, email2, email3
1 , e#e.us, NULL , NULL
2 , a#a.com,b#b.co, c#c.com
3 , j#j.com,l#l.uk
Where personid is the primary key and uniquely identifies the table. Each e-mail is functionally dependent on personid but obviously this isn't in 3NF because that would involve having a person table and an e-mail table such as:
personid, email ,email_num
1 ,e#e.us ,1
2 ,a#a.com,1
2 ,b#b.co ,2
2 ,c#c.com,3
3 ,j#j.com,1
3 ,l#l.uk ,2
Where email_num takes the place of the n from the previous table.
What normal form is the first (pivoted) table in?

Creating a global ID for every row in multiple tables for notifications

I'm designing what is essentially an accounting/retailing application for MS SQL Server 2016. I have ~75 core data object types, each in their own respective table (users, organizations, invoices, payments, etc.).
What I need to create is a notification system,
e.g. "Steve paid invoice 1234", "Bob purchased product XYZ", etc. What I was going to create was a "notification types" table:
NotificationTypes:
id message
11 "{0} paid invoice {1]"
12 "{0} purchased product {1]"
...
and then have two corresponding tables that store each notification event's info, and then the values for that notification message. So for example for "Steve paid invoice 1234":
NotificationEvent:
id notificationType (FK) occured
21 11 2016-01-01 00:00:00
NotificationEventValues:
id notificationEvent (FK) XXXXX
31 21 (FK reference to Steve in users table)
32 21 (FK reference to invoice 1234 in invoices table)
Now since I can't create generic foreign keys for NotificationEventValues.XXXXX, I was going to have a single 'dataObjects' table that has FK columns for all 75 data types I have, with only one of the 'data type' columns having a value per row.
This way, every instance of a data object in my database has a unique ID I can reference in the notification field - which will mean a huge table given it has a unique ID for basically every row in the other 75 tables. The other downside is it means for every user, invoice, any 'data object', I'm wasting significant amounts of space since space will be reserved for ID references for the other 74 null-valued columns (since they're fixed size IDs and not variable).
ASK:
Is there a better way to achieve my 'global' identifier across all the tables? Or a better way to handle the notification system to avoid this problem?
Create triggers in all table that will insert a record in a table say KeyMaster with one column key which will also be the primry key whenever a record is inserted in any table. The value inserted in this table will be in the format _. For example if a record is inserted in user table with id 1 then the record inserted in KeyMaster table will be 'user_1'. Similarly if a record is inserted in invoice table with id 1 then the recor inserted in KeyMaster table will be 'invoice_1'.
In your NotificationEventValues table you will need only three columns id,notificationEvent and key (FK reference to KeyMaster table).
For getting corresponding record you need to write query similar to below:
select *
from NotificationEventValues n
inner join users u on 'user_' + u.id = n.key

How can I associate a single record with one or more PKs

If I had a single record that represented, say, a sellable item:
ItemID | Name
-------------
101 | Chips
102 | Candy bar
103 | Beer
I need to create a relationship between these items and one or more different types of PKs. For instance, a company might have an inventory that included chips; a store might have an inventory that includes chips and a candy bar, and the night shift might carry chips, candy bars, and beer. The is that we have different kinds of IDs: CompanyID, StoreID, ShiftID respectively.
My first though was "Oh just create link tables that link Company to inventory items, Stores to inventory items, and shifts to inventory items" and that way if I needed to look up the inventory collection for any of those entities, I could query them explicitly. However, the UI shows that I should be able to compile a list arbitrarily (e.g. show me all inventory items for company a, all west valley stores and Team BrewHa who is at an east valley store) and then display them grouped by their respective entity:
Company A
---------
- Chips
West Valley 1
-------------
- Chips
- Candy Bar
West Valley 2
-------------
- Chips
BrewHa (East Valley 6)
--------------------
- Chips
- Candy Bar
- Beer
So again, my first though was to base the query on the provided information (what kinds of IDs did they give me) and then just union them together with some extra info for grouping (candidate keys like IDType+ID) so that the result looked kind of like this:
IDType | ID | InventoryItemID
------------------------------
1 |100 | 1
2 |200 | 1
2 |200 | 2
2 |201 | 1
3 |300 | 1
3 |300 | 2
3 |300 | 3
I guess this would work, but it seems incredibly inefficient and contrived to me; I'm not even sure how the parameters of that sproc would work... So my question to everyone is: is this even the right approach? Can anyone explain alternative or better approaches to solve the problem of creating and managing these relationships?
It's hard to ascertain what you want as I don't know the purpose/use of this data. I'm not well-versed in normalization, but perhaps a star schema might work for you. Please keep in mind, I'm using my best guess for the terminology. What I was thinking would look like this:
tbl_Current_Inventory(Fact Table) records current Inventory
InventoryID INT NOT NULL FOREIGN KEY REFERENCES tbl_Inventory(ID),
CompanyID INT NULL FOREIGN KEY REFERENCES tbl_Company(ID),
StoreID INT NULL FOREIGN KEY REFERENCES tbl_Store(ID),
ShiftID INT NULL FOREIGN KEY REFERENCES tbl_Shift(ID),
Shipped_Date DATE --not really sure, just an example,
CONSTRAINT clustered_unique CLUSTERED(InventoryID,CompanyID,StoreID,ShiftID)
tbl_Inventory(Fact Table 2)
ID NOT NULL INT,
ProductID INT NOT NULL FOREIGN KEY REFERENCES tbl_Product(ID),
PRIMARY KEY(ID,ProductID)
tbl_Store(Fact Table 3)
ID INT PRIMARY KEY,
CompanyID INT FOREIGN KEY REFERENCES tbl_Company(ID),
RegionID INT FOREIGN KEY REFERENCES tbl_Region(ID)
tbl_Product(Dimension Table)
ID INT PRIMARY KEY,
Product_Name VARCHAR(25)
tbl_Company(Dimension Table)
ID INT PRIMARY KEY,
Company_Name VARCHAR(25)
tbl_Region(Dimension Table)
ID PRIMARY KEY,
Region_Name VARCHAR(25)
tbl_Shift(Dimension Table)
ID INT PRIMARY KEY,
Shift_Name VARCHAR(25)
Start_Time TIME,
End_Time TIME
So a little explanation. Each dimension table holds only distinct values like tbl_Region. Lists each region's name once and an ID.
Now for tbl_Current_Inventory, that will hold all the columns. I have companyID and StoreID both in their for a reason. Because this table can hold company inventory information(NULL StoreID and NULL shiftID) AND it can hold Store Inventory information.
Then as for querying this, I would create a view that joins each table, then simply query the view. Then of course there's indexes, but I don't think you asked for that. Also notice I only had like one column per dimension table. My guess is that you'll probably have more columns then just the name of something.
Overall, this helps eliminate a lot of duplicate data. And strikes a good balance at performance and not overly complicated data structure. Really though, if you slap a view on it, and query the view, it should perform quite well especially if you add some good indexes.
This may not be a perfect solution or even the one you need, but hopefully it at least gives you some ideas or some direction.
If you need any more explanation or anything else, just let me know.
In a normalized database, you implement a many-to-many relationship by creating a table that defines the relationships between entities just as you thought initially. It might seem contrived, but it gives you the functionality you need. In your case I would create a table for the relationship called something like "Carries" with the primary key of (ProductId, StoreId, ShiftId). Sometimes you can break normalization rules for performance, but it comes with side effects.
I recommend picking up a good book on designing relational databases. Here's a starter on a few topics:
http://en.wikipedia.org/wiki/Entity%E2%80%93relationship_model
http://en.wikipedia.org/wiki/Database_normalization
You need to break it down to inventory belongs to a store and a shift
Inventory does does not belong to a company - a store belongs to a company
If the company holds inventory directly then I would create a store name warehouse
A store belongs to a region
Don't design for the UI - put the data in 3NF
Tables:
Company ID, name
Store ID, name
Region ID, name
Product ID, name
Shift ID, name
CompanyToStore CompanyID, StoreID (composite PK)
RegionToStore RegionID, StoreID (composite PK)
Inventory StoreID, ShiftID, ProductID (composite PK)
The composite PK are not just efficient they prevent duplicates
The join tables should have their own ID as PK
Let the relationships they are managing be the PK
If you want to report by company across all shifts you would have a query like this
select distinct store.Name, Product.Name
from Inventory
join Store
on Inventory.StoreID = Store.ID
join CompanyToStore
on Store.ID = CompanyToStore.StoreID
and CompanyToStore.CompanyID = X
store count in a region
select RegionName, count(*)
from RegionToStore
join Region
on Region.ID = RegionToStore.RegionID
group by RegionName

MS Access Relationship help needed

I have 2 MS Access Tables.
Table 1
id
room-name
Table 2
wall
cupboard
ceiling
Now... table1.room-name has the room names and table2 contains object (many) so each room name contains many objects.
My question is ... How do I set the relationships for this please?
Nothing in table 2 tells you what room things are in so you need to add a foreign key of the room to the primary key of table 1. In this case either column of table1 could be its primary key - I would use room- name and drop the id.
So table2 needs altering so that room-name is in it and the draw the connection from table1 to table2.
Something like:
[Room]
RoomId eg 1 2
RoomName eg bedroom kitchen
[RoomItem]
RoomItemId eg 1 eg 2 eg 3
RoomId eg 1 eg 1 eg 2
ItemName eg wardrobe eg bed eg cooker
Where the RoomId links the Room and RoomItem tables.

Merge two tables with common values by adding the count, append rest

I have two tables, created at runtime, which pulls some data from some entirely different tables and joins and complex where clauses, and finally, I have these two tables with columns something like this:
TableCars:
Id Company Views
```````````````````````
01 Honda 12
32 audi 6
18 BMW 3
17 Vector 5
TableBikes:
Id Company Views
```````````````````````
01 Honda 3
32 audi 1
19 Kawasaki 2
Note:
company names and id's will always be the same in these two tables, like, honda = 01
I want to merge these two tables in the sense that I dont want any repetitions in the names (or id's) and I want the views to be added. Is there any way to do this without using a while loop and a whole lotta hair loss?
the resultant table should be something like this:
ResultantTable
Id Company Views
``````````````````````
01 Honda 15
32 audi 7
18 BMW 3
17 Vector 5
19 Kawasaki 2
Many thanks in advance.
ps: I tried to check google, came across "merge" clause, looked up on that, MSDN was way over my head. I never understand ANYTHING in MSDN. wonder if there are any other people like me.
You could try this:
SELECT company, SUM(views) FROM
(SELECT company, views FROM first
UNION ALL
SELECT company, views FROM second) as t
GROUP BY company
Let me know if you have any issues
select id, company, sum(views) views
from
(
select id, company, views
from tablecars
union all
select id, company, views
from tablebikes
) joined
group by id, company
You can always turn SELECT statements into a CREATE table statement:
select id, company, sum(views) views
into NewTableName
from
(
select id, company, views
from tablecars
union all
select id, company, views
from tablebikes
) joined
group by id, company
I don't normally recommended this method to create permanent tables. It is better to create the table and manually define keys, constraints, defaults, indexes. If you create it this way, you can still add required keys later using ALTER table statements.
If you will always have the two base tables around, and you need this "joined" data for querying/reporting, and it has to keep in sync with the base tables - then what you are really after is a VIEW using the first SELECT statement.

Resources