插入 MySQL 中的 tag_map

问题描述

我有一个简单的文章和 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

解决方法

一种模式:

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 万篇文章,那么可能存在性能问题。您需要多少文章和标签?

如果大图是“为 Tag='...显示'最新的 10 篇文章',那么性能问题将出现在 ORDER BY date DESC LIMIT 10 中。目前涉及到文章表的连接,请检查用于未“删除”、排序等。但我有一个解决方案:http://mysql.rjweb.org/doc.php/lists

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...