I have 4 MySQL tables with depending on each other via FOREIGN KEYs.
Please checkout this below schema for tables structure:
CREATE DATABASE IF NOT EXISTS courses
CHARACTER SET latin1
COLLATE latin1_bin;
CREATE TABLE IF NOT EXISTS courses.institution
(
icode INT UNSIGNED NOT NULL AUTO_INCREMENT,
iname VARCHAR(255) NOT NULL,
PRIMARY KEY (icode),
UNIQUE (iname)
)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS courses.cities
(
ccode INT UNSIGNED NOT NULL AUTO_INCREMENT,
cname VARCHAR(255) NOT NULL,
PRIMARY KEY (ccode),
UNIQUE (cname)
)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS courses.skills
(
scode INT UNSIGNED NOT NULL AUTO_INCREMENT,
sname VARCHAR(255) NOT NULL,
PRIMARY KEY (scode),
UNIQUE (sname)
)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS courses.relation
(
icode INT UNSIGNED NOT NULL,
scode INT UNSIGNED NOT NULL,
ccode INT UNSIGNED NOT NULL,
UNIQUE KEY ucols (icode, scode, ccode),
FOREIGN KEY (icode) REFERENCES courses.institution (icode),
FOREIGN KEY (scode) REFERENCES courses.skills (scode),
FOREIGN KEY (ccode) REFERENCES courses.cities (ccode)
)
ENGINE = InnoDB;
Currently I'm executing this below queries to insert just one record in relation table.
It costs 4 INSERT queries and 3 SELECT subqueries for just one insert on each time.
INSERT IGNORE INTO institution(iname) VALUES ('ABC Learners');
INSERT IGNORE INTO skills(sname) VALUES ('PHP');
INSERT IGNORE INTO cities(cname) VALUES ('Bangalore');
INSERT IGNORE INTO relation (icode, scode, ccode) VALUES (
(SELECT icode FROM institution WHERE iname = 'ABC Learners'),
(SELECT scode FROM skills WHERE sname = 'PHP'),
(SELECT ccode FROM cities WHERE cname = 'Bangalore')
);
Is it necessary to execute all this queries every time? or is there any better way to perform this action in single or few queries?
Checkout this below simple PHP code. In this code for just inserting 7 records on relation table it executes 4 INSERT queries with 3 SELECT subqueries on each record.
It costs total 7 * 7 = 49
queries for 7 records. How to solve this?
<?php
$db = new mysqli('localhost', 'user', '****', 'courses');
$records = [
['ABC Learners', 'CSS', 'Bangalore'],
['ABC Learners', 'PHP', 'Bangalore'],
['ABC Learners', 'HTML', 'Bangalore'],
['ABC Learners', 'PHP', 'Hyderabad'],
['XYZ Solutions', 'PHP', 'Hyderabad'],
['XYZ Solutions', 'JAVA', 'Hyderabad'],
['XYZ Solutions', 'JAVA', 'Bangalore'],
];
foreach ($records as $record) {
list($institute, $skill, $city) = $record;
$db->query("INSERT IGNORE INTO institution (iname) VALUES ('{$institute}')");
$db->query("INSERT IGNORE INTO skills (sname) VALUES ('{$skill}')");
$db->query("INSERT IGNORE INTO cities (cname) VALUES ('{$city}')");
$db->query(
"INSERT IGNORE INTO relation (icode, scode, ccode) VALUES (" .
"(SELECT icode FROM institution WHERE iname = '{$institute}'), " .
"(SELECT scode FROM skills WHERE sname = '{$skill}'), " .
"(SELECT ccode FROM cities WHERE cname = '{$city}'))"
);
}
$db->close();
NOTE: Above script is example purpose. Using batch mode or disabling auto commit is not useful for me. Because many times I need to add single new record to relation table (based on user request via web panel)
UPDATE 1:
After some research and benchmarks I have created a MySQL stored function to speed up this process and it increased performance by 250 - 300%
Please checkout function here:
DELIMITER $$
CREATE FUNCTION courses.record(i_name VARCHAR(255), s_name VARCHAR(255), c_name VARCHAR(255)) RETURNS INT
BEGIN
DECLARE _icode, _scode, _ccode INT UNSIGNED;
SELECT icode INTO _icode FROM institution WHERE iname = i_name;
SELECT scode INTO _scode FROM skills WHERE sname = s_name;
SELECT ccode INTO _ccode FROM cities WHERE cname = c_name;
IF _icode IS NULL THEN
INSERT IGNORE INTO institution (iname) VALUES (i_name);
SELECT icode INTO _icode FROM institution WHERE iname = i_name;
END IF;
IF _scode IS NULL THEN
INSERT IGNORE INTO skills (sname) VALUES (s_name);
SELECT scode INTO _scode FROM skills WHERE sname = s_name;
END IF;
IF _ccode IS NULL THEN
INSERT IGNORE INTO cities (cname) VALUES (c_name);
SELECT ccode INTO _ccode FROM cities WHERE cname = c_name;
END IF;
INSERT IGNORE INTO relation (icode, scode, ccode) VALUES (_icode, _scode, _ccode);
RETURN ROW_COUNT();
END $$
DELIMITER ;
Now, this below PHP script can insert one record in relation table with just one query
<?php
$db = new mysqli('localhost', 'user', '***', 'courses');
$records = [
['ABC Learners', 'CSS', 'Bangalore'],
['ABC Learners', 'PHP', 'Bangalore'],
['ABC Learners', 'HTML', 'Bangalore'],
['ABC Learners', 'PHP', 'Hyderabad'],
['XYZ Solutions', 'PHP', 'Hyderabad'],
['XYZ Solutions', 'JAVA', 'Hyderabad'],
['XYZ Solutions', 'JAVA', 'Bangalore'],
];
$query = $db->prepare("SELECT record (?, ?, ?)");
$query->bind_param('sss', $institute, $skill, $city);
foreach ($records as $record) {
list($institute, $skill, $city) = $record;
$query->execute();
}
$db->close();
This MySQL stored function increased the performance. But, still I'm using multiple INSERT and SELECT statements in this function. Is it possible to optimize this function with few statements to gain more performance?