Tuesday, March 17, 2015

The day I created a SQL trigger and why it was a smart move

We have conflicting needs in one of our moodle instances.  We need to
  • Remove completed or dropped enrollments from our moodle site/courses. 
  • We also need to keep records or all enrollments and the work they did for perpetuity.
I created a new moodle instance and copied the externally configured DB responsible for enrollments.  I called it perpetuity_dbname.  Clever right?  Self describing?  Should persist?

Now my configuration looks like this:
  • live moodle site consumes enrollment DB
  • copy of live moodle site consumes copy of enrollments DB
What I was looking for was some sql code to copy records from the enrollment DB to the copy of enrollment DB.  Simple.  'ish'.  Clean?  Clean = logical, low maintenance, near the DBs in question.

First - I needed to find the SQL that would do it.  since this is a simple table to table copy operation, the SQL was not too hard to *find.

 INSERT INTO copyofliveDB.enrollments (courseid, username)
             SELECT courseid, username
             FROM liveDB.enrollments
                 WHERE (courseid,username) NOT IN
                  (
                SELECT courseid, username
                FROM copyofliveDB.enrollments
            );
This code is a little confusing for my brain...but it's solid.  Next step?  How to save it so I could call it.  I tested the SQL by adding records to the live DB table and watched them appear in the perpetuity DB.  I initially saved it as a snippet, but then moved it to a Stored procedure.  So I could call it from somewhere.

This is how I created a stored procedure.

DELIMITER //

CREATE PROCEDURE `copy_perpetuity` ()
LANGUAGE SQL
DETERMINISTIC
SQL SECURITY DEFINER
COMMENT 'copies records in the live DB that do not exist in the copyofliveDB'
BEGIN
    INSERT INTO copyofliveDB.enrollments (courseid, username)
             SELECT courseid, username
             FROM liveDB.enrollments
                 WHERE (courseid,username) NOT IN
                  (
                SELECT courseid, username
                FROM copyofliveDB.enrollments
            );
END//

Now that I had the SQL and had saved it to a stored procedure called copy_perpetuity - the last hurdle was how to call it.  My first thought was to call the file from windows on the cron script.  This makes some sense because the cron script is timed - so this stored procedure would be called automatically, a trigger of sorts.  A trigger to copy the new records from the live DB to the copy.

I looked at something like this:

Then I saw this post/code snippet on creating a mysql event - and specify the frequency of the call to the stored procedure.

CREATE EVENT myevent
    ON SCHEDULE EVERY 1 Day
    DO
      copy_perpetuity();
But, I ended up creating a trigger on the insertion event of the live DB table.  This seemed clean and simple.  This is the trigger created.

CREATE TRIGGER copy_perpetuity AFTER INSERT ON enrollments FOR EACH ROW CALL copy_perpetuity();

I used the other TRIGGER commands will figuring this out.  Straightforward.

DROP TRIGGER copy_perpetuity;
SHOW TRIGGERS;

 I tested the trigger by manually adding a record to the liveDB table and immediatly browsing the copyDB table and seeing the record.  Ya.

Moral of the story?

Do not rush to judgement.  I rolled around in this task for the better part of the day yesterday, read some things, thought about, marinated in the concept.  This am, I can in and in about 2 hrs had implemented and tested the solution.

Other links that helped.

Triggers
More triggers
Stack overflow post


2 comments:

  1. James - I was a little confused reading this about a year later. I was looking in the moodle db for the trigger. lead me into both user_enrol and enroll in persute of the trigger. I remember I had created one to, but forgot some of detail. Took me about 30 minutes of looking in the moodle db for the trigger before realizing that the trigger is one the other live db here, silly boy, the genuisIntegration DB, enrollments table. Once I looked there, and issued the show triggers command, there it was.....as advertised.

    ReplyDelete
  2. Hello James,
    We have same problem in our instance of moodle, and my thinking was similar to yours.
    I created copy of moodle original tables in same database (live moodle), for example for enrolment (original table) I made xenrolment (copy of original table), and for course (original table) I made xcourse and …
    After that I creating a trigger on the after insertion event on original course table (and similar on other tables)
    DELIMITER $$
    CREATE TRIGGER writetoxcourse
    AFTER INSERT
    ON m320_course FOR EACH ROW

    BEGIN
    INSERT INTO m320_xcourse (xid, xcategory, xfullname, xshortname, xidnumber, xstartdate, xenddate, xgroupmode, xgroupmodeforce, xtimecreated, xtimemodified)
    VALUES (id, category, fullname, shortname , idnumber, startdate, enddate, groupmode, groupmodeforce, timecreated , timemodified);
    END$$
    DELIMITER ;

    But when I try to create new course, I get error from moodle dmlexception (same with other tables).
    Idea of make copies of the tables in same database coming from simplicity of accessing and doing reports from standard “Configurable Reports” or “Ad-hoc”.
    Question: You didn’t have these errors in your work, and do you have some idea how to resolve this problem.

    ReplyDelete