CSCI 5333.4 DBMS
Solution to Homework #8

Due date: 11/16 at the beginning of the class.

(1) No, it is not in 1NF. This is because the attribute "special_features" is not atomic. It is multi-valued. For example, the value can be:

"Trailers, Commentaries, Deleted Scenes, Behind the Scenes"

Note that it is specified that the values of special_features "can be zero or more of: Trailers, Commentaries, Deleted Scenes, Behind the Scenes." Thus, the followings are possible redesign to remove the violation of 1NF.

(a) Remove the attribute special_features from the relation file. Create four field of type Boolean (i.e. TINYINT(1)) with default value of 0: Trailers, Commentaries, Deleted_Scenes, Behind_the_Scenes.

Advantages: no decomposition and thus no need of additional JOIN for queries.
Disadvantages: SQL statements may be more complicated for certain queries. Less flexible for addition of possible special feature values.

(b) Remove the attribute special_features from the relation file. Create two tables to store the many to many relations between films and special features.

(i) special_feature(feature_id, feature, last_update)

primary key: feature_id
candiate keys: (1) feature_id and (2) feature

(ii) film_feature(film_id, feature_id, last_update)

primary key: film_id, feature_id
candidate key: film_id, feature_id
foreign keys: (1) film_id references file(film_id) and (2) feature_id references special_feature(feature_id)

Note that the attribute last_update is added to conform to the design of exsiting tables.

Advarntages: flexible for extension.
Disadvantages: decomposition may mean slower and more complicated queries

(2) For example:

DROP VIEW category_film_count;

CREATE VIEW category_film_count
AS
SELECT
   cat.name AS category,
   count(DISTINCT fc.film_id) AS "Number of distinct films",
   count(i.inventory_id) AS "Total copies of films"
FROM category AS cat
   LEFT JOIN film_category AS fc ON cat.category_id = fc.category_id
   JOIN inventory AS i ON fc.film_id = i.film_id
GROUP BY category;


(3) For example:

DROP PROCEDURE rewards_report_month;

delimiter //

CREATE PROCEDURE rewards_report_month (
    IN min_monthly_purchases TINYINT UNSIGNED
    , IN min_dollar_amount_purchased DECIMAL(10,2) UNSIGNED
    , IN year INT(4) UNSIGNED
    , IN month INT(2) UNSIGNED
    , OUT count_rewardees INT
)
LANGUAGE SQL
NOT DETERMINISTIC
READS SQL DATA
SQL SECURITY DEFINER
COMMENT 'Provides a customizable report on best customers'
proc: BEGIN

    DECLARE month_start DATE;
    DECLARE month_end DATE;

    /* Some sanity checks... */
    IF min_monthly_purchases = 0 THEN
      -- Return a table that can then be used. 
        SELECT 'Minimum monthly purchases parameter must be > 0';
        LEAVE proc;
    END IF;
    IF min_dollar_amount_purchased = 0.00 THEN
        SELECT 'Minimum monthly dollar amount purchased parameter must be > $0.00';
        LEAVE proc;
    END IF;

    /* Determine start and end time periods */
    SET month_start = STR_TO_DATE(CONCAT_WS('-',year,month,1),'%Y-%m-%d');
    SET month_end = LAST_DAY(month_start);

    /*
        Create a temporary storage area for
        Customer IDs.
    */
    CREATE TEMPORARY TABLE tmpCustomer (
      customer_id SMALLINT UNSIGNED NOT NULL PRIMARY KEY,
      number_purchased SMALLINT UNSIGNED,
      total_amount_purchased DECIMAL(10,2) UNSIGNED);

    /*
        Find all customers meeting the
        monthly purchase requirements
    */
  
   --  May modify to add number of purchases and total amount as output.
    INSERT INTO tmpCustomer (customer_id, number_purchased, total_amount_purchased)

    SELECT p.customer_id, COUNT(customer_id), SUM(p.amount)
    FROM payment AS p
    WHERE DATE(p.payment_date) BETWEEN month_start AND month_end
    GROUP BY customer_id
    HAVING SUM(p.amount) > min_dollar_amount_purchased
    AND COUNT(customer_id) > min_monthly_purchases;

    /* Populate OUT parameter with count of found customers */
    SELECT COUNT(*) FROM tmpCustomer INTO count_rewardees;

    /*
        Output
    */
  
   SELECT * from tmpCustomer;

    /* Clean up */
    DROP TABLE tmpCustomer;
END //

delimiter ;