I have a table, and it has the Columns Quantity, Price and LineTotal. When I add something to the Cart, I add the Quantity and the Price of the product, and when the user visits the Cart page to view their cart, I want to re-calculate the LineTotal when they update the quantities for the items they've selected.
Now, my question is, should I re-calculate the LineTotal of each item in the cart using SQL (and if so, how?) or, should I do it in C# (And if so, what would be the best way to go about this?) To my surprise, I can't really seem to find anything on calculations in SQL - other than forums where people talk about it, but I have yet to see any code or documentation.
You should be able to use the arithmetic operators within SQL to generate your line totals. Reference here.
Well if you wanted to you could change the LineTotal column to a computed column but there's nothing stopping you from doing it in C# before the Insert/update
Since it seems to be a fairly simple calculation its tough to really strongly go with one or the other, and its mostly up to preference
Computed Column sample
ALTER TABLE dbo.YourTable
ADD LineTotalComputed AS (Quantity * Price) PERSISTED
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.
We have a billing system where we process individual charges as well as recurring charges (subscriptions).
There are two SQL tables:
StandardCharges
RecurringCharges
StandardCharges table holds individual items purchased by customers during the month.
RecurringCharges table holds recurring items with a charge by date. When the time comes our system automatically creates a recur request which adds a row to the StandardCharges table and increases the charge by date to next month in RecurringCharges table.
At the end of each month we get the total values for each customer from StandardCharges table and create an invoice.
Is there a kind of design pattern or another way of doing this? Is this the right database design? Ideally I would like to hold all charges in one Charges table and manage recurring charges from there as well?
Thanks
I suspect that your design is indeed correct.
When thinking about the data in real world terms it makes no sense to have "possible" transactions (I.E., transactions which have not yet happened and may not materialize, perhaps because the customer had overrun their credit limit) mixed in with committed and actual transactions.
Merging the data into a single table can also make reporting difficult as you have to apply special filtering criteria and store extra meta data - like TransactionCompleted and TransactionIsFutureCharge.
If I was to make a suggestion it would be renaming the StandardCharges to something closer to the data it holds like CompletedTransactions and the RecurringTransactions something like PendingTransactions.
The current design seems reasonable to me. But if you want to merge the two tables, you could simply add a BIT column called IsRecurring or IsFuture or IsScheduled or whatever you want to use to designate the charges that would have otherwise gone in RecurringCharges. Then when your due date is hit for a recurring charge, you just insert into the same table instead of a different table. As for the invoice, you'd just add a condition to the query to filter out the charges that have the BIT column set.
is stackoverlow database structure available somewhere?
I am facing a situation where I have Table1 with information about something and Table2 with numeric information about Table1 on a 1:n relatioship. Like we have users and answers here on stack overflow for example.
I need to have quick access to the sum of the numeric information (like S.O. has quick access to the user's reputation sum) and I'm wondering if I add a column on Table1 to store the sum and update it everytime table2 is updated or if I sum all records on table2 every time I need that Info.
S.O. has the same situation and seems to be dealing very well with it. I would like to know which approach do they use.
Yes, we denormalize that into a database field that we update over time. We used to do recalcs (it would drift occasionally) by performing the join / sum, but that hurts - and we didn't like it being out of sync in the first place. So now, we keep a specific store for reputation changes over time - more direct and accurate history.
Your situation is not defined by the realtionship in SO between posts, replies and rep. You have a totally generic database situation where you need to sum values associated with a header record.
You have hot upon the 2 usual ways to solve this requirement
Sum the values using the aggregation methods available to SQL when you need the value
Sum the values as new rows are added, and store it against the header
Both have pros and cons, but there is mainly 1 weigh-up to consider
Data integrity maintained (pro), performance hindered (con)
Data corruption possible (con), performance not hindered (pro)
I have a normalized table which shows the supply delivery days for different supplies. The table is normalized keeping with good DB practices and shows the day of the week as a numeric value (1,2,3 etc). I am using Entity framework and a Telerik grid and need to display the weekdays on the grid showing each day in the week and the min/max number of units that can be delivered on that day. This table (Supply Deliveries) is linked to the Product Table. I have shown the table design and the desired format in the grid below.
I am not sure how to display this data in the grid. I was told I can use Presentation model to display this? I haven't any examples of how to do this. If someone can show me with a code example preferably on what's the best way to do this with Entity Framework and C# so it can take the no of day and know where to bind in the grid that would be great. Many thanks in advance!
Table: Products
product_id (PK, INT, not null)
ProductName (varchar(150), not null)
Cost (decimal(18,2), not null)
Table : SupplyDeliveries
schedule_id (PK, INT, not null)
product_id (FK, INT, not null)
DayOfTheWeek (smallint, not null) //(Day of the week stored in number for ex 1,2,3 )
MinNo (int, not null)
MaxNo (int, not null)
*NOTE: So if I wanted to show schedule for Paper deliveries in table SupplyDeliveries here is what that record would look like for product_id = 1 (Paper), DayofWeek = 1 (Monday), MinNo=4, MaxNo=5
so in the grid you wil see for Dayoftheweek = 1 (Monday) the min/max units (4/5) I can recieve and there will be another record for product_id=1 (Paper), DayOftheWeek = 2 (Tuesday) to show the min/max units I can get as well..there will be a seperate record for each product for each day of the week.....hope that helps
This is what I want to show in a grid:
Product Name Cost Mon Tue Wed Thu Fri Sat Sun
Paper $5 4/5 4/5
Stationery $20 4/5 8/10 8/10
Printers $100 4/5 5/6 5/6
First of all, regarding your model, why do you have a schedule_id column in your Products table? You are storing the relationship between product and schedule in the SupplyDeliveries table, so it seems like the schedule_id column in your Products table is unnecessary.
What you are trying to do is called a pivot. You're taking data modeled as rows and displaying it as columns. As far as I know, there is no explicit mechanism for expressing a pivot in LINQ.
There are several approaches you could take here:
You could create a view in your database that pivots the data and expresses it just as you've shown in your results. Use EF to query the view and display the results in the grid. Display should be easy since the entities materialized by EF will be exactly what you're trying to display.
You could use EF and queries over a grouping expression to perform a pivot. This likely will not be as fast as doing the pivot in a view in the db, but should accomplish the same result. See below for an example.
You could also change your db model so that it is already column based. One thing to note about your existing model is that without a second unique index on SupplyDeliveries(product_id, DayOfTheWeek), you could have multiple "Monday" records for the same product. Maybe that's okay... However, if you don't want that in the first place, another model you could consider for your data would be to have columns: (product_id, mon_min, mon_max, tue_min, ...). This eliminates the pivot entirely.
Here's an example for #2:
from s in SupplyDeliveries
group s by s.product_id into g
select new
{
ProductId = g.Key,
MondayMin = (from x in g where x.DayOfTheWeek == 1 select x.MinNo).FirstOrDefault(),
MondayMax = (from x in g where x.DayOfTheWeek == 1 select x.MaxNo).FirstOrDefault(),
TuesdayMin = ...
}
Edit:
So to recap, approach #1 has you constructing a pivot query in SQL and exposing it to EF as a view, while #2 does it in EF as a LINQ expression over the underlying table. The advantage of #1 (depending on your underlying database) is you could take advantage of SQL operators like PIVOT and transform your data more efficiently before it hits the application layer. The advantage of #2 is that you can keep this transformation in the application layer, which might be easier for you to maintain, especially if your database up until this point is strictly just tables.
With regards to #3, it's just a suggestion and representative of what you could do. I don't know the details of your model and your application, so it's hard to make complete suggestions. However, I would not be concerned with the data being sparse in this case - there are relatively few columns involved, especially if you have only one min/max per weekday per product. From a space efficiency point of view, excluding product_id, you have 56 bytes in the column approach and 14 bytes in the row approach (in the row approach you also have to store the day of week and a separate schedule_id column). So if you have 4 days of the week specified on average per product you break even. This excludes the extra space you'll need in the row approach for appropriate indexing. Also, in the row approach, your queries will always be more complex (i.e. slower) because of extra joins and filtering.
Thanks for your help Michael, your suggestion got me thinking in the right direction I ended up making a pivot table and using a DTO class to bind to the result. I was able to get all the values the way I wanted. I hope this helps someone else as well I looked at the following example for creating pivot table
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=110576