SQL Column name for indicating a change - c#

I didn't really know how to formulate the title.
But this is my problem.
We are using SqlCacheDependencies to update out cache objects. But the query behind this is "complicated" that we would like to make it somewhat easier.
Our first thought was to add a fieldname like DateChanged and use the DateChanged to check if an object has actually changed in stead of loading all the columns of a table.
SELECT DateChanged FROM Table1
in stead of
SELECT Id, Title, DateChanged, Description FROM Table1
But I was hoping someone could tell me if there are other ways to do this or if there are standards for naming the column that indicates a change.
How are frameworks like "Entity framework" or "NHibernate" handle this?

It's usually called " timestamp"
ie in
LLBLGEN
LINQTOSQL

These are the columns I use on "auditable" entities:
version: hibernate's optimistic concurrency control
createdBy: FK to users
modifiedBy: FK to users
createdAt: timestamp
modifiedAt: timestamp

If you're on SQL Server be aware a timestamp column is somewhat misleadingly named. Whilst it can be used to track changes to a column, its value has nothing to do with the date or time. It can be thought of as an ID uniqiue within the database, nothing more. http://msdn.microsoft.com/en-us/library/aa260631.aspx
In order to know if a change has taken place you need two timstamps, like in cherouvim's answer. One is set when an object is created, the other on any modification, probably via update trigger. If they differ then the object has been edited, but you can't tell exactly when.
On SQL Server you have to use the same pattern, but with datetime columns. You can then calculate the date of the row by subtracting the creation date from the mod date.

Related

Best approach to track Amount field on Invoice table when InvoiceItem items change?

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.

Run SQL at a certain date/time

I've got a Microsoft SQL database containing a table called Bookings with a column EndDate and a column Status.
Whenever a row is inserted into Bookings, it will set the Status to be unavailable and insert an EndDate (like a week from now).
What I want to do is to automatically update Status from unavailable to available when the current date and time is equal to EndDate.
I can't use SQL Server Agent Jobs since I'm running Express Edition.
Thanks
This is a dangerous path that you want to go down. As soon as this process gets missed once you now have rows out there that have invalid data. Not to mention, you're spending an awful lot of processing time (and maintenance) just to mark a column that you should be able to determine at any point in time anyway.
Instead of trying to maintain duplicate data in your database (which will invariably get out of sync), just calculate it at run time or create a view to present it:
CREATE VIEW dbo.Bookings_WithStatus
AS
SELECT
booking_id,
start_date,
end_date,
CASE
WHEN COALESCE(end_date, GETDATE()) > GETDATE() THEN 'Unavailable'
ELSE 'Available'
END AS booking_status
FROM
dbo.Bookings
Although the logic is simple enough that I probably wouldn't even bother with a view myself.
#Sean Lange makes the excellent point that this can also be done as a computed column, which is probably better than using a view:
ALTER TABLE dbo.Bookings
DROP COLUMN booking_status
ALTER TABLE dbo.Bookings
ADD booking_status AS
CASE
WHEN COALESCE(end_date, GETDATE()) > GETDATE() THEN 'Unavailable'
ELSE 'Available'
END
If you need to do this logic constantly and if you can't use SQL Agent, maybe you need to consider a simple application like Windows Service or Console Application.
This can be triggered once per day by Windows Scheduler and that will go through all the records in your database, check if your conditions have met and update records if it is necessary?
You could do this by a service or a script run using sqlcmd, but I'd be inclined to step back and look at the base requirement.
To what end must the Status change at that exact time? The output data that you're looking for sounds much like it could be better accomplished using a view rather than a persisted field, and with much less complexity.
A solution without updating the status column might be sufficient. Your "available" rows are those whose EndDate is greater or equal to GETUTCDATE() assuming EndDate is in UTC

sorting a data table column containing timestamp with time zone

