MacLochlainns Weblog

Michael McLaughlin's Technical Blog

Site Admin

Archive for the ‘MySQL DBA Techniques’ tag

Fedora LAMP Steps

without comments

I posted earlier in the year how to configure a Fedora instance to test PHP code on a local VM. However, I’ve got a few questions on how to find those posts. Here’s a consolidation with links on those steps:

  1. Go to this blog post and install the httpd and php libraries with the yum installer.
  2. In the same blog post as step 1 (you can put the sample PHP code into the /var/www/html directory for testing), connect to the yum shell and remove the php-mysql library and then install the mysqlnd library.
  3. Go to this blog post and install the php-gd libraries, which enable you to render PNG images stored as binary streams in MySQL.

As always, I hope that helps.

Written by maclochlainn

December 9th, 2015 at 9:44 am

Querying InnoDB Tables

without comments

Somebody ran into the following error message trying to query the innodb_sys_foreign and innodb_sys_foreign_cols tables from the information_schema database:

ERROR 1227 (42000): Access denied; you need (at least one of) the PROCESS privilege(s) for this operation

It’s easy to fix the error, except you must grant the PROCESS privilege. It’s a global privilege and it should only be granted to super users. You grant the privilege global PROCESS privilege to the student user with the following command:

GRANT PROCESS ON *.* TO student;

Then, you can run this query to resolve foreign keys to their referenced primary key column values:

SELECT   SUBSTRING_INDEX(f.id,'/',-1) AS constraint_name
,        CONCAT(SUBSTRING_INDEX(for_name,'/',-1),'.',SUBSTRING_INDEX(for_col_name,'/',-1)) AS foreign_key_column
,        CONCAT(SUBSTRING_INDEX(ref_name,'/',-1),'.',SUBSTRING_INDEX(ref_col_name,'/',-1)) AS primary_key_column
FROM     innodb_sys_foreign f INNER JOIN innodb_sys_foreign_cols fc
ON       f.id = fc.id
WHERE    SUBSTRING_INDEX(f.for_name,'/',-1) = 'system_user_lab'
ORDER BY CONCAT(SUBSTRING_INDEX(for_name,'/',-1),'.',SUBSTRING_INDEX(for_col_name,'/',-1))
,        CONCAT(SUBSTRING_INDEX(ref_name,'/',-1),'.',SUBSTRING_INDEX(ref_col_name,'/',-1));

It returns the following:

+---------------------+--------------------------------------+------------------------------------+
| constraint_name     | foreign_key_column                   | primary_key_column                 |
+---------------------+--------------------------------------+------------------------------------+
| system_user_lab_fk1 | system_user_lab.created_by           | system_user_lab.system_user_id     |
| system_user_lab_fk2 | system_user_lab.last_updated_by      | system_user_lab.system_user_id     |
| system_user_lab_fk3 | system_user_lab.system_user_group_id | common_lookup_lab.common_lookup_id |
| system_user_lab_fk4 | system_user_lab.system_user_type     | common_lookup_lab.common_lookup_id |
+---------------------+--------------------------------------+------------------------------------+
4 rows in set (0.00 sec)

However, you can get the same information without granting the global PROCESS privilege. You simply use the table_constraints and key_column_usage tables, like this:

SELECT   tc.constraint_name
,        CONCAT(kcu.table_name,'.',kcu.column_name) AS foreign_key_column
,        CONCAT(kcu.referenced_table_name,'.',kcu.referenced_column_name) AS primary_key_column
FROM     information_schema.table_constraints tc JOIN information_schema.key_column_usage kcu
ON       tc.constraint_name = kcu.constraint_name
AND      tc.constraint_schema = kcu.constraint_schema
WHERE    tc.constraint_type = 'foreign key'
AND      tc.table_name = 'system_user_lab'
ORDER BY tc.table_name
,        kcu.column_name;

It prints the same output:

+---------------------+--------------------------------------+------------------------------------+
| constraint_name     | foreign_key_column                   | primary_key_column                 |
+---------------------+--------------------------------------+------------------------------------+
| system_user_lab_fk1 | system_user_lab.created_by           | system_user_lab.system_user_id     |
| system_user_lab_fk2 | system_user_lab.last_updated_by      | system_user_lab.system_user_id     |
| system_user_lab_fk3 | system_user_lab.system_user_group_id | common_lookup_lab.common_lookup_id |
| system_user_lab_fk4 | system_user_lab.system_user_type     | common_lookup_lab.common_lookup_id |
+---------------------+--------------------------------------+------------------------------------+
4 rows in set (0.00 sec)

