【问题标题】:Joining Tables in MySQL (slow)在 MySQL 中连接表(慢)
【发布时间】:2021-04-12 13:49:16
【问题描述】:

我正在尝试将一些数据保存在 2 个单独的表中,然后将它们与参考表连接起来。

我的数据如下所示(搜索查询和页面 URL):

[
['widget', 'www.example.com/widgets'],
['blue widget', 'www.example.com/blue-widgets'],
['red widget', 'www.example.com/widgets'],
['widget', 'www.example.com/green-widgets'],
['orange widget', 'www.example.com/widgets'],
]

如您所见,一些搜索查询被分配到多个页面,考虑到这一点以及其他一些客户端性能原因,我想将搜索查询和页面 URL 拆分到单独的表中,但是跟踪哪个搜索查询属于哪个页面网址。

为此,到目前为止,我有以下 2 个表(unique_key 列只是用于阻止添加重复项的哈希):

CREATE TABLE queries (
        id BIGINT NOT NULL AUTO_INCREMENT,
        query VARCHAR(400) collate utf8_bin,
        unique_key varchar(100) unique,
        PRIMARY KEY (id));

CREATE TABLE pages (
        id BIGINT NOT NULL AUTO_INCREMENT,
        page VARCHAR(2083),
        unique_key varchar(100) unique,
        PRIMARY KEY (id));

insert into queries (query, unique_key) values 
('widget', '1234'),
('widgets', '1233'),
('blue widget', '3243'), 
('red widget', '5432'),
('green widget', '4642');

insert into pages (page, unique_key) values 
('www.example.com/widgets', '7895'),
('www.example.com/widgets-1', '4569'),
('www.example.com/widgets-2', '4568'),
('www.example.com/widgets-3', '1254'),
('www.example.com/widgets-4', '6527');

然后,为了构建将查询与页面相关联的数据透视表,我这样做:

CREATE TABLE page_query_join (
    id BIGINT NOT NULL AUTO_INCREMENT,
    query_id int,
    page_id int,
    PRIMARY KEY (id));

insert into page_query_join (query_id, page_id)
                    values ((select id from queries where query='widget' limit 1), (select id from pages where page='www.example.com/widgets' limit 1)) ON DUPLICATE KEY UPDATE query_id=query_id

这都是在 Python 脚本中完成的,我基本上创建哈希键,在一个循环中插入所有搜索查询,然后在另一个循环中插入所有页面 URL,然后最后一次循环并执行最后查询加入他们。

这个最终查找的完整函数如下所示:

def add_query_page_join(self, data):
    sql = '''insert into page_query_join (query_id, page_id)
                values ((select id from queries where query=%s limit 1), (select id from pages where page=%s limit 1)) ON DUPLICATE KEY UPDATE query_id=query_id'''
    try:
        # Execute the SQL command
        self.cursor.executemany(sql, data)
        # Commit your changes in the database
        self.db.commit()
        #return self.cursor.lastrowid
    except Exception as e:
        # Rollback in case there is any error
        self.db.rollback()
    finally:
        self.db.close()

data 变量如下所示:

data = [
['widget', 'www.example.com/widgets'],
['blue widget', 'www.example.com/blue-widgets'],
['red widget', 'www.example.com/widgets'],
['widget', 'www.example.com/green-widgets'],
['orange widget', 'www.example.com/widgets'],
]

我的数据变量一次传递 1,000 个项目,但我每天总共有大约 100 万个项目要做。我当前的版本需要几个小时才能完成。

我可以做些什么来提高连接表查询的性能,或者只是我将所有内容添加到表中的方式(也许有一种方法可以一次插入所有 3 个表?)

感谢您的帮助 - 如有任何遗漏,请告诉我。

更新 解释join查询:

+----+-------------+---------+------+---------------+-------+---------+-------+------+----------------+
| id | select_type | table   | type | possible_keys | key   | key_len | ref   | rows | Extra          |
+----+-------------+---------+------+---------------+-------+---------+-------+------+----------------+
|  1 | PRIMARY     | NULL    | NULL | NULL          | NULL  | NULL    | NULL  | NULL | No tables used |
|  3 | SUBQUERY    | pages   | ALL  | NULL          | NULL  | NULL    | NULL  | 8641 | Using where    |
|  2 | SUBQUERY    | queries | ref  | query         | query | 768     | const |    1 | Using where    |
+----+-------------+---------+------+---------------+-------+---------+-------+------+----------------+

