I have a file receive location which is schedule to run at specific time of day. I need to trigger a alert or mail if receive location is unable to find any file at that location.
I know I can create custom components or I can use BizTalk 360 to do so. But I am looking for some out of box BizTalk feature.
BizTalk is not very good at triggering on non-events. Non-events are things that did not happen, but still represent a certain scenario.
What you could do is:
Insert the filename of any file triggering the receive location in a custom SQL table.
Once per day (scheduled task adapter or polling via stored procedure) you would trigger a query on the SQL table, which would only create a message in case no records were made that day.
Also think about cleanup: that approach will require you to delete any existing records.
Another options could be a scheduled task with a custom c# program which would create a file only if there were no input files, etc...
The sequential convoy solution should work, but I'd be concerned about a few things:
It might consume the good message when the other subscriber is down, which might cause you to miss what you'd normally consider a subscription failure
Long running orchestrations can be difficult to manage and maintain. It sounds like this one would be running all day/night.
I like Pieter's suggestion, but I'd expand on it a bit:
Create a table, something like this:
CREATE TABLE tFileEventNotify
(
ReceiveLocationName VARCHAR(255) NOT NULL primary key,
LastPickupDate DATETIME NOT NULL,
NextExpectedDate DATETIME NOT NULL,
NotificationSent bit null,
CONSTRAINT CK_FileEventNotify_Dates CHECK(NextExpectedDate > LastPickupDate)
);
You could also create a procedure for this, which should be called every time you receive a file on that location (from a custom pipeline or an orchestration), something like
CREATE PROCEDURE usp_Mrg_FileEventNotify
(
#rlocName varchar(255),
#LastPickupDate DATETIME,
#NextPickupDate DATETIME
)
AS
BEGIN
IF EXISTS(SELECT 1 FROM tFileEventNotify WHERE ReceiveLocationName = #rlocName)
BEGIN
UPDATE tFileEventNotify SET LastPickupDate = #LastPickupDate, NextPickupDate = #NextPickupDate WHERE ReceiveLocationName = #rlocName;
END
ELSE
BEGIN
INSERT tFileEventNotify (ReceiveLocationName, LastPickupDate, NextPickupDate) VALUES (#rlocName, #LastPickupDate, #NextPickupDate);
END
END
And then you could create a polling port that had the following Polling Data Available statement:
SELECT 1 FROM tFileEventNotify WHERE NextPickupDate < GETDATE() AND NotificationSent <> 1
And write up a procedure to produce a message from that table that you could then map to an email sent via SMTP port (or whatever other notification mechanism you want to use). You could even add columns to tFileEventNotify like EmailAddress or SubjectLine etc. You may want to add a field to the table to indicate whether a notification has already been sent or not, depending on how large you make the polling interval. If you want it sent every time you can ignore that part.
One option is to set up a BAM Alert to trigger if no file is received during the day.
Here's one mostly out of the box solution:
BizTalk Server: Detecting a Missing Message
Basically, it's an Orchestration that listens for any message from that Receive Port and resets a timer. If the timer expires, it can do something.
Related
I am able to delete a channel from the back office UI and run the DeleteDomainReferences job in SMC to clear the reference and be able to create a new channel again with the same id.
However, once an order has been created, the above mentioned process won't work.
I heard that we can run some stored procedures against the database for situation like this.
Question: what are the stored procedures and steps to take to be able to clean any reference in Intershop so that I can create a channel with the same id again?
Update 9/26:
I did configure a new job in SMC to call DeleteDomainReferencesTransaction pipeline with ToBeRemovedDomainID attribute set to the domain id that I am trying to clean up.
The job ran without error in the log file. The job finished almost instantly, though.
Then I ran the DeleteDomainReferences job in SMC. This is the job I normally run after deleting a channel when there is no order in that channel. This job failed the following exception in the log file.
ORA-02292: integrity constraint (INTERSHOP.BASKETADDRESS_CO001) violated - child record found
ORA-06512: at "INTERSHOP.SP_DELETELINEITEMCTNRBYDOMAIN", line 226
ORA-06512: at line 1
Then I checked BASKETADDRESS table and did see the records for that domain id. This is, I guess, the reason why DeleteDomainReferences job failed.
I also execute the SP_BASKET_OBSERVER with that domain id, but it didn't seem to make a difference.
Is there something I am missing?
sp_deleteLineItemCtnrByDomain
-- Description : This procedure deletes basket/order related stuff.
-- Input : domainID The domain id of the domain to be deleted.
-- Output : none
-- Example : exec sp_deleteLineItemCtnrByDomain(domainid)
This stored procedure should delete the orders. Look up the domainid that you want to delete in the domaininformation table and call this procedure.
You can also call the pipeline DeleteDomainReferencesTransaction. Setup an smc job that calls this pipeline with the domainid that you want to clean up as a parameter. It also calls a second sp that cleans up the payment data so it actually a better approach.
Update 9/27
I tried this out on my local 7.7 environments. The DeleteDomainReferences job also removes the orders from the isorder table. No need to run sp_deleteLineItemCtnrByDomain separately. Recreating the channel I see no old orders. I'm guessing that you discovered a bug in the version you are running. Maybe related to the address table being split into different tables. Open a ticket for support to have them look at this.
With the assistance from intershop support, it has been determined that, in IS 7.8.1.4, the sp_deleteLineItemCtnrByDomain.sql has issue.
line 117 and 118 from 7.8.1.4
delete from staticaddress_av where ownerid in (select uuid from staticaddress where lineitemctnrid = i.uuid);
delete from staticaddress where lineitemctnrid = i.uuid;
should be replaced by
delete from basketaddress_av where ownerid in (select uuid from basketaddress where basketid = i.uuid);
delete from basketaddress where basketid = i.uuid;
After making the stored procedure update, running DeleteDomainReference job finishes without error and I was able to re-create the same channel again.
The fix will become available in 7.8.2 hotfix as I was told.
I have a SQL query which is running on a view and on top has lot of wild card operators, hence taking a lot of time to complete.
The data is consumed by an ASP.net application, is there any way I could pre-run the query once in a day so data is already there when asp.net application needs it and only pass on the parameter to fetch specific records.
Much simplified example would be
select * from table
Run every day and result stored somewhere and when asp.net passes on the parameter only specific records are fetched like
select * from table where field3 = 'something'
Either use SQLAgent (MSSQL) or equivalent to run a scheduled process that stores the result into a Table like this...
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[MyTemporaryTable]') AND type in (N'U'))
BEGIN
TRUNCATE TABLE [dbo].[MyTemporaryTable];
INSERT INTO [dbo].[MyTemporaryTable]
SELECT * FROM [vwMyTemporaryTable];
END
ELSE
BEGIN
SELECT *
INTO [MyTemporaryTable]
FROM [vwMyTemporaryTableDataSource];
END
or you could store the result in ASP.Net as an Application/Session variable or even a Property in a class that is stored in Application/Session. The Property approach will load the data the first time it is requested, and use memory thereafter.
private MyObjectType _objMyStoredData;
public MyObjectType MyStoredData
{
get
{
if (_objMyStoredData == null)
{
_objMyStoredData = GetMyData();
}
return _objMyStoredData;
}
}
However, if your source data for this report is only 2,000 rows... I wonder if all this is really necessary. Perhaps increasing the efficiency of the query could solve the problem without delving into pre caching and the downsides that go with it, such as re-using data that could be out of date.
You can use redis. You can run the view once the user logs in. Then fill redis with the view data. Set that object in Session Context of user so that it is accessible on all pages. Then when the user logs out. clean up the redis. By this way user won't go to database everytime for result instead will get the data from redis cache. it's very fast. You can contact me if more help is needed.
you can mark it as answer if you find it helpful.
Can someone provide a link or an example of an Oracle DB trigger that sends an email if a specific column in the table gets updated AND updates a different column in the same table?
Example:
A table has multiple issues and there are two columns 'Issue Added' and 'Email Sent' that default to '0'
Issue Added Email Sent
0 0
When the 'Issue Added' column gets updated to '1'
Issue Added Email Sent
1 0
Trigger sends email and updates column 'Email Sent'
Issue Added Email Sent
1 1
It would generally be a bad idea to try to send an email in a trigger.
If the system is unable to send the email (for example, because the SMTP server is temporarily down), the trigger will fail and the triggering statement will fail and be rolled back. It is very rare that you would really want to stop the underlying transaction simply because you weren't able to send an email.
Sending an email is non-transactional. That means that you'll send emails for changes that never get committed. And you'll send emails multiple times because Oracle chooses to rollback and re-execute all or part of an INSERT statement in order to maintain write consistency.
You'll generally be much better served with a database job that periodically looks for rows that need to have an email sent, sends the emails, and then updates the table. You can use either the older DBMS_JOB package or the newer and more sophisticated DBMS_SCHEDULER package. Something along the lines of
CREATE OR REPLACE PROCEDURE process_issues
AS
BEGIN
FOR i IN (SELECT *
FROM your_table_name
WHERE issue_added = 1
AND email_sent = 0)
LOOP
send_email( i.issue_id );
UPDATE your_table_name
SET email_sent = 1
WHERE issue_id = i.issue_id;
END LOOP;
END;
which is then scheduled to run, say, every 5 minutes (you could also use the DBMS_SCHEDULER package)
DECLARE
l_jobno PLS_INTEGER:
BEGIN
dbms_job.submit( l_jobno,
'BEGIN process_issues; END;',
sysdate + interval '5' minute,
'sysdate + interval ''5'' minute' );
commit;
END;
You can use the UTL_MAIL package to implement the send_email procedure. You probably just need to call UTL_MAIL.SEND with appropriate parameters (assuming you've configured your SMTP_OUT_SERVER parameter and your user has been granted appropriate access to the UTL_MAIL package and to an ACL that allows you to communicate with that SMTP server).
I am developing an Auction site in asp.net3.5 and sql server 2008R2, My Database has an Auction Table that has a calculated column "AuctionStatus" -
(case when [EndDateTime] < getdate() then '0' else '1' end)
that gives auction status Active or inactive based on End Date.
Now I want to call a stored procedure that sends email notifications to buyers and sellers as soon as AuctionStatus becomes '0'. For that i tried to create a after update trigger that could call the email notification sp, but i am not able to do so.
I am getting the following error message :-
Msg 2114, Level 16, State 1, Procedure trgAuctionEmailNotification,
Line 6 Column 'AuctionStatus' cannot be used in an IF UPDATE clause
because it is a computed column.
The trigger is:
CREATE TRIGGER trgAuctionEmailNotification ON SE_Auctions
AFTER UPDATE
AS
BEGIN
IF (UPDATE (AuctionStatus))
BEGIN
IF EXISTS (SELECT * FROM inserted WHERE currentbidderid > 0
AND AuctionStatus='0' )
BEGIN
DECLARE #ID int
SELECT #ID = AuctionID from inserted
EXEC spSelectSE_AuctionsByAuctionID #ID
END
END
END
You could just replace AuctionStatus with the corresponding expression :
IF EXISTS (SELECT * FROM inserted WHERE currentbidderid > 0 AND [EndDateTime] < getdate() )
But, the point is I don't see how your trigger will be "triggered" as [AuctionStatus] is never "updated". Its Value is just calculated whenever you need it.
You could go for a sql job that runs every x minutes and send a notification for each auction which ended during the last x minutes.
You need to add a real column containing a flag to indicate whether the notifications have been sent, and then implement a polling technique to scan the table for rows where the status is inactive and notifications haven't been sent.
The computed column doesn't really transition from one state to another, so it's not like an UPDATE has occurred. Even if SQL Server did implement this, it would be hideously expensive, since it would have to query the entire table for transitioning rows every 3ms. (or even more frequently if you're using datetime2 with a higher precision)
Whereas you can pick a suitable polling interval yourself. This could be an SQL agent job, or in some service code somewhere, whatever best fits the rest of your architecture.
I would like to find out if it is possible to find out which package or procedure in a package is updating a table?
Due to a certain project being handed over (the person who handed over the project has since left) without proper documentation, data that we know we have updated always go back to some strange source point.
We are guessing that this could be a database job or scheduler that is running the update command without our knowledge. I am hoping that there is a way to find out where the source code is calling from that is updating the table and inserting the source as a trigger on that table that we are monitoring.
Any ideas?
Thanks.
UPDATE: I poked around and found out
how to trace a statement back to its
owning PL/SQL object.
In combination with what Tony mentioned, you can create a logging table and a trigger that looks like this:
CREATE TABLE statement_tracker
( SID NUMBER
, serial# NUMBER
, date_run DATE
, program VARCHAR2(48) null
, module VARCHAR2(48) null
, machine VARCHAR2(64) null
, osuser VARCHAR2(30) null
, sql_text CLOB null
, program_id number
);
CREATE OR REPLACE TRIGGER smb_t_t
AFTER UPDATE
ON smb_test
BEGIN
INSERT
INTO statement_tracker
SELECT ss.SID
, ss.serial#
, sysdate
, ss.program
, ss.module
, ss.machine
, ss.osuser
, sq.sql_fulltext
, sq.program_id
FROM v$session ss
, v$sql sq
WHERE ss.sql_address = sq.address
AND ss.SID = USERENV('sid');
END;
/
In order for the trigger above to compile, you'll need to grant the owner of the trigger these permissions, when logged in as the SYS user:
grant select on V_$SESSION to <user>;
grant select on V_$SQL to <user>;
You will likely want to protect the insert statement in the trigger with some condition that only makes it log when the the change you're interested in is occurring - on my test server this statement runs rather slowly (1 second), so I wouldn't want to be logging all these updates. Of course, in that case, you'd need to change the trigger to be a row-level one so that you could inspect the :new or :old values. If you are really concerned about the overhead of the select, you can change it to not join against v$sql, and instead just save the SQL_ADDRESS column, then schedule a job with DBMS_JOB to go off and update the sql_text column with a second update statement, thereby offloading the update into another session and not blocking your original update.
Unfortunately, this will only tell you half the story. The statement you're going to see logged is going to be the most proximal statement - in this case, an update - even if the original statement executed by the process that initiated it is a stored procedure. This is where the program_id column comes in. If the update statement is part of a procedure or trigger, program_id will point to the object_id of the code in question - you can resolve it thusly:
SELECT * FROM all_objects where object_id = <program_id>;
In the case when the update statement was executed directly from the client, I don't know what program_id represents, but you wouldn't need it - you'd have the name of the executable in the "program" column of statement_tracker. If the update was executed from an anonymous PL/SQL block, I'm not how to track it back - you'll need to experiment further.
It may be, though, that the osuser/machine/program/module information may be enough to get you pointed in the right direction.
If it is a scheduled database job then you can find out what scheduled database jobs exist and look into what they do. Other things you can do are:
look at the dependencies views e.g. ALL_DEPENDENCIES to see what packages/triggers etc. use that table. Depending on the size of your system that may return a lot of objects to trawl through.
Search all the database source code for references to the table like this:
select distinct type, name
from all_source
where lower(text) like lower('%mytable%');
Again that may return a lot of objects, and of course there will be some "false positives" where the search string appears but isn't actually a reference to that table. You could even try something more specific like:
select distinct type, name
from all_source
where lower(text) like lower('%insert into mytable%');
but of course that would miss cases where the command was formatted differently.
Additionally, could there be SQL scripts being run through "cron" jobs on the server?
Just write an "after update" trigger and, in this trigger, log the results of "DBMS_UTILITY.FORMAT_CALL_STACK" in a dedicated table.
The purpose of this function is exactly to give you the complete call stack of al the stored procedures and triggers that have been fired to reach your code.
I am writing from the mobile app, so i can't give you more detailed examples, but if you google for it you'll find many of them.
A quick and dirty option if you're working locally, and are only interested in the first thing that's altering the data, is to throw an error in the trigger instead of logging. That way, you get the usual stack trace and it's a lot less typing and you don't need to create a new table:
AFTER UPDATE ON table_of_interest
BEGIN
RAISE_APPLICATION_ERROR(-20001, 'something changed it');
END;
/