I've developed an application for a client with the following relationship:
A SalesOrder has a foreign key relationship to a Customer (one-to-one).
The client now wants to be able to delete Customers so that they won't be available for any future (new) SalesOrders, but obviously I would like to retain the historical records for reporting, etc. Likewise, the record may need to be update-able in the future in rare cases, so in "Edit" mode the customer would need to be there (but ALL other deleted Customers would not)
I am looking for input on a pattern to model this, or better recommendations. My thought was to have an "Archived" bit on the Customer, but my problem is that if someone loads up an old SalesOrder I would also need to load that archived record for the DropDownList that Customers populate.
Since I'm using Entity Framework and an EntityDataSource, my guess is that I might get a runtime exception on the SelectedValue bind of the DropDownList also, but I haven't verified this.
Any thoughts or recommendations on where to start?
Thank you.
I believe I have a solution. I've been racking my brain on it all day but I think it's fairly easy. In a quick prototype it appears to do what I need it to:
I added an "Archived" bit to the Customers table (actually to the BusinessEntity table they inherit from). Then for the "Edit Sales Order" page, when querying Customers, I have a where clause which includes the customer by ID.
For example (pseudocode):
SELECT CustomerID, Name FROM Customers WHERE Archived = 0 OR CustomerID = 52
That pulls all the active customers and the one customer for the record that I need. This allows the customer to still be linked to the record for editing AND doesn't generate a runtime exception with the DropDownList binding.
For reporting, I assume I'll just not filter based on the Archived bit since it's all read-only anyway.
Related
I'm building an app where I need to store invoices from customers so we can track who has paid and who has not, and if not, see how much they owe in total. Right now my schema looks something like this:
Customer
- Id
- Name
Invoice
- Id
- CreatedOn
- PaidOn
- CustomerId
InvoiceItem
- Id
- Amount
- InvoiceId
Normally I'd fetch all the data using Entity Framework and calculate everything in my C# service, (or even do the calculation on SQL Server) something like so:
var amountOwed = Invoice.Where(i => i.CustomerId == customer.Id)
.SelectMany(i => i.InvoiceItems)
.Select(ii => ii.Amount)
.Sum()
But calculating everything every time I need to generate a report doesn't feel like the right approach this time, because down the line I'll have to generate reports that should calculate what all the customers owe (sometimes go even higher on the hierarchy).
For this scenario I was thinking of adding an Amount field on my Invoice table and possibly an AmountOwed on my Customer table which will be updated or populated via the InvoiceService whenever I insert/update/delete an InvoiceItem. This should be safe enough and make the report querying much faster.
But I've also been searching some on this subject and another recommended approach is using triggers on my database. I like this method best because even if I were to directly modify a value using SQL and not the app services, the other tables would automatically update.
My question is:
How do I add a trigger to update all the parent tables whenever an InvoiceItem is changed?
And from your experience, is this the best (safer, less error-prone) solution to this problem, or am I missing something?
There are many examples of triggers that you can find on the web. Many are poorly written unfortunately. And for future reference, post DDL for your tables, not some abbreviated list. No one should need to ask about the constraints and relationships you have (or should have) defined.
To start, how would you write a query to calculate the total amount at the invoice level? Presumably you know the tsql to do that. So write it, test it, verify it. Then add your amount column to the invoice table. Now how would you write an update statement to set that new amount column to the sum of the associated item rows? Again - write it, test it, verify it. At this point you have all the code you need to implement your trigger.
Since this process involves changes to the item table, you will need to write triggers to handle all three types of dml statements - insert, update, and delete. Write a trigger for each to simplify your learning and debugging. Triggers have access to special tables - go learn about them. And go learn about the false assumption that a trigger works with a single row - it doesn't. Triggers must be written to work correctly if 0 (yes, zero), 1, or many rows are affected.
In an insert statement, the inserted table will hold all the rows inserted by the statement that caused the trigger to execute. So you merely sum the values (using the appropriate grouping logic) and update the appropriate rows in the invoice table. Having written the update statement mentioned in the previous paragraphs, this should be a relatively simple change to that query. But since you can insert a new row for an old invoice, you must remember to add the summed amount to the value already stored in the invoice table. This should be enough direction for you to start.
And to answer your second question - the safest and easiest way is to calculate the value every time. I fear you are trying to solve a problem that you do not have and that you may never have. Generally speaking, no one cares about invoices that are of "significant" age. You might care about unpaid invoices for a period of time, but eventually you write these things off (especially if the amounts are not significant). Another relatively easy approach is to create an indexed view to calculate and materialize the total amount. But remember - nothing is free. An indexed view must be maintained and it will add extra processing for DML statements affecting the item table. Indexed views do have limitations - which are documented.
And one last comment. I would strongly hesitate to maintain a total amount at any level higher than invoice. Above that level one frequently wants to filter the results in any ways - date, location, type, customer, etc. At this level you are approaching data warehouse functionality which is not appropriate for a OLTP system.
First of all never use triggers for business logic. Triggers are tricky and easily forgettable. It will be hard to maintain such application.
For most cases you can easily populate your reporting data via entity framework or SQL query. But if it requires lots of joins then you need to consider using staging tables. Because reporting requires data denormalization. To populate staging tables you can use SQL jobs or other schedule mechanism (Azure Scheduler maybe). This way you won't need to work with lots of join and your reports will populate faster.
This past week I was tasked with moving a PHP based database to a new SQL database. There are a handful of requirements, but one of those was using ASP.Net MVC to connect to the SQL database...and I have never used ASP.Net or MVC.
I have successfully moved the database to SQL and have the foundation of the ASP site set up (after spending many hours pouring through tutorials). The issue I am having now is that one of the pages is meant to display a handful of fields (User_Name, Work_Date, Work_Description, Work_Location, etc) but the only way of grabbing all of those fields is by combining two of the tables. Furthermore, I am required to allow the user to search the combined table for any matching rows between a user inputted date range.
I have tried having a basic table set up that displays the correct fields and have implemented a search bar...but that only allows me to search by a single date, not a range. I have also tried to use GridView with its Query Builder feature to grab the data fields I needed (which worked really well), but I can't figure out how to attach textboxes/buttons to the newly made GridView. Using a single table with GridView works perfectly and using textboxes/buttons is very intuitive. I just can't seem to make the same connection with a joined view.
So I suppose my question is this: what is the best way for me to combine these two tables while also still having the ability to perform searches on the displayed data? If I could build this database from scratch I would have just made a table with the relevant data attached to it, but because this is derived from a previously made database it has 12+ years of information that I need to dump into it.
Any help would be greatly appreciated. I am kind of dead in the water here. My inexperience with these systems is getting the better of me. I could post the code that I have, but I am mainly interested in my options and then I can do the research on my own.
Thanks!
It's difficult to offer definitive answers to your questions due to the need for guesswork.
But here are some hints.
You can say WHERE datestamp >= '2017-01-01' AND datestamp < '2018-01-01' to filter all the rows in calendar year 2017. Many variations on this sort of date range filter are available.
Your first table probably has some kind of ID number on each row. Let's call it first.first_id. Your second table probably has its own id, let's call it second.second_id. And, it probably has another id that identifies a row in your first table, let's call it second.first_id. That second.first_id is called a foreign key in the second table to the first table. There can be any number of rows in your second table corresponding to your first table via this foreign key.
If this is the case you can do something like this:
SELECT first.datestamp, first.val1, first.val2, second.val1, second.val2
FROM first
JOIN second ON first.first_id = second.first_id
WHERE first.datestamp >= '2018-06-01' AND first.datestamp < '2018-07-01'
AND (first.val1 = 'some search term' OR second.val1 = 'some search term')
ORDER BY first.datestamp
This makes a virtual table by joining together your two physical tables (FROM...JOIN...).
Then it filters the rows you want from that virtual table (FROM ...).
Then it puts them in the order you want (ORDER BY...).
Finally, it chooses the columns from the virtual table you want in your result set (SELECT ...).
SQL database servers (MySQL, SQL Server, postgreSQL, Oracle and the rest) are very smart about doing this sort of thing efficiently.
I'm writing code in C#.
It has two type of user: distributors and clients.
Clients can see all products of all distributors and can select them with mentioning the number of each product they need.So they make a list of orders.
Distributors can see lists of orders received from all clients to them. The table should contains "distributor id" and "client id" and "list of ordered products" with their cost and numbers which have ordered.
But I have no idea about saving this data to a single table.i want that each row mentions to a single list of orders from a special client to a special distributor.
Can I have a 3D table or an array or there is a better and more efficient way?
Is it possible to do what you want? Yes.
Should you? No.
I mean, let's say you set up your table. A column for the distributor, a column for the client, and a column for your list of orders (let's use XML for the example) :
DistID ClientID Orders
1 1 <item>A</item><item>B</item><item>C</item>
2 1 <item>D</item><item>C</item>
2 2 <item>E</item><item>A</item><item>F</item><item>G</item>
... so what problems will you run into? First, imagine trying to add or remove an order. You'd have to read the full Orders list, change it, and then write it all back to SQL. That's a lot of overhead, especially if your 'list' has a lot of items or if you're doing a lot of order adds/removes.
Worse, what happens when you need to find anyone that's ordered 'B'? You're going to have to do a full text scan on that column - no index seeks, no optimizations, nothing - just to find the matching items.
Here's my suggestion - take a look at "SQL Normalization" - basically, the guidelines on how to organize SQL data efficiently and effectively. Here are some good starter places:
http://www.studytonight.com/dbms/database-normalization.php
https://www.essentialsql.com/get-ready-to-learn-sql-database-normalization-explained-in-simple-english/
https://www.guru99.com/database-normalization.html
Based on the information you give, it seems you want a data model like this:
Distributor
Id
Name
Client
Id
Name
Product
Id
DistributorId (foreign key)
Order
Id
ProductId (foreign key)
ClientId (foreign key)
Number
That way you can store product orders from clients, and make sure distributors see order of their own products by selecting all orders having productId's that are linked to the selected distributor.
I have a issue regarding Merge Replication. I have a table SETTINGS where in i store the settings of my software.
The schema of the table is ID ( PK) , Description , Value.
Suppose i have 15 rows in this table on my server.
Now i have applied filter on this table saying only the first 10 rows would replicate.
Now with this settings when i sync for the first time, i receive the 10 rows on my client (having subscription).
Then i add the remaining 5 on my client.
Now when i sync again it gives me a conflict saying that
A row insert at 'ClientServer.ClientDatabaseName' could not be
propagated to 'MyServer.ServerDatabaseName'. This failure can be
caused by a constraint violation. Violation of PRIMARY KEY constraint
'PK_SETTINGS'. Cannot insert duplicate key in object 'dbo.SETTINGS'.
The duplicate key value is (11).
What i don't understand is why is it trying to replicate something (row) which is outside the subset filter applied on that table ?? Please help guys.
Is this scenario not possible with Merge replication ?
https://msdn.microsoft.com/en-us/library/ms151775.aspx the link suggests that this is possible. But confused.
Filters created on for a merge article are evaluated only at the publisher. Changes made at the subscriber will always be propagated back to the subscriber, even if they are outside the filter criteria. However if the changes from the one subscriber do not meet the filtering criteria, then they will sit on the publisher, but not be replicated to all the other subscribers.
Is this a production scenario, or are you playing around with replication? If you do static filtering, which is what you have above, it is typically done on read-only type of tables. For example, a salesperson in the field may only need prices for products in their region. They are not expected to update this table. If you do dynamic filtering, for example, filtering based on HOSTNAME(), then you would only get data specific for that user. For example, a salesperson in the field would receive only their customer information. Thus, any updates to that information, unless it's shared across multiple salespersons, would propagate back up, and not flow to anyone else.
In your case, i would not recommend updating tables on the subscriber that have static filters, thus i suggest re-evaluating your filtering design to ensure you have the right filtering model for your scenario.
I have set up a view on a legacy table containing order items. In my program, I want to distinguish between phone orders, wifi orders, internet orders, and miscellaneous orders. The legacy table doesn't have a discriminator column, however, you can group the orders by their product code string. Is there a way of creating a table-per-hierarchy from this design?
Example:
Phone codes: 30000-01, 30000-02, 30000-05, ...
Wifi codes: 30000-17, 30000-52, ...
etc.
Edit: I still need an order view showing all of these orders for the department listed as well.
You can create a View per type:
create view PhoneOrders as
select *
from orders
where code in ('30000-01', '30000-02',...
EF should be OK with these, as read-only anyway.