Creating a global ID for every row in multiple tables for notifications - sql-server

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

Related

What's the cleanest way in SQL to break a table into two tables?

I have a table that I want to break into 2 tables. I want to pull some data out of table A, put it into a new table B, and then point each record in A to the corresponding record in the new table.
It's easy enough to populate the new table with an INSERT INTO B blah blah SELECT blah blah FROM A. But the catch is, when I create the new records in B, I want to write the ID of the B record back into A.
I've thought of two ways to do this:
Create a cursor, loop through A a record at a time, create the record in B and post the new ID back to A.
Create a temporary table with the extracted data, an ID for the new record, and the ID of A. Then use this temporary table to populate B and also to post the ID back to A.
Both methods seem cumbersome with a lot of copying all the data back and forth. Is there a clean, simple way to do this or should I just knuckle down and do it the hard way?
Oh, I'm using Microsoft SQL Server, if your answer depends on non-standard features of SQL.
Someone asks for an example. Yes, I should have included something concrete to make it clear. The real example is a bunch of data, but let me give a simplified example of what I mean.
Let's say I have a Customer table with customer_id, name, and city. I want to break city out into a separate table.
So for example:
Customer
ID Name City
17 Al Detroit
22 Betty Baltimore
39 Charles Cleveland
I want to convert this to:
Customer
ID Name City_ID
17 Al 1
22 Betty 2
39 Charles 3
City
ID Name
1 Detroit
2 Baltimore
3 Cleveland
The exact ID values don't matter.
So easy enough to create the City table and the reference ...
create table city (id int identity primary key, name varchar(50))
alter table customer add city_id int references city
And then populate the city table ...
insert into city (name)
select city from customer
The trick is how to get those city IDs back into the Customer table.
(And yes, in this simplified example, the effort may appear pointless. In real life we have many tables with addresses and I want to pull all those fields out of all the other tables and put them into a single address table, so we can standardize the declarations and processing of addresses.)
(Note: I haven't tested the sample code above. Excuse me if there's a typo or something in there.)
You can use the output clause to capture your new ID values.
without any sample data or examples of what you are doing the following is just a guide.
Create a #table to hold the new ID values, then insert the newly inserted Id identity values along with a correlating value from the inserted virtual table. You can then update the original table with the new IDs by joining on this correlating value.
create table #NewIds (TableBId int, TableAId int)
insert into TableB (column list)
output inserted.Id, inserted.TableAId into #NewIds
select column list
from TableA
update a
set a.TableBId=Id
from #NewIds n join TableA a on a.Id=n.TableAId

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 ?

Database schema for end user report designer

