【发布时间】:2019-12-25 09:32:29
【问题描述】:
我有 4 个 MySQL 表,它们通过 FOREIGN KEYs 相互依赖。
请查看以下架构以了解表格结构:
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;
目前我正在执行以下查询以在 relation 表中仅插入一条记录。
每次只插入一次就需要 4 次 INSERT 查询和 3 次 SELECT 子查询。
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')
);
是否有必要每次都执行所有这些查询?或者在那里 在单个或几个查询中执行此操作的更好方法?
查看下面的简单 PHP 代码。在这段代码中,仅在 relation 表上插入 7 条记录,它执行 4 INSERT 查询,每条记录有 3 SELECT 子查询。
总共需要花费7 * 7 = 49 查询 7 条记录。如何解决?
<?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();
注意:上面的脚本是示例目的。使用 批处理模式 或 禁用自动提交 对我没有用。因为很多时候我需要向关系表中添加一条新记录(基于用户通过网络面板的请求)
更新 1:
经过一些研究和基准测试,我创建了一个 MySQL 存储函数来加速这个过程,并将性能提高了 250 - 300%
请在此处结帐功能:
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 ;
现在,下面这个 PHP 脚本可以通过一个查询在关系表中插入一条记录
<?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();
这个 MySQL 存储函数提高了性能。但是,我还是 在此函数中使用多个 INSERT 和 SELECT 语句。 是否有可能用很少的语句来优化这个函数来获得 性能更高?
【问题讨论】:
-
一次插入只能插入一张表。您的最后一个插入不需要 selects ,为什么您没有像在其他插入上那样使用 insert..values ?
-
@P.Salmon 因为最后一次插入所需的值是
auto_incrementids,所以在成功插入其他 3 个之前,OP 不知道它们 -
一个建议:在
relation表中,不要将组合(icode, scode, ccode)定义为UNIQUE键,而是将其定义为PRIMARY键。 -
@ChandraNakka 现在您正在循环中多次执行一个准备好的语句,正如我在回答中提出的那样。但是你为什么不使用事务呢?
标签: php mysql foreign-keys