插入 MySQL 中的 tag_map

INSERTing into a tag_map in MySQL

我有一篇简单的文章和 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.[=17= 的一个查询中是否有更快的方法来执行此操作]

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

此外,当我们有一个标签列表

时,我希望找到一种批量处理 INSERT 的方法(可能通过 LOAD DATA LOCAL INFILE
ArticleID,Tag
1,tag2
2,tag11
4,tag3

一个模式:

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;.

过度规范化。

“标签”往往是短字符串,对吗?为每个创建一个 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 万篇文章,则可能存在性能问题。您期望有多少篇文章和标签?

如果大图是“显示标签='...'的 'latest' 10 篇文章,那么性能问题将出现在 ORDER BY date DESC LIMIT 10。目前这涉及到一个连接到文章 table、检查是否 'deleted'、排序等。但我有一个解决方案:http://mysql.rjweb.org/doc.php/lists