Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在MySQL中,执行一个JOIN+1LIKE语句还是两个JOIN更快?_Mysql_Performance_Join_Sql Like - Fatal编程技术网

在MySQL中,执行一个JOIN+1LIKE语句还是两个JOIN更快?

在MySQL中,执行一个JOIN+1LIKE语句还是两个JOIN更快?,mysql,performance,join,sql-like,Mysql,Performance,Join,Sql Like,我必须创建一个cron作业,它本身很简单,但因为它每分钟都在运行,所以我担心性能。我有两个表,一个有用户名,另一个有关于他们网络的详细信息。大多数情况下,一个用户只属于一个网络,但从理论上讲,他们可能属于更多的网络,但即使是很少,可能是两个或三个。因此,为了减少连接的数量,我在用户表的一个字段中保存了由|分隔的网络ID,例如 |1 | 3 | 9| 此问题的简化用户表结构为 TABLE `users` ( `u_id` BIGINT UNSIGNED NOT NULL AUTO_INCREM

我必须创建一个cron作业,它本身很简单,但因为它每分钟都在运行,所以我担心性能。我有两个表,一个有用户名,另一个有关于他们网络的详细信息。大多数情况下,一个用户只属于一个网络,但从理论上讲,他们可能属于更多的网络,但即使是很少,可能是两个或三个。因此,为了减少连接的数量,我在用户表的一个字段中保存了由|分隔的网络ID,例如

|1 | 3 | 9|

此问题的简化用户表结构为

TABLE `users` (
  `u_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
  `userid` VARCHAR(500) NOT NULL UNIQUE,
  `net_ids` VARCHAR(500) NOT NULL DEFAULT '',
  PRIMARY KEY (`u_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `network` (
  `n_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
  `netname` VARCHAR(500) NOT NULL UNIQUE,
  `login_time` DATETIME DEFAULT NULL,
  `timeout_mins` TINYINT UNSIGNED NOT NULL DEFAULT 10,
  PRIMARY KEY (`n_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
也简化了网络表结构

TABLE `users` (
  `u_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
  `userid` VARCHAR(500) NOT NULL UNIQUE,
  `net_ids` VARCHAR(500) NOT NULL DEFAULT '',
  PRIMARY KEY (`u_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `network` (
  `n_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
  `netname` VARCHAR(500) NOT NULL UNIQUE,
  `login_time` DATETIME DEFAULT NULL,
  `timeout_mins` TINYINT UNSIGNED NOT NULL DEFAULT 10,
  PRIMARY KEY (`n_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
我必须在超时时发送警告,我的查询是

SELECT N.netname, N.timeout_mins, N.n_id, U.userid FROM
(SELECT netname, timeout_mins, n_id FROM network
 WHERE is_open = 1 AND notify = 1
 AND TIMESTAMPDIFF(SECOND, TIMESTAMPADD(MINUTE, timeout_mins, login_time), NOW()) < 60) AS N
INNER JOIN users AS U ON U.net_ids LIKE CONCAT('%|', N.n_id, '|%');
此查询:

SELECT N.netname, N.timeout_mins, N.n_id, U.userid FROM
(SELECT netname, timeout_mins, n_id FROM network
 WHERE is_open = 1 AND notify = 1
 AND TIMESTAMPDIFF(SECOND, TIMESTAMPADD(MINUTE, timeout_mins, login_time), NOW()) < 60) AS N
INNER JOIN user_net AS UN ON N.n_id = UN.n_id
INNER JOIN users AS U ON UN.u_id = U.u_id;
您应该为user_net表定义。其中一个可以而且应该是主键

TABLE `user_net` (
    `u_id` BIGINT UNSIGNED NOT NULL,
    `n_id` BIGINT UNSIGNED NOT NULL,
    PRIMARY KEY (`u_id`, `n_id`),
    INDEX `uid_nid` (`n_id`, `u_id`),
    FOREIGN KEY (`u_id`) REFERENCES `users`(`u_id`),
    FOREIGN KEY (`n_id`) REFERENCES `network`(`n_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
我还要将您的查询改写为:

SELECT N.netname, N.timeout_mins, N.n_id, U.userid
FROM network N
INNER JOIN user_net AS UN ON N.n_id = UN.n_id
INNER JOIN users AS U  ON UN.u_id   = U.u_id
WHERE N.is_open = 1 
  AND N.notify = 1
  AND TIMESTAMPDIFF(SECOND, TIMESTAMPADD(MINUTE, N.timeout_mins, N.login_time), NOW()) < 60
SELECT N.netname, N.timeout_mins, N.n_id, U.userid
FROM network N
INNER JOIN user_net AS UN ON N.n_id = UN.n_id
INNER JOIN users AS U  ON UN.u_id   = U.u_id
WHERE N.is_open = 1 
  AND N.notify  = 1
  AND N.timeout_dt < NOW() + INTERVAL 60 SECOND
现在将查询更改为:

SELECT N.netname, N.timeout_mins, N.n_id, U.userid
FROM network N
INNER JOIN user_net AS UN ON N.n_id = UN.n_id
INNER JOIN users AS U  ON UN.u_id   = U.u_id
WHERE N.is_open = 1 
  AND N.notify = 1
  AND TIMESTAMPDIFF(SECOND, TIMESTAMPADD(MINUTE, N.timeout_mins, N.login_time), NOW()) < 60
SELECT N.netname, N.timeout_mins, N.n_id, U.userid
FROM network N
INNER JOIN user_net AS UN ON N.n_id = UN.n_id
INNER JOIN users AS U  ON UN.u_id   = U.u_id
WHERE N.is_open = 1 
  AND N.notify  = 1
  AND N.timeout_dt < NOW() + INTERVAL 60 SECOND


并查看它是否有帮助。

重新格式化以避免在函数中隐藏列。我无法理解你的约会表情,但请注意:

login_time < NOW() - INTERVAL timeout_mins MINUTE
如果这还不够好,让我们看看另一个公式,这样我们可以比较它们

用逗号或|分隔可能是一个非常糟糕的主意


一句话:假设联接不是性能问题,那么根据需要使用尽可能多的联接编写查询。然后让我们优化它。

:-不要这样做。检查执行计划。别猜了。尽管像“%something%”这样的索引速度总是很慢,但无法将索引与这样的字符串一起使用,这意味着将扫描每一行。请不要使用我当前的方法?我意识到这不是一个理想的设计,但我主要关心的是减少查询时间。顺便说一句,如果你的net_id字段包含一个带分隔值的字符串,你实际上有一个严重的终端,就像癌症设计错误一样-你在一个单元格中存储多个值,从而破坏了1NF。使用单独的表格如何检查执行计划?我没有访问phpMyAdmin的权限。我的客户没有给我cPanel访问权限。我一直在使用我自己制作的一个简单表单来运行我必须运行的查询,比如创建表。谢谢!我使用的是4.0版,对此我无法控制。我会尝试其余的,然后回来。如果主键和索引都相同,我需要使用主键和索引吗?我认为主键是自动编入索引的,因为索引中列的顺序很重要。indexa,b与indexb,a不同。对于多对多表,通常两者都需要。顺便说一句:你应该早点告诉我们,你至少使用了10年的旧版本-这也许可以解释加入性能差的问题。哦,我明白了。关于版本,我没有意识到它会影响JOIN的性能。但我认为它应该比使用LIKE的JOIN更快。我仍然得到不一致的运行时:但是你的答案肯定很有帮助,所以我对它投了赞成票。我可能会选择第三个选项,虽然不理想,但比我原来的解决方案要简单得多。有趣的是,我在看到这一点之前几分钟就尝试了:但每次我尝试定义一个包含三列的索引时,它只接受前两列。我想对它进行排序,所以我使用ALTER TABLE network ADD INDEX notify\u list notify DESC,is open DESC,login\u time ASC;,但是,即使我删除了排序顺序,它仍然只接受两列。实际上,我回到了SE以找到解决此问题的方法。:/这是版本4的问题吗?抱歉,没关系,我使用descripe查看表定义,它只是在第一列中指示MUL,索引很好。@inarilo-ASC和DESC在索引声明中被接受,但被忽略。因此,在8.0之前,优化器不会通过混合方向优化订单。我是盲目的-如果你想要更多的讨论,请向我们展示创建表而不是描述和解释。嗨,谢谢你的帮助,但我最终决定取消连接,使用一个包含所有必要值的单独表。在添加网络时,将为每个网络插入子记录以及相关用户名。通知时间随登录和注销而更新。我认为这比重复运行同一个连接查询更有意义。我的cron作业将只从这个表中选择记录,我将在通知时间对该表进行索引。
INDEX(is_open, notify, login_time)