16

除了表名外,我有 10 个具有相同结构的表。

我有一个 sp(存储过程)定义如下:

 select * from table1 where (@param1 IS NULL OR col1=@param1)
 UNION ALL
 select * from table2 where (@param1 IS NULL OR col1=@param1)
 UNION ALL
 ...
 ...
 UNION ALL
 select * from table10 where (@param1 IS NULL OR col1=@param1)

我用以下行调用 sp:

call mySP('test')  //it executes in 6,836s

然后我打开了一个新的标准查询窗口。我刚刚复制了上面的查询。然后将@param1 替换为“测试”。

这在 0,321 秒内执行,比存储过程快约 20 倍。

我反复更改参数值以防止结果被缓存。但这并没有改变结果。SP 比同等标准查询慢约 20 倍。

请你能帮我弄清楚为什么会这样吗?

有没有人遇到过类似的问题?

我在 windows server 2008 R2 64 位上使用 mySQL 5.0.51。

编辑:我正在使用 Navicat 进行测试。

任何想法都会对我有所帮助。

编辑1:

我刚刚根据 Barmar 的回答做了一些测试。

最后,我将 sp 更改为如下所示的一行:

 SELECT * FROM table1 WHERE col1=@param1 AND col2=@param2

然后首先我执行了标准查询

 SELECT * FROM table1 WHERE col1='test' AND col2='test'  //Executed in 0.020s

在我打电话给我的 sp 之后:

 CALL MySp('test','test')    //Executed in 0.466s

所以我完全改变了where子句,但没有任何改变。我从mysql命令窗口而不是navicat调用了sp。它给出了相同的结果。我仍然坚持下去。

我的 sp ddl:

 CREATE DEFINER = `myDbName`@`%`
 PROCEDURE `MySP` (param1 VARCHAR(100), param2 VARCHAR(100))
 BEGIN
    SELECT * FROM table1 WHERE col1=param1 AND col2=param2
 END

并且 col1 和 col2 被组合索引。

你可以说那你为什么不使用标准查询呢?我的软件设计不适合这个。我必须使用存储过程。所以这个问题对我来说非常重要。

编辑2:

我得到了查询配置文件信息。很大的不同是因为 SP Profile Information 中的“发送数据行”。发送数据部分需要 %99 的查询执行时间。我正在本地数据库服务器上进行测试。我没有从远程计算机连接。

SP 配置文件信息 SP 配置文件信息

查询档案信息 在此处输入图像描述

我在我的 sp 中尝试了如下所示的 force index 语句。但同样的结果。

 SELECT * FROM table1 FORCE INDEX (col1_col2_combined_index) WHERE col1=@param1 AND col2=@param2

我已经改变了 sp 如下所示。

 EXPLAIN SELECT * FROM table1 FORCE INDEX (col1_col2_combined_index) WHERE col1=param1 AND col2=param2

这给出了这个结果:

 id:1
 select_type=SIMPLE
 table:table1
 type=ref
 possible_keys:NULL
 key:NULL
 key_len:NULL
 ref:NULL
 rows:292004
 Extra:Using where

然后我执行了下面的查询。

 EXPLAIN SELECT * FROM table1 WHERE col1='test' AND col2='test'

结果是:

 id:1
 select_type=SIMPLE
 table:table1
 type=ref
 possible_keys:col1_co2_combined_index
 key:col1_co2_combined_index
 key_len:76
 ref:const,const
 rows:292004
 Extra:Using where

我在 SP 中使用 FORCE INDEX 语句。但它坚持不使用索引。任何的想法?我想我快结束了:)

4

4 回答 4

9

只是一个猜测:

当您手动运行查询时,WHERE ('test' IS NULL or COL1 = 'test')可以在解析查询时优化表达式。解析器可以看到字符串'test'不为空,因此它将测试转换为WHERE COL1 = 'test'. 如果有一个索引,COL1将被使用。

但是,当您创建存储过程时,会在创建过程时进行解析。那时,它不知道@param会是什么,并且必须将查询实现为对表的顺序扫描。

尝试将您的程序更改为:

IF @param IS NULL
THEN BEGIN
  SELECT * FROM table1
  UNION ALL
  SELECT * FROM table2
  ...
END;
ELSE BEGIN
  SELECT * FROM table1 WHERE col1 = @param
  UNION ALL
  SELECT * FROM table2 WHERE col1 = @param
  ...
END;
END IF;

我对 MySQL 存储过程没有太多经验,所以我不确定这是否是正确的语法。

于 2013-05-25T19:46:42.120 回答
8

可能的字符集问题?如果您的表字符集与数据库字符集不同,这可能会导致问题。

请参阅此错误报告:http ://bugs.mysql.com/bug.php?id=26224

[2007 年 11 月 12 日 21:32] Mark Kubacki 5.1.22_rc 仍然没有运气 - 键被输入,查询在 36 秒内和 0.12 秒外进行。

[2007 年 11 月 12 日 22:30] Mark Kubacki 在将字符集更改为 UTF-8(尤其是使用的两个字符集)之后,无论如何都用于连接,在存储过程中考虑了密钥!

我无法回答的问题是:为什么优化器在存储过程内外以其他方式处理字符集转换?(事实上​​,我问这个可能是错的。)

于 2013-06-05T20:49:22.110 回答
0

有趣的问题,因为我喜欢使用存储过程。原因是维护和封装原则。

这是我找到的信息:http: //dev.mysql.com/doc/refman/5.1/en/query-cache-operation.html

它指出查询缓存不用于 1. 是属于外部查询的子查询,以及 2. 在存储过程、触发器或事件的主体内执行的查询。

这意味着它按设计工作。

于 2013-06-05T20:10:51.967 回答
0

我见过这种行为,但它与字符集无关。

我有一个包含自引用分层数据的表(有孩子的父母,有些孩子有自己的孩子,等等)。由于 parent_id 必须引用主 ID(并且该列指定了该效果的约束),我无法将父 ID 设置为 NULL 或 0(零)以解除子代与父代的关联,因此我只是将其引用为本身。

当我去运行一个存储过程来执行递归查询以查找特定父级的所有子级(在所有级别)时,查询的运行时间在 30 到 40 倍之间。我发现更改存储过程使用的查询以确保它排除顶级父记录(通过指定 WHERE parent_id != id)恢复了查询的性能。

我正在使用的存储过程基于以下内容: https ://stackoverflow.com/questions/27013093/recursive-query-emulation-in-mysql 。

于 2015-01-05T22:04:50.270 回答