Exclusice Arc and multiple many to many relationships db design issue - database

I have an existing application that has the following entities in the database
Customer
InvoiceGroup
SalesGroup
A customer can be part of multiple groups. Currently this is mapped in the following way
Customer table
- cid (Pk)
- fname
- surname
----
---
- invgrpdid (Fk)
- salesid (Fk)
Invoicegroup table
- invgrpid (Pk)
- name
- type
---
----
Salesgroup table
- salesid (Pk)
- name
- desc
-----
-----
I now have a requirement to add a new entity campaign. A Customer can be part of multiple campaigns. A campaign is deployed for a customer group which is created by
- selecting individual customers (custom) OR
- selecting an existing invoicegroup (invoice) OR
- selecting an existing salesgroup (sales)
The campaign customer list should be
- reusable, i.e can be used for multiple campaigns
- dynamic, i.e if created from an invoice/sales group, changes across the invoice and sales group
should be reflected to the campaign customer list
I am having difficulty designing for the dynamic requirement. I have come up with the following design, but it has exclusive arcs that is one key refering to multiple foreign keys which is not the recommended approach. I have thought of supertype and subtype, but I am not clear on designing the many-to-many relationships.
campaign
- campaignid(Pk)
- name
- startdate
- status
------
campaignlist
- listid(Pk)
- listname
- listtype - Invoice, Sales, Custom
- typeid (Fk) - Refers to invoicegroupid or salesgroupid depending on listtype
-----
customercampaign
- listid (Fk)
- customerid (Fk)
- status
- dateupdated
-------------
What would be a better approach to design this considering referential integrity and normalization.
The most frequent query run multiple times daily will be to retrieve all campaign information for a customer. So need to be mindful of multiple tables and joins.

At a first stab, I would do something like this:
CampaignList
- ID (PK)
- Name
CustomerCampaignList
- CampaignListID (FK to CampaignList.ID)
- CustomerID (FK)
UNIQUE (CampaignListID, CustomerID)
SalesGroupCampaignList
- CampaignListID (PK,FK to CampaignList.ID)
- SalesGroupID (FK)
UNIQUE (CampaignListID, SalesGroupID)
InvoiceGroupCampaignList
- CampaignListID (PK,FK to CampaignList.ID)
- InvoiceGroupID (FK)
UNIQUE (CampaignListID, InvoiceGroupID)
That is, use table inheritance. This is quite a normalised design, which in my opinion is the approach to take when you're not yet clued up on specific requirements. Optionally, you could make your life easier by denormalising a little and putting a CampaignListTypeID in the CampaignList table. In this case though, it would be difficult to maintain the integrity of the data model without writing logic in a trigger.
I'm assuming that you won't ever have to do interesting combination groups e.g., 2 x sales group + invoice group + a set of customers.

Related

how to set up database

I’m creating an app that tracks attendance of players in amateur football teams. And I’m just not sure how to set up the database. A bit of info: every user has a team and should only see the events within his team per season.
These are the main things:
Users
Events
User stats (for later)
Events
Training / match / social
Date
Time
Subject / opponent
Attendance
Main questions:
Do I make an event table per team per season? Or do I put all events in one big table for every user?
What is the best way to handle the attendance? They should be able to choose from
Present
absent
Fan
Thanks in advance!
I would do it this way:
User
userid PK
Name
Team
teamid PK
TeamName
UserInTeam
userid FK
teamid FK
Event
eventid PK
home_teamid FK
visiting_teamid FK
eventtypeid FK
Date
Time
Subject / opponent
Attendance
Event type
eventtypeid PK
EventType
Attendance Type
attendanceid PK
AttendanceType
UserAtEvent
userid FK
eventid FK
attendancetypeid FK
Content of Event Type
1 Training
2 Match
3 Social
Content of Attendance Type
1 Present
2 Absent
3 Fan
For Event, home_teamid and visiting_teamid can be NULL if the event is not a match
The application will show the events based on the team the user is a member of.

Database Schema for a daily delivery system?

I want to design a database where I have customers who daily buy varying quantities of any dairy product(milk, curd, cheese) but pay at the end of the month.
I am unable to figure out the correct database schema as I want to store each day's quantity of a particular product and a customer can buy multiple types of products.
So, what should be the way forward?
EDIT: My schema
Customer:
-Customer ID
-Name
-Mobile Number unique
-Address
-Daily Quantity(just for reference. during transaction it can increase or
decrease as per requirement)
Product:
-Product Id
-Name
-Price per unit
Transaction Table
-Customer id (foreign key)
-product id (foreign key)
-Quantity
-Date
Now, i want to generate a monthly bill for each customer by combining all the different items he bought over the month. So, will this transaction table be the best approach to do so?
I´ve add a new table Ticket so every single product bought during the day belongs to a unique Ticket.
This way you can get back to a specific ticket and see how much was spend ( this is because prices could change and there are multiple tickets during one day ).
<Ticket>
-Product id (foreign key)
-Quantity
-total
-Date
I´ve modified your Transaction table to have only ticket-customer relationship.
<Transaction>
-customer_id (foreign key)
-ticket id (foreign key)
-Date
Some SQL example
SELECT Tr.customer_id, MONTH(tr.Date), SUM(ti.total)
FROM Transaction tr, Ticket ti
WHERE tr.ticket_id = ti.id
GROUP BY MONTH(tr.Date)
WHERE Tr.customer_id = ? AND tr.Date BETWEEN ? AND ?

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

Design Database that Keeps Favorite Posts for Every User - Sugesstion Needed

Offer: offerID, title, thread ...
Wanted: wantedID, title, thread ...
Member: memberID, username ...
A member can pick many topics as favorite which will be put in a UI list. How can i design the db for it?
My Solution:
Favorite:
favoriteID (PK)
memberID - FK
offerID - FK
wantedID - FK
Many to many relationship. However, there always be NULL either in offerID or wantedID, is this ok?
You say you're allowing multiple favorites per user, right?
Why don't you just use two tables then?
1) favoriteOffers
favoriteOfferID (PK)
memberID - FK
offerID - FK
2) favoriteWanted
favoriteWantedID (PK)
memberID - FK
wantedID - FK
You get the same functionality (maybe even more) with clear clean structure.

Resources