我有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
然后我打开了一个新的标准查询窗口。我刚刚复制了上面的查询。然后用' test'替换@ param1。
这在0,321秒内执行,比存储过程快20倍。
我重复更改了参数值,以防止缓存结果。但这并没有改变结果。 SP比同等标准查询慢约20倍。
请你帮我弄清楚为什么会这样?
有没有人遇到过类似的问题?
我在Windows Server 2008 R2 64位上使用mySQL 5.0.51。
编辑:我正在使用Navicat进行测试。
任何想法对我都有帮助。
EDIT1:
根据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命令窗口调用sp而不是navicat。它给出了相同的结果。我仍然坚持下去。
my 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被组合索引。
你可以说为什么不使用标准查询呢?我的软件设计不合适。我必须使用存储过程。所以这个问题对我来说非常重要。
EDIT2:
我已经获得了查询个人资料信息。差异很大是因为"发送数据行"在SP配置文件信息中。发送数据部分占查询执行时间的%99。我正在本地数据库服务器上进行测试。我没有从远程计算机连接。
SP个人资料信息
查询个人资料信息
我在我的sp中试过了下面的强制索引语句。但结果相同。
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语句。但它坚持不使用索引。任何的想法?我想我即将结束:)
答案 0 :(得分: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存储过程没有太多经验,所以我不确定这是否是正确的语法。
答案 1 :(得分:4)
可能的字符集问题?如果表格字符集与数据库字符集不同,则可能会导致问题。
请参阅此错误报告:http://bugs.mysql.com/bug.php?id=26224
[2007年11月12日21:32] Mark Kubacki 5.1.22_rc仍然没有运气 - 键 在进行中,查询在36秒内和外面进行 0.12S。
[2007年11月12日22:30] Mark Kubacki将字符集更改为UTF-8(特别是使用的两个)后,用于 无论如何连接,在存储的内部考虑密钥 程序!
我无法回答的问题是:为什么优化器会处理字符集 在存储过程内外转换另一种方式? (事实上,我可能会错误地问这个。)
答案 2 :(得分:0)
有趣的问题,因为我喜欢使用存储过程。原因是维护和封装原则。
这是我发现的信息: http://dev.mysql.com/doc/refman/5.1/en/query-cache-operation.html
它声明查询缓存不用于查询 1.是属于外部查询的子查询,和 2.在存储过程,触发器或事件的主体内执行。
这意味着它按设计工作。
答案 3 :(得分:0)
我见过这种行为,但它与字符集无关。
我有一张表,其中包含自我引用的分层数据(有孩子的父母,有些孩子有自己的孩子,等等)。由于parent_id必须引用主id(并且该列指定了该效果的约束),因此我无法将父id设置为NULL或0(零)以将子项与父项取消关联,因此我只是将其引用为本身。
当我去运行存储过程来执行递归查询以查找特定父级的所有子级(在所有级别)时,查询占用了30到20之间。运行时间长40倍。我发现更改存储过程使用的查询以确保它排除了顶级父记录(通过指定WHERE parent_id!= id)恢复了查询的性能。
我正在使用的存储过程基于以下所示的存储过程: https://stackoverflow.com/questions/27013093/recursive-query-emulation-in-mysql