MySQL Challenge – Training Files: Collating one column’s data values from several related tables

Posted by admin at April 17, 2020

The training module of an application I am working required a section that displays all the file attachments from five different training types submitted by a user. A request was sent in requesting that files from all the tables be combined into one report.

The training records to be filed for each employee include the following:

  1. The minimum requirements for hire MIN_Q
  2. Formal classroom training with study credit hours CLASS_TRAIN,
  3. On-the-job training performed in the field OJT_TRAIN,
  4. Specialised training SPL_TRAIN
  5. Miscellaneous courses completed MISC_TRAIN.

Additionally, a user table (USER) is provided as well, containing the bio-data of employees.Each of these training tables have common fields:

  1. USER which is the integer for the user’s ID
  2. CERTIFICATE_FILE which contains the BLOB or link to the scanned copy of the training completion certificate.

The Challenge:

To search all 5 tables and collect the file attachments related a given user’s training and display them in the given format:

  1. Training ID TID
  2. Type of training TYPE
  3. Training title TITLE
  4. Date of Completion DATE_OF_COMPLETION
  5. File Link CERTIFICATE_FILE

The Solution (Sort of):

The approach here is to gather and build the sub-tables inside the MySQL query. Several sub-queries will be run to search the items inside each table and the final results will be combined in a master query.

Below is what I have:

SELECT
    -- build the table: combine the fields into HTML
    CONCAT(
    "<table>"
        , "<thead>"
        , "<tr>"
            , "<td colspan=\"5\">"
                , "Employee Name: "
                , `USER`.`NAME`
            , "</td>"
        , "</tr>"
        , "</thead>"
        , "<tbody>"
            -- minimum requirement rows
            , @file_mq
            -- classroom training rows
            , @file_class
            -- on the job training rows
            , @file_ojt
            -- specialisation training rows
            , @file_spl
            -- miscellaneous training rows
            , @file_misc
        , "</tbody>"
    , "<table>"
    )

    , @file_mq := @file_mq AS `@file_mq`
    , @file_class := @file_class AS `@file_class`
    , @file_ojt := @file_ojt AS `@file_ojt`
    , @file_spl := @file_spl AS `@file_spl`
    , @file_misc := @file_misc AS `@file_misc`

FROM `USERS`
WHERE 1
    -- fetch  the values from the minimun hire requirements
    , (SELECT @file_mq := CONCAT(
    , GROUP_CONCAT(
        CONCAT(
        "<tr>"
            , "<td>"
                , `MIN_Q`.`ID`
            , "</td><td>"
                , "Minimum Requirement"
            , "</td><td>"
                , `MIN_Q`.`TITLE`
            , "</td><td>"
                , `MIN_Q`.`DATE_OF_COMPLETION`
            , "</td><td>"
                , `MIN_Q`.`CERTIFICATE_FILE`
            , "</td>"
        , "</tr>"
        )
        SEPARATOR "")
    , "")
    FROM `MIN_Q`
    WHERE 1 AND  `MIN_Q`.`USER` = "3"
    ) AS `file_mq`

    -- fetch the rows from the classroom work
    , (SELECT @file_class := CONCAT(
    , GROUP_CONCAT(
        CONCAT(
        "<tr>"
            , "<td>"
                , `CLASS_TRAIN`.`ID`
            , "</td><td>"
                , "Classroom Training"
            , "</td><td>"
               , `CLASS_TRAIN`.`TITLE`
            , "</td><td>"
                , `CLASS_TRAIN`.`DATE_OF_COMPLETION`
            , "</td><td>"
                , `CLASS_TRAIN`.`CERTIFICATE_FILE`
            , "</td>"
        , "</tr>"
        )
        SEPARATOR "")
    , "")
    FROM `CLASS_TRAIN`
    WHERE 1 AND  `CLASS_TRAIN`.`USER` = "3"
    ) AS `file_class`

    -- fetch the rows from the on the job training
    , (SELECT @file_ojt := CONCAT(
    , GROUP_CONCAT(
        CONCAT(
        "<tr>"
            , "<td>"
                , `OJT_TRAIN`.`ID`
            , "</td><td>"
               , "On-the-Job Training"
             , "</td><td>"
               , `OJT_TRAIN`.`TITLE`
             , "</td><td>"
               , `OJT_TRAIN`.`DATE_OF_COMPLETION`
             , "</td><td>"
               , `OJT_TRAIN`.`CERTIFICATE_FILE`
            , "</td>"
        , "</tr>"
        )
        SEPARATOR "")
    , "")
    FROM `OJT_TRAIN`
    WHERE 1 AND  `OJT_TRAIN`.`USER` = "3"
    ) AS `file_ojt`

    -- fetch the specialised training
    , (SELECT @file_spl := CONCAT(
    , GROUP_CONCAT(
        CONCAT(
        "<tr>"
            , "<td>"
                , `SPL_TRAIN`.`ID`
            , "</td><td>"
                , "Specialised Training"
            , "</td><td>"
                , `SPL_TRAIN`.`TITLE`
            , "</td><td>"
                , `SPL_TRAIN`.`DATE_OF_COMPLETION`
            , "</td><td>"
                , `SPL_TRAIN`.`CERTIFICATE_FILE`
            , "</td>"
        , "</tr>"
        )
        SEPARATOR "")
    , "")
    FROM `SPL_TRAIN`
    WHERE 1 AND  `SPL_TRAIN`.`USER` = "3"
    ) AS `file_spl`

    -- fetch the miscellaneous
    , (SELECT @file_misc := CONCAT(
    , GROUP_CONCAT(
        CONCAT(
        "<tr>"
            , "<td>"
                , `MISC_TRAIN`.`ID`
            , "</td><td>"
                , "Miscellaneous Training"
            , "</td><td>"
                , `MISC_TRAIN`.`TITLE`
            , "</td><td>"
                , `MISC_TRAIN`.`DATE_OF_COMPLETION`
            , "</td><td>"
                , `MISC_TRAIN`.`CERTIFICATE_FILE`
            , "</td>"
        , "</tr>"
        )
        SEPARATOR "")
    , "")
    FROM `MISC_TRAIN`
    WHERE 1 AND  `MISC_TRAIN`.`USER` = "3"
    ) AS `file_misc`

AND `USERS`.`ID` = "3"

GROUP_CAT String Limits

Now this approach of building the html inside the MySQL takes a tool on the length limits. So we would have to run the following command before the above:

SET SESSION group_concat_max_len = 100000000

This will increase the GROUP_CONCAT result limits to 100M characters.

Picking valid files only

Additionally we can add check before each file row is added to the sub-query, to determine if the file exists. this way we can ensure that the file that the user has submitted a valid certificate file. Doing that, the minimum table sub-query will look like this:

, (SELECT @file_mq := CONCAT(
    , GROUP_CONCAT(
        CONCAT(
        "<tr>"
            , "<td>"
                , `MIN_Q`.`ID`
            , "</td><td>"
                , "Minimum Qualidication"
            , "</td><td>"
                , `MIN_Q`.`TITLE`
            , "</td><td>"
                , `MIN_Q`.`DATE_OF_COMPLETION`
            , "</td><td>"
                , `MIN_Q`.`CERTIFICATE_FILE`
            , "</td>"
        , "</tr>"
        )
        SEPARATOR "")
    , "")
    FROM
        `MIN_Q`
    WHERE 1
        AND  `MIN_Q`.`USER` = "3"
        AND LOAD_FILE(`MIN_Q`.`CERTIFICATE_FILE`) IS NOT NULL
) AS `file_mq`

   0 likes

Suggested Read