I have a grid that is populated using an Oracle 11g query that returns a TIMESTAMP(6) WITH TIME ZONE field along with other fields.
When I select a date range for data to be displayed that goes from 12/26/2014 to 1/5/2015, and then try to sort by this column (asc or desc), it does not sort properly. For example, in desc order it displays from 01/01 to 01/05 and then from 12/26 to 12/31. Looks like string sorting.
I am guessing a TIMESTAMP(6) WITH TIME ZONE field that contains a value like 21-JAN-2015 18:17:16:00000 USA/EASTERN is not recognized as a date-time but rather a string. Is there any way to resolve this issue?
Turning a comment into an answer:
There is a number of options:
(i) Rely on the database to sort the data correctly - which you might want to prevent for performance reasons.
(ii) Instruct the grid to handle the timestamp column as such (and not as a string column) - which might not be possible.
(iia) Use a different grid component, which handles date columns properly.
(iii) Use a hidden (string) column with the timestamp formatted as 'YYYY-MM-DD HH24…' for sorting.
(iv) Write your own sort routine.
Feel free to provide more detail if and as you see fit.

use metadata defined in db in code

I have metadata in a DB table that i want to use in code.
The metadata is different sorts of Time types for reporting spent time.
The data can be:
NormalTime
OverTime
Vacation
Illness
etc
The data have a ID and a description and some other stuff.
ID = 1
Name = "Regular time"
Description = "Normal work time"
What is a good way to relate to this data in my code?
If for example i want create a method that sums all the NormalTime reported (i have another table that stores used time where the NormalTime ID and amount and some other stuff) how do i do that?
I dont want to hardcode the ID:
Select * from xyz where TimeType = 1
What i wanna do is:
Select * from xyz where TimeType = NormalTime.
Otherwise the code becomes very hard to read.
In my current solution i have hardcoded string consts that correlates to the ID.
The problem with this is if someone changes the description of the TimeType from NormalTime to something eles the hardcoded string const sais one thing and the db data sais something else.
And yes, this has happend as i dont have control over the DB content :(
So, how do I solve this in the best maintainable and readable way where changes can occur in the DB table and the code dont get very hard to read.
Where someone can add TimeTypes to the DB and later I can add methods that uses them in code.
One way to do this would be to use Visual Studio's T4 text generation templates.
(Entity Framework uses these for its code generation)
You can create a template file which contains code to pull the tables with metadata
from the database, and generates classes with static constants in.
They do need to be run whenever the database changes, though. But I think you might be able
to set them up so they do re-generate every time your code is built.
A question about T4 templates
You could have an enum type on the C# side that maps to a table in the database.
http://www.codeproject.com/Articles/41746/Mapping-NET-Enumerations-to-the-Database

How to keep previous and new value of data?

I’m currently working on a project where we need to archive and trace all the modified data’s.
When a modification surrender, we have to kept theses information
Who has modified the data?
When?
And … that’s why I’m asking this question: Keep the previous
and the new value of the data.
Quickly, I have to trace every modification for every data.
Example :
I have a name field why the value “Morgan”.
When I modify this value, I have to be able to say to the user that the 6th of January, by XXX, the value changed from “Morgan” to “Robert” …
I have to find a clean and generic method to do this because a large amount of data is concerned by this behavior.
My program is in C# (.NET 4) and we are using Sql Server 2008 R2 and NHibernate for the object mapping.
Do you any ideas, experience or solution about how to do a thing like that?
I am a little confused about at what point you want to have the old vs new data available. But, this can be done within a database trigger as in the following question:
trigger-insert-old-values-values-that-was-updated
NHibernate Envers its what you want :)
You must use NHibernate 3.2+ (3.2 is the current release).
Its easy like
enversConf.Audit<Person>();
You can get info here and here
I've been in the same situation as you. I ended up doing in this way:
Save an ActivityEntry in the database containing an identity column (if you have multiple objects that change), an action-indicator (could be "User changed firstname", as a int), date field, userId and most important a parameter field.
Combining the values from the parameter field and the action-indicator I'm able to make strings like "{0} changed {1}'s firstname from {2} to {3}" where my parameter values could be "John;Joe".
I know it feels kinda wrong saving these totally loosely typed values in the database, but I believe it's the only way around, without having a copy of each table.

Categories

Resources