basic trigger lock issue - sql

I have one question regarding trigger.
The scenario is like this
Create Procedure
begin
Insert into XYZ (a) values (b)
end
Now i have placed the trigger on INSERT - AFTER on table XYZ.
In that trigger there is business logic which takes 2-3 seconds to execute it, business logic is performed against other database table not on the XYZ table
So what i need to confirm here that once INSERT is been done, then the table XYZ will be ready to do insert for another records or it will be locked until the trigger is completed?
EDIT
I have done some more research on this issue and explain it below
In the INSERT - TRIGGER, i have put the my business logic and also below line
WAITFOR DELAY '00:01'
Now when i try to execute the above SP, the SP was not completed for 1 minues (as i have specified the delay of 1 minute in the trigger) and table XYZ was also locked during this period.
So this brings me to the conclusion that trigger does LOCKS the table even if you are not using the same table in the trigger. Am i right? Does anyone has different opinion here?

The question and answer linked to by #Hallainzil show one approach:
Wrap all table INSERTs and UPDATEs into Stored Procedures
The SP can then complete the additional Business Logic without maintaining a lock
There is also another approach which is slightly messier in several ways, but also more flexible in many ways:
Keep a record of which fields have been INSERTED or UPDATED
Have an agent job fire repeatedly or overnight to process those changes
You may use a trigger to keep that record. Maybe with a LastModifiedTime field, or a hasBeenProcessed field, or even a separate tracking table. It can be done in many ways, and is relatively light weight to maintain (none of the business logic happens yet).
This releases your table from any locks as quickly as possible. It also means that you are able to deal with logins that have the ability to write directly to your table, circumventing your Stored Procedures.
The down side is that your INSERTS/UPDATES and your business logic are being processed Asynchronously. Your other SQL code may need to check whether or not the business logic ha sbeen completed yet, rather than just assuming that both the INSERT and the Business Logic always happen atomically.
So, yes, there are ways of avoiding this locking. But you introduce additional constraints and/or complexity to your model. This is by no means a bad thing, but it needs to be considered within your overall design.

Related

Is using triggers best solution for this scenario

A large SQL transactional database has more than 100 tables (and it will grow). One of them is called Order. Then, there is another table WorkLoad which derives from Order and many other joined table which contains a list of all active order. Every time an order record is created, if it meets certain conditions, it should be instantly inserted into WorkLoad table. And finally, there is a third table WorkLoadAggregation which displays aggregated data grouped by date and shop and it is completely built from WorkLoad table. WorkLoadAggregation should also display live data meaning that if a record is inserted in WorkLoad table then matching date/shop aggregation should also be updated.
My idea was to handle this by following triggers:
When record is inserted in Order table, trigger calls stored procedure which inserts record into WorkLoad table
When Order record is deleted trigger deletes the record from WorkLoad table
When Order record is updated in a way that it doesn't meet WorkLoad conditions, trigger deletes the record from WorkLoad table
When record is inserted/deleted/updated in WorkLoad table, trigger calls stored procedure which updates matching date/shop aggregated record in WorkLoadAggregation table
I haven't used triggers that much in such large transaction dbs and for such frequent calls. Is there anything bad in this approach? My biggest concern is usage of "chained triggers", meaning that trigger on one table activates trigger on another table. I've been reading few articles which state that developers should be very cautious when using triggers. Are there any better solutions? Should I consider any NoSQL solution?
Database is hosted on SQL Server 2012.
Note: In case 5) the stored procedure that's called contains a CTE (case someone suggests using an indexed view)
It is a little difficult to provide a more concrete opinion, but based on the presentation of the problem space. I would not recommend this solution, as it would be difficult to test effectively and I can see this causing issues under times of high load. Also it is really hard to quantify the total impact as I am not sure what the read load would look like and how many other processes may need information out of those tables.
Based on how you have described the problem, and the fact that you asked about NoSQL approaches, I assume that eventual consistency is not much of a concern so I would recommend a more EventDriven Architecture. Keep in mind that this may mean a significant re-write of your current implementation but would definitely allow for better domain decomposition and scaling.

Ensuring business rule is not broken with multiple users