示例演示: https://www.db-fiddle.com/f/v9ZJR6UwHqRv36qobkGxsv/1

【问题讨论】:

标签: mysql mysql-python


【解决方案1】:

阐述:

我假设您已将数据加载到名为 widget_url_pairs 的表中。这是一个模型...

USE test;

DROP TABLE IF EXISTS widget_url_pairs;

CREATE TABLE widget_url_pairs
(widget VARCHAR(100) NOT NULL
,url VARCHAR(200) NOT NULL
,PRIMARY KEY(widget,url)
);

INSERT IGNORE INTO widget_url_pairs VALUES
('widget', 'www.example.com/widgets'),
('blue widget', 'www.example.com/blue-widgets'),
('red widget', 'www.example.com/widgets'),
('widget', 'www.example.com/green-widgets'),
('orange widget', 'www.example.com/widgets');

DROP TABLE IF EXISTS widgets;

CREATE TABLE widgets
(widget_id SERIAL PRIMARY KEY
,widget VARCHAR(100) NOT NULL UNIQUE
);

INSERT INTO widgets (widget) SELECT DISTINCT widget FROM widget_url_pairs;

DROP TABLE IF EXISTS urls;

CREATE TABLE urls
(url_id SERIAL PRIMARY KEY
,url VARCHAR(200) NOT NULL UNIQUE
);

INSERT INTO urls(url) SELECT DISTINCT url FROM widget_url_pairs;

DROP TABLE IF EXISTS widget_url;

CREATE TABLE widget_url 
(widget_id INT NOT NULL
,url_id INT NOT NULL
);


INSERT IGNORE INTO widget_url
SELECT DISTINCT w.widget_id
              , u.url_id
           FROM widget_url_pairs wu 
           JOIN widgets w 
             ON w.widget = wu.widget
           JOIN urls u
             ON u.url = wu.url;
             
ALTER TABLE widget_url ADD PRIMARY KEY (widget_id,url_id);

SELECT w.widget
     , u.url 
  FROM widgets w 
  JOIN widget_url wu 
    ON wu.widget_id = w.widget_id 
  JOIN urls u 
    ON u.url_id = wu.url_id;
+---------------+-------------------------------+
| widget        | url                           |
+---------------+-------------------------------+
| blue widget   | www.example.com/blue-widgets  |
| orange widget | www.example.com/widgets       |
| red widget    | www.example.com/widgets       |
| widget        | www.example.com/widgets       |
| widget        | www.example.com/green-widgets |
+---------------+-------------------------------+

【讨论】:

  • www.example.com/widgetswidget 被列出两次,所以它不会是唯一的 id - 如果我正确地跟随
  • 忽略我,我没有完全理解。我今晚会测试这个
  • 好的,经过测试。首先,感谢您的帮助。但是,这实际上比我的版本慢:/
  • 还不错;我想这大约需要 5 分钟,所以如果你的方法更快,那听起来像是赢家
  • 在我自己的机器上测试这个(没什么花哨的)上一百万行的 widget_url_pair 数据集(50 个不同的小部件和 10000 个不同的 url 的交叉乘积),整个操作花了 30 秒。
【解决方案2】:

不清楚你在做什么,但你显然需要

queries: INDEX(query, id)  -- This seems to exist
pages: INDEX(page, id)     -- Definitely missing

但是,由于page 太大而无法编入索引,请考虑将unique_hash 更改为PRIMARY KEY(并完全摆脱当前的id)。

同时,您有一个LIMIT 没有ORDER BY;你在乎你得到什么东西吗?

【讨论】:

  • 我认为页面列对于索引来说太大了
  • @Adders - unique_key 是否为 page/query 提供唯一性测试?
  • 是的,没错,我只是散列查询/页面
猜你喜欢
  • 2023-03-12
  • 1970-01-01
  • 2015-11-02
  • 2012-02-13
  • 2012-11-11
  • 1970-01-01
  • 2018-05-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多