Hope this helps.

Written by maclochlainn

December 27th, 2014 at 1:18 am

Popular PHP Frameworks

with 2 comments

My students often ask me about popular PHP frameworks and MySQL approaches. I wish a PHP framework choice was as clear as a JavaScript framework, like AngularJS. Unfortunately, PHP frameworks aren’t that clear cut.

It seems that the most popular PHP frameworks are: Laravel (1st), Phalcon (2nd), and Symphony2 (3rd). I found the following graphic (from December 2013) that highlights popularity by percentage of the market (though I can’t guarantee its accuracy). As far as jobs go, on Dice.com only Laravel had more than 3 positions referring to the framework by name. There were actually 42 PHP developer positions that mention Laravel out of 2,115 PHP developer positions. So, it seems learning a specific framework for PHP doesn’t yet have much market appeal.

PHPFrameworkPopularity-1024x853

While learning the basics of PHP are generic, frameworks expedite process and control pattern implementation. My suggestion to students is to target three to five employers that use a specific framework and start learning how to use the framework. I’d like to get opinions from those in the field whether this is the best advice.

As to MySQL, I suggest they learn native Mysqli and MySQL PDO. Any other suggestions on that from readers? Thanks as always.

Written by maclochlainn

December 23rd, 2014 at 1:55 pm

Cleanup a MySQL Schema

with 7 comments

My students required way to remove all their tables, views, and constraints from a MySQL database (or the alias schema). Since they’re using referential or foreign key constraints, I also wrote one procedure to drop all foreign key constraints from a database. There’s also another to drop views. The final stored procedure calls the procedure that drops foreign keys, then calls the procedure to drop views before dropping all the tables.

Here’s the dropForeignKeys stored procedure, but if you want to capture ALTER statements that add these back later please check my follow-up Capture MySQL Foreign Keys post.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
-- Provide a log file debugging statement.
SELECT 'DROP PROCEDURE IF EXISTS dropForeignKeys';
 
-- Conditionally drop the procedure.
DROP PROCEDURE IF EXISTS dropForeignKeys;
 
-- Provide a log file debugging statement.
SELECT 'CREATE PROCEDURE dropForeignKeys';
 
-- Change delimiter to create procedure.
DELIMITER $$
 
-- Create procedure.
CREATE PROCEDURE dropForeignKeys
( pv_database          VARCHAR(64)
, pv_referenced_table  VARCHAR(64))
BEGIN
 
  /* Declare local statement variables. */
  DECLARE lv_stmt VARCHAR(1024);
 
  /* Declare local cursor variables. */
  DECLARE lv_table_name       VARCHAR(64);
  DECLARE lv_constraint_name  VARCHAR(64);
 
  /* Declare control variable for handler. */
  DECLARE fetched       INT DEFAULT 0;
 
  /* Declare local cursor. */
  DECLARE foreign_key_cursor CURSOR FOR
    SELECT   rc.table_name
    ,        rc.constraint_name
    FROM     information_schema.referential_constraints rc
    WHERE    constraint_schema = IFNULL(pv_database,database())
    AND      referenced_table_name = pv_referenced_table
    ORDER BY rc.table_name
    ,        rc.constraint_name;
 
  /* Declare a not found record handler to close a cursor loop. */
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET fetched = 1;
 
  /* Open a local cursor. */  
  OPEN foreign_key_cursor;
  cursor_foreign_key: LOOP
 
    FETCH foreign_key_cursor
    INTO  lv_table_name
    ,     lv_constraint_name;
 
    /* Place the catch handler for no more rows found
       immediately after the fetch operation.          */
    IF fetched = 1 THEN LEAVE cursor_foreign_key; END IF;
 
    /* Set a SQL statement by using concatenation. */
    SET @SQL := CONCAT('ALTER TABLE',' ',lv_table_name,' ','DROP FOREIGN KEY',' ',lv_constraint_name);
 
    /* Prepare, run, and deallocate statement. */
    PREPARE lv_stmt FROM @SQL;
    EXECUTE lv_stmt;
    DEALLOCATE PREPARE lv_stmt;
 
  END LOOP cursor_foreign_key;
  CLOSE foreign_key_cursor;  
 