I'm trying to implement a feature whereby, apart from all the reports that I have in my system, I will allow the end user to create simple reports. (not overly complex reports that involves slicing and dicing across multiple tables with lots of logic)
The user will be able to:
1) Select a base table from a list of allowable tables (e.g., Customers)
2) Select multiple sub tables (e.g., Address table, with AddressId as the field to link Customers to Address)
3) Select the fields from the tables
4) Have basic sorting
Here's the database schema I have current, and I'm quite certain it's far from perfect, so I'm wondering what else I can improve on
AllowableTables table
This table will contain the list of tables that the user can create their custom reports against.
Id Table
----------------------------------
1 Customers
2 Address
3 Orders
4 Products
ReportTemplates table
Id Name MainTable
------------------------------------------------------------------
1 Customer Report #2 Customers
2 Customer Report #3 Customers
ReportTemplateSettings table
Id TemplateId TableName FieldName ColumnHeader ColumnWidth Sequence
-------------------------------------------------------------------------------
1 1 Customer Id Customer S/N 100 1
2 1 Customer Name Full Name 100 2
3 1 Address Address1 Address 1 100 3
I know this isn't complete, but this is what I've come up with so far. Does anyone have any links to a reference design, or have any inputs as to how I can improve this?
This needs a lot of work even though it’s relatively simple task. Here are several other columns you might want to include as well as some other details to take care of.
Store table name along with schema name or store schema name in additional column, add column for sorting and sort order
Create additional table to store child tables that will be used in the report (report id, schema name, table name, column in child table used to join tables, column in parent table used to join tables, join operator (may not be needed if it always =)
Create additional table that will store column names (report id, schema name, table name, column name, display as)
There are probably several more things that will come up after you complete this but hopefully this will get you in the right direction.

How to retrieve multiple aggregate columns from SQL Server in a SQL View/Access Query (pref SQL View)

I have a table tblRecords, into this table are inserted 3500 records each day. tblRecords has a foreign key linking it to tblUpload. Each Row is identified, apart from a generic ID column, by a REF (ie: XYDG74G) and the Foreign Key Column (ie: 345) that links it to tblUpload.
The circa 3500 rows that are uploaded each day are the same rows as were uploaded from the previous day (with updates) as well as additional, new entries from the last 24 hrs.
Each tblRecord row can be flagged (BIT column) as MissCustName and MissCustNameFixed. I view the tblRecord rows by tblUpload, that is to say I “Show me all (3500+) records uploaded today”. I want, along with the actual columns in tblRecord to see two columns, CountOfPrevMissCustName and CountOfPrevMissCustNameFixed
ID - Ref - CustName - CountofPrevMissCustName- CountofPrevMissCustNameOK
1 - AASD001 - <NULL> - 14 - 12
2 - ZRFG789 - Joe Bloggs - 10 - 8
3 - YERF777 - Mary Blyge - 0 - 0
Missing Customer Name counts the total records (historical) for that Transaction where Missing Customer Name is flagged as TRUE. `MissNameFixed does the same.
Record 1 has previously had 14 missing customer name flags and 12 MissNameCustNameOK flags, but is still missing the customer name.
Record 2 has previously had 10 missing customer name flags, and 8 MissNameCustNameOK flags and currently has a customer name.
Record 3 has never had any missing customer name flags and hence no MissNameCustNameOK flags and currently has a name.
What would be the most efficient (quickest and less stress on SQL Server) to implement these 'dcount' style columns. Trying a dcount column within Access is painfully slow 9See below), as is using DAO recordsets in Access.
SELECT *, dcount("RecordID","tblRecordsHistorical","Ref=" & Ref & " AND CustName is null) as CountofPrevMissCustName, dcount("RecordID","tblRecordsHistorical","Ref=" & Ref & " AND CustName is not null) as CountofPrevMissCustNameOK from tblRecords
I would ideally like to be able to create some kind of view, that I could load, then use to do a bulk update on the table.
I will only ever work on the latest batch of tblRecords so once the CountOf... physical columns have been updated once, they don't need to be again.
Thnx

Some basic questions on database design and how to insert accordingly with LINQ to Entities

Ok, I am total newbie so bear with me.
Trying to implement an ordering system and wish
to save the orders to the database with LINQ to Entities. I can do it now
but for each new object that is saved to the orders table
a new row is inserted, with new OrderNo for each ProductID where as I obviously
should be able to have multiple ProductID's for each OrderNo.
Everything is very simplified as I am just testing.
I have an orders table with columns as such:
OrderNo PK, Identity specification
Line int PL
ProductID int
and a products table
ProductID int PK
An order entity object is instantiated and its properties
are populated with data from a form which is posted to an action method.
It is then saved to the orders table with the following code:
(DropDownList1Value) has value of an existing ProductID and "DropDownList1Value" is the id of the DropDownList element in view.
[HttpPost]
public ActionResult OrderProcessor()
{
int productId = int.Parse(Request.Form["DropDownList1Value"]);
var order = new Order();
order.ProductID = productId;
context.Orders.AddObject(order);
context.SaveChanges();
return View(order);
}
So the records that are inserted look as such:
Sorry, couldn't line up the values under their respective column name in this editor.
OrderNo Line ProductID
101 0 3
102 0 5
103 0 2
Where as I want something like this:
OrderNo Line ProductID
101 1 3
101 2 5
101 3 2
102 1 2
So I wish to know how can I modify the orders table so it
can have multiple records with same "OrderNo" and just increment for "Line" for diff ProductID's and how do I go about inserting such records with LinQ to Entities where
I will obviously have many ProductId from multiple DropDowLists
and they will all be for the one order.
Currently I have foreign key dependency on ProductID in Products table,
so no record in the Orders table can have ProductID which does not exists in the Products table.
I need to make the table depend on the whole key that is OrderNo + Line
and have the "Line" auto increment.
Or is there a much better way of implementing of what I am after here?
Thanks.
Let me first explain briefly what I understood.
There is an invoice, which contains several products for one order number.
and this is how your invoice looks like:
Order Number: 101
------------------
Sl. Products
1 3
2 5
3 2
Before answering I want to point out that you are taking OrderId from a form (That is from client side) This is a wrong and INSECURE approach. Let the order id be AutoGenerated by database.
I would suggest to tweak your database design a little.
Here is a solution that will work.
Note: I am consedering your database support Auto-Increment, for MS SQL replace it with IDENTITY, for Oracle you need to create a sequence.
Product (
id INT PK AUTO-INCREMENT
);
Order (
id INT PK AUTO-INCREMENT
user-id INT FK # user who purchased
### and other common details Like date of purchase etc.
);
Order-Detail (
id INT PK AUTO-INCREMENT
order-id INT FK # Common order id
pdt-id INT FK # product which was purchased.
);
When you make a purchase:
1. Insert a row in order table
2. Fetch the last inserted id
3. Insert order-id from last step and products which are purchased in Order-Detail table,
Fetch all the orders made by a user:
1. Read from order table.
Fetch all products purchased for an order:
1. Fetch details from Order-Detail
Note: You will get List of products purchased, Use Order-detail.id as "Line"
EDIT:
Thanx to HLGEM's comment
If you think price of a product may change then instead of updating the price add a new row to the table (and flag the old table so that it wont be visible, you can also have a column in new table pointing to old table), thus old purchase will point to old product and new orders will point to updated (new) row.
There is one more approach this problem:
store the current cost of product in order-detail table.
If you are facing difficulty understanding above solution here is another and simpler one.
In Order table, Make a composite primary key including OrderNo and Line.
Whenever inserting into database you will need to generate line number in your code, which you can do by runnign a loop over array of propduct being purchased.
I think it would be better to split your current Order table into two separate tables:
Order table
(PK, Identity specification) OrderId
Perhaps other fields like Invoice address, Delivery address, etc.
OrderLine table
(PK, Identity specification) OrderLineId
(FK to Order table) OrderId
(FK to Product table) ProductId
For both tables you have an Entity in your class model: class Order and class OrderLine and a one-to-many relationship between them, so Order has a collection of OrderLines.
Creating an order with all order lines would then look like this:
var order = new Order();
foreach (var item in collection)
{
var orderLine = new OrderLine()
// Get productId from your DropDownLists
orderLine.ProductId = productId;
order.OrderLines.Add(orderLine);
}
context.Orders.AddObject(order);
context.SaveChanges();
Edit
The MVC MusicStore Tutorial might also help for the first steps to create an order processing system with ASP.NET MVC and Entity Framework. It contains classes for orders and order details (among others) and explains their relationships.

Resources