Suppose I have an order which has order lines and we have a multi-user system where different users can add order lines. Orders are persisted to the database
One business rule is that only 10 order lines can be created for an order.
What are my options to ensure that this rule is not broken? Should I check in memory and apply a lock or do I handle this in the database via procedures?
You have a bunch of options on how you can handle this, including: triggers, constraints, business rule logic, and data structure.
My preference is the following. Wrap all inserts/deletes/updates to orderlines in a stored procedure and only give the application layer access to the table through the stored procedure. This procedure can then enforce this and other business rules. It would also lock the table for updates while running, so only one user can change the table at any given instant.
A similar approach is to have an insert instead-of trigger on the table. This would cause the insert to fail when this (and other) business rules fail. The main concern with this is maintainability. One trigger on one table is fine. However, you can end up with a triggering nightmare if you start doing this for multiple tables with cascading inserts/deletes.
You can attempt to do this with a constraint, as one of the comments suggests. You would definitely want to test this for performance, because you have little control over how the constraint is implemented.
Also, you could have ten orderline columns in the order table. This would emphatically enforce this rule. But, there is a cost to having separate columns for each order line and having to deal with issues such as deletes.
Another option, not appropriate in this case, is to enforce the business rule at the application level. However, with multiple concurrent users, you should be doing this work in the database.
Assume the worst case, that a large number (aka lots more than 2) of users are all simultaneously attempting to add lines to a single order. (Even worse, assume they’re all trying to add varying numbers of lines, though never more than 10 at a time -- the GUI can control at least that much). This leads to scenarios like:
A checks is there space for her rows, gets ok
B checks is there space for his rows, gets ok
A adds her rows, life is good
B adds his rows, now there are 11
All of this took place within less than a tenth of a second
To avoid this, there needs to be some form of central “checkpoint” or regulator where all users go to determine if they can add rows. If they pass the checkpoint, they can add; if the don’t pass, they can’t. The checkpoint must be absolute: once approval is received/assigned, that decision impacts all subsequent checks (i.e. when you check and are granted the 10th line, no one else has previously been granted it, and no one else subsequently will be granted it).
There are several ways to implement this, and they all involve database transactions (ACID properties). Transactions must always be as brief as possible, to avoid blocking or deadlocking with other users. This can be tricky code, and for my money the best way to implement/control the process is via stored procedures (like #Gordon Linoff said).

SQL Server - On-Insert Trigger vs. Per-Minute Job

My question is mainly concerned with "what's best for performance", but kinda "philosophically" speaking as well (if it makes a difference)... so let's jump right in.
[TableA].[ColumnB] stores a value that needs to exist in [TableC].[ColumnD]. Right off the bat, no answers involving Foreign-keys - just assume that they're "not allowed" in this environment for whatever reason.
But due to "circumstances x,y,z", [TableA].[ColumnB] sometimes gets values that do not exist in [TableC].[ColumnD], because, let's say, [TableA] gets populated from an object that exists in running code as a "serialized blob", an in-memory representation of the data, and the [ColumnB] values got populated before those values were deleted from [TableC].[ColumnD] by some other process. ANYWAY, this is for example's sake, so don't get bogged down in the "why does this condition happen", just accept that it does.
To "fix" the problem, which method is best of these two: 1. make a Trigger that fires on-INSERT on [TableA], to Update [ColumnB] to the value that it should be (and assume I have a "mapping" of bad-to-good values). Or, 2. run a scheduled-Job every hour/minute/whatever that runs Update queries to change all possible "bad" values to their corresponding "good" values.
To put it more generally, what's better for performance and/or what is best practice: a Trigger, or a periodic Scheduled-Job? In context, let's say [TableA] is typically on the order of hundreds of thousands of rows, with Inserts happening 10-100 records at-a-time, as frequently as every few minutes to as rarely as a few times per day.
On-insert.
Doing triggers is like callbacks- They're more logically sound, and they spread any lag into every query. Doing continual checks (called polling or cron-jobs), you end up with more severe moments of lag every now and then. In almost all cases, using triggers/callbacks are the better way to go as having 1ms of lag added to every query is better than 100ms of lag at seemingly random intervals.
Use of triggers is generally discouraged, but your load is light and your case seems to be a natural trigger case. Consider using instead-of trigger to avoid two operations on the same row (one insert instead of insert and update). It may be the simplest and most reliable solution (as long as you have written reliable code in the trigger that won't cause the whole operation to crash).
Since you are considering a batch job, you are not concerned with timing issues. I.e it's OK with your application that tables may be out of sync for 1 minute or even 1 hour. That's the major difference with the trigger approach, which will guarantee that tables are in sync all the time. Potential timing issues would make me uncomfortable. On the plus side, you won't be at risk of crashing the original insert operation with your trigger.
If you go this route, please consider Change Tracking feature. Change tracking will indicate which rows have been inserted since the last time you checked, so you won't have to scan the whole table for new records. Alternatively, if your TableA has an INDENITY primary or unique key, you can implement similar design without change tracking functionality.
Triggers are both best performance and practice, as they maintain referential integrity as well as allowing the server to optimise for performance.
You didn't say what version of SQL Server you were using, but if it's 2008+, you can use Change Data Capture to keep track of data changes to your "primary" table. Then, periodically, you can run a batch over the change table and do whatever processing is required over that small set.

difference before and after trigger in oracle

Can somebody explain difference between "before" and "after" trigger in oracle 10g with an example ?
First, I'll start my answer by defining trigger: a trigger is an stored procedure that is run when a row is added, modified or deleted.
Triggers can run BEFORE the action is taken or AFTER the action is taken.
BEFORE triggers are usually used when validation needs to take place before accepting the change. They run before any change is made to the database. Let's say you run a database for a bank. You have a table accounts and a table transactions. If a user makes a withdrawal from his account, you would want to make sure that the user has enough credits in his account for his withdrawal. The BEFORE trigger will allow to do that and prevent the row from being inserted in transactions if the balance in accounts is not enough.
AFTER triggers are usually used when information needs to be updated in a separate table due to a change. They run after changes have been made to the database (not necessarily committed). Let's go back to our back example. After a successful transaction, you would want balance to be updated in the accounts table. An AFTER trigger will allow you to do exactly that.
I'm not completely sure what you're interested in knowing, so I'll keep this fundamental.
Before Triggers
As per the name, these triggers are fired prior to creating the row in the table. Subsequently, since the row has not yet been created you have full access to the :new.table_element field. This allows for data cleansing and uniformity if unwanted/malformed data is attempting to be inserted/updated. This is just a basic example, but you need to utilize the before trigger any time you may require access to the ":new" data.
After Triggers
Since the after trigger fires once the row has already been created, these triggers are typically utilized when you want logic to occur due to the row. For example, if you have an address table and a user updates his/her address, then you may want to update the address reference ids in an xref table upon creation (if you happen to be retaining all old addresses as well). Also, unlike the before trigger, you do not have access to modify any of the column values since the row already exists in the table.
BEFORE TRIGGER are used when the trigger action should determine whether or not the triggering statements should be allowed to complete .by using BEFORE TRIGGERS user can eliminate unnecessary processing of the triggering statement
but,AFTER TRIGGERS are used when the triggering statements should completed before executing the trigger action.

Is it possible to prevent batch update at the sql database level?

A simple stupid "UPDATE table SET something=another WHERE (always true)" in accident will easily destroy everything in the database. It could be a human mistake, an SQL injection/overflow/truncation attack, or a bug in the code who build the WHERE causes.
Are popular databases provide a feature that protect tables by limit maximum number of row could be updated in one SQL statement?
I mean some kind of defensive setting that apply to pre-table access right on database: no-way to bypass, less code to program, no human mistake(unless grant yourself too much access right).
You can add a trigger that checks how many rows are being updated (count the Inserted magic trigger table), and RAISEERROR if that's too many rows.
I don't know of anything.
I'm not sure that this would solve anything. How can the database distinguish between a SQL injection attack and a nightly batch update that happens to exceed your limit?
One assumption is the auto commit is set to true. If the SQL injection attack isn't committed, you always have the opportunity to roll it back, assuming that you're looking at logs and such.
I think the real answer is better layering of apps, validation, binding, etc. You can't do SQL injection if those measures are in place.
The short answer is "no"...
Oracle allows you set define profiles that can be assigned to users to limit usage of resources such as CPU, logical reads. However, it isn't intended for your purpose, it is more about managing resources in a multi-user environment.
Perhaps more importantly, it also has flashback table so that unintended changes can be easily undone.
Most of your scenarios should be dealt with by other means:
human mistake: most users should not be granted update privileges on tables, they should be forced to call APIs (typically via an application) to perform updates. DBAs must be very careful when accessing live databases - never mind row limits, they can drop the table altogether!
Injection attack: these can and should be prevented from occuring
Code bugs: these should be caught through proper testing
If your data is important, it should be properly protected and looked after as above, and a maximum row update limit is unnecessary; if your data isn't important enough to protect as above, then why worry?
As David B was first to point out, you can do this with a trigger. It's a good practice to start your triggers with a ##ROWCOUNT test anyway. So imagine:
CREATE TRIGGER dbo.trg_myTrigger_UD ON dbo.myTable FOR UPDATE, DELETE
AS
IF ##ROWCOUNT <> 1 RETURN
This would kick out any updates and/or deletes that try to affect more than one row.
As a general rule, I start mine with a rowcount test of <> 0. The point being if the trigger was kicked off by something that actually affected no rows (UPDATE table SET col1 = 'hey' WHERE 1 = 0) then there's no point in running through the trigger code as it won't do anything anyway.
I understand your reasons, but how do you want handle batches, which are legitimate?
If you do manualy some changes, and you want be able "undo" the changes, use transactions.
If you want be able recontruct data, use archive of changes.
But you are not able create check for "this is correct/incorrect batch" only from batch with 100% correct results.
You just need to write stored procedures and only expose those to users. And you will not work in a priviledged account in normal situations. Only connect as admin when needed.
You can wrap the update in a transaction and prompt the user (letting them know how many row are going to be updated) before committing.