-1

我有一个简单的文章和 tag_map 表作为

CREATE TABLE Articles
(
  ArticleID int(11) unsigned NOT NULL AUTO_INCREMENT,
  Title varchar(255),
  PRIMARY KEY(ArticleID)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_general_ci

CREATE TABLE Tags
(
  TagID int(11) unsigned NOT NULL AUTO_INCREMENT,
  Tag varchar(255),
  UNIQUE INDEX(Tag),
  PRIMARY KEY(TagID)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_general_ci

CREATE TABLE TagMap
(
  ArticleID int(11) unsigned NOT NULL,
  TagID int(11) unsigned NOT NULL,
  INDEX(TagID),
  PRIMARY KEY(ArticleID,TagID)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_general_ci

我通过 PHP 添加标签

$result = $mysqli->query("SELECT TagID FROM Tags WHERE Tag='$tag'");

if($result->num_rows == 1) {
    $row = $result->fetch_assoc();
    $tag_id = $row['TagID'];
}
else {
    $mysqli->query("INSERT INTO Tags (Tag) VALUES ('$tag')");
    $tag_id = $mysqli->insert_id;
}

$mysqli->query("INSERT INTO TagMap (ArticleID,TagID) VALUES ($article_id,$tag_id)");

我想知道是否有一种更快的方法可以在 MySQL 中的一个查询中执行此操作。

在这里,我需要 2 或 3 个查询来添加每个标签。

此外,当我们有一个标签列表时,我希望找到一种批处理方式INSERT(可能是通过)LOAD DATA LOCAL INFILE

ArticleID,Tag
1,tag2
2,tag11
4,tag3
4

2 回答 2

3

一种模式:

CREATE PROCEDURE load_to_TagMap ()
BEGIN
-- create table for loading data
CREATE TABLE tmp_TagMap ( ArticleID INT, Tag VARCHAR(255) ) ENGINE = Memory;
-- load data from file
LOAD DATA INFILE '/directory/filename.ext'
    INTO TABLE tmp_TagMap
    SKIP 1 LINES;
-- add absent tags into Tags table
INSERT INTO Tags (Tag)
    SELECT tmp_TagMap.Tag
    FROM tmp_TagMap 
    LEFT JOIN Tags USING (Tag)
    WHERE Tags.Tag IS NULL;
-- insert loaded data into TagMap table with lookup
INSERT INTO TagMap
    SELECT ArticleID, TagID
    FROM Tag
    JOIN tmp_TagMap USING (Tag);
-- remove loaded data table
DROP TABLE tmp_TagMap;
END

从 PHP 只需执行CALL load_to_TagMap;.

于 2021-06-10T12:18:47.210 回答
1

过度规范化。

“标签”往往是短字符串,对吗?为每个创建一个INT并进行二次查找的开销是不值得的。替换TagsTagMap_

CREATE TABLE Tags
(
  ArticleID int(11) unsigned NOT NULL,
  Tag VARCHAR(255) NOT NULL,
  PRIMARY KEY(ArticleID,Tag)
  INDEX(Tag, ArticleID)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_general_ci

这可能很有趣:http: //mysql.rjweb.org/doc.php/lists

更多的

SELECT COUNT(*) FROM Tags WHERE Tag = '...';

非常有效,即使 Tag 是VARCHAR. 这也简化了您的代码——您不需要额外的代码来计算计数器;删除文章时也很容易减少计数器:

DELETE FROM Tags WHERE ArticleID = ...;

如果您希望每个标签有 10 万篇文章,那么可能存在性能问题。您期望有多少文章和标签?

如果更大的图景是“Display the 'latest' 10 Articles for Tag='...',那么性能问题将出现在ORDER BY date DESC LIMIT 10. 目前涉及到 Article 表的连接,检查未删除,排序,等等,但我有一个解决方案:http: //mysql.rjweb.org/doc.php/lists

于 2021-06-10T15:03:39.767 回答