END;
$$
 
-- Reset delimiter to run SQL statements.
DELIMITER ;

Here’s the dropViews stored procedure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
-- Provide a log file debugging statement.
SELECT 'DROP PROCEDURE IF EXISTS dropViews';
 
-- Conditionally drop the procedure.
DROP PROCEDURE IF EXISTS dropViews;
 
-- Provide a log file debugging statement.
SELECT 'CREATE PROCEDURE dropViews';
 
-- Change delimiter to create procedure.
DELIMITER $$
 
-- Create procedure.
CREATE PROCEDURE dropViews
( pv_database  VARCHAR(64))
BEGIN
 
  /* Declare local statement variables. */
  DECLARE lv_stmt VARCHAR(1024);
 
  /* Declare local cursor variables. */
  DECLARE lv_view_name       VARCHAR(64);
 
  /* Declare control variable for handler. */
  DECLARE fetched       INT DEFAULT 0;
 
  /* Declare local cursor. */
  DECLARE view_cursor CURSOR FOR
    SELECT   v.table_name
    FROM     information_schema.views v
    WHERE    table_schema = IFNULL(pv_database, database())
    ORDER BY v.table_name;
 
  /* Declare a not found record handler to close a cursor loop. */
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET fetched = 1;
 
  /* Open a local cursor. */  
  OPEN view_cursor;
  cursor_view: LOOP
 
    FETCH view_cursor
    INTO  lv_view_name;
 
    /* Place the catch handler for no more rows found
       immediately after the fetch operation.          */
    IF fetched = 1 THEN LEAVE cursor_view; END IF;
 
    /* Set a SQL statement by using concatenation. */
    SET @SQL := CONCAT('DROP VIEW',' ',lv_view_name);
 
    /* Prepare, run, and deallocate statement. */
    PREPARE lv_stmt FROM @SQL;
    EXECUTE lv_stmt;
    DEALLOCATE PREPARE lv_stmt;
 
  END LOOP cursor_view;
  CLOSE view_cursor;  
 
END;
$$
 
-- Reset delimiter to run SQL statements.
DELIMITER ;

Here’s the dropTables stored procedure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
CREATE PROCEDURE dropTables
( pv_database  VARCHAR(64))
BEGIN
 
  /* Declare local statement variables. */
  DECLARE lv_stmt  VARCHAR(1024);
 
  /* Declare local cursor variables. */
  DECLARE lv_table_name  VARCHAR(64);
 
  /* Declare control variable for handler. */
  DECLARE fetched       INT DEFAULT 0;
 
  /* Declare local cursor. */
  DECLARE table_cursor CURSOR FOR
    SELECT   t.table_name
    FROM     information_schema.tables t
    WHERE    table_schema = IFNULL(pv_database, database())
    ORDER BY t.table_name;
 
  /* Declare a not found record handler to close a cursor loop. */
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET fetched = 1;
 
  /* Drop the views. */
  CALL dropViews(null);
 
  /* Open a local cursor. */  
  OPEN table_cursor;
  cursor_table: LOOP
 
    FETCH table_cursor
    INTO  lv_table_name;
 
    /* Place the catch handler for no more rows found
       immediately after the fetch operation.          */
    IF fetched = 1 THEN LEAVE cursor_table; END IF;
 
    /* Drop the tables. */
    CALL dropForeignKeys(null,lv_table_name);
 
    /* Set a SQL statement by using concatenation. */
    SET @SQL := CONCAT('DROP TABLE',' ',lv_table_name);
 
    /* Prepare, run, and deallocate statement. */
    PREPARE lv_stmt FROM @SQL;
    EXECUTE lv_stmt;
    DEALLOCATE PREPARE lv_stmt;
 
  END LOOP cursor_table;
  CLOSE table_cursor;  
 
END;
$$
 
-- Reset delimiter to run SQL statements.
DELIMITER ;

You put these in a rerunnable script, run it, and then call the dropTables stored procedure. You can pass a database (or schema) name or a null value. When you pass a null value, it uses the current database, like:

CALL dropTables(null);

As always, I hope this helps others.

Written by maclochlainn

February 7th, 2014 at 1:38 am