怎么优化大数据量的查询

theoffspring 2010-05-06 10:15:23
正在做一个项目里查询性能优化,对业务不了解,表都定型了,无法修改字段了。
主表A 20多W条数据,内连接了视图B两次,视图B有20多W条数据,然后又左连接了一个40多W条数据的表,总的查询再group by了一下,被by的字段有十几个,select中有一些字段做了一些sum计算,还select了一些其它字段,where有一些动态生成的查询条件,对应一个搜索页面,搜索条件根据用户选择生成,我做索引前查询需要5分钟,做了之后也要100多秒,原来所有的查询是写在一个存储过程来调用的,动态生成的查询条件也写在过程里,调用一个大视图来查询我前面讲的这些,我把查询条件直接写到视图里的话,可以缩短到50多秒,看来还是写进去比较好啊,但再也没办法再缩短了,要命的是,查询条件还有一些是数值比较的,是通过一些字段的计算再与其比较的,如果在where后面写一些字段的运算再比较,无疑效率很低,也用不上索引,有没有比较好的办法再优化一下。写得有点抽象,也不知你们能不能看懂。唉,不太方便把源代码贴上来。
...全文
293 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
wsliang2006 2010-05-08
  • 打赏
  • 举报
回复
考虑到别人理解中文比理解英文更好吗
wsliang2006 2010-05-08
  • 打赏
  • 举报
回复
一个可行的办法,全部建立索引,将你用到的几个表的数据全部筛选到临时表中,然后再sum,再group by
黄_瓜 2010-05-08
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 theoffspring 的回复:]
引用 9 楼 csw200201 的回复:
You might want to consider creating temporary tables (not table variables nor CTE) that materialise intermediate steps between multiple joins.

For example, if you are joining……
[/Quote]
两位的e文太犀利了
Garnett_KG 2010-05-08
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 theoffspring 的回复:]

现在主要问题是需要从一个视图中返回指定范围的数据,这个视图进行分组计算,然后查询这个视图,在where中指定符合的范围,where 字段 between 1 and 200这样。视图可以返回37W条数据,所以卡在这儿了,又不把把条件加到视图里,要能加进去就可以过滤掉很多了,真正符合条件的最后只有2000条。
[/Quote]

where条件下推不到最底层,这是索引有问题,或是你的VIEW写法有问题。

你最好提供完整代码出来。
csw200201 2010-05-08
  • 打赏
  • 举报
回复
I meant the execution plan
csw200201 2010-05-08
  • 打赏
  • 举报
回复
If you post the exeuction query and actual table structure, it would it easier for us to give you suggestions as to what is the best course of action.
Garnett_KG 2010-05-07
  • 打赏
  • 举报
回复
最好先看看各项性能计数器。

确定你的查询是在消耗CPU还是在消耗I/O.对症下药,语法层面的优化效果通常比较小的。
theoffspring 2010-05-07
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 csw200201 的回复:]
You might want to consider creating temporary tables (not table variables nor CTE) that materialise intermediate steps between multiple joins.

For example, if you are joining A -> B -> C, you may w……
[/Quote]
Yeah, I actually did so,first I create two temporary tables who data come from view or joining query,then make a left join query with a big table of 40W rows to do statatisc calculating mainly sum ,but the problem is that the fields groupped by are about ten, the time to take is over 5 minutes ,not effective.Certainly there are some where conditions added ,but due to group by they can't be added after keyword where ,only can be added from outside of the view containing all the sql,that will make a whole scan of table. In fact the matched rows only several thousands more or less,it depends,all in all,won't be a big number.Is there any more ways to figure out? Because we are not the guys who designed the tables and app ,just recevied the project from other company and codes amount is large,almost impossible to redesign the tables.So it's very hard,all I can do is tuing the sql things.
theoffspring 2010-05-07
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 sql77 的回复:]
查询条件还有一些是数值比较的,是通过一些字段的计算再与其比较的,如果在where后面写一些字段的运算再比较,

A-B>10

如果A有索引可以写成A>10+B
如果B有索引写成B<A-10

能这样转换的话也是可以用到的,具体不清楚你的,还

内连接了视图B两次,视图B有20多W条数据,

内联两次?不明白
[/Quote]
是的,关联不同的字段,比如说你出生地是一个城市,现居住地是另一个城市,和城市表关联两次才能查出具体的城市名。
A-B>9这样的我也知道,但有个字段被分组sum了,然后再和一个参数比较。计算字段无法在where中,这比较麻烦
csw200201 2010-05-07
  • 打赏
  • 举报
回复
You might want to consider creating temporary tables (not table variables nor CTE) that materialise intermediate steps between multiple joins.

For example, if you are joining A -> B -> C, you may want to consider outputing A->B result first into a temp table and create indices on the temp table and then join C.

This methodology is quite effective for tables with millions of rows - may be not as effective given the relatively small number of records in each of your tables.
theoffspring 2010-05-07
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 garnett_kg 的回复:]
引用 5 楼 theoffspring 的回复:

性能计数器怎么打开?

我运行一段查cpu占用情况的sql如下,但报错,在家里的机器不报,同样的sql server 2005 sp3:
CREATE PROC spHighestCPUTime
AS
SELECT TOP 50
total_worker_time/1000 AS [总消耗CPU 时间(ms)],execution……
[/Quote]
好了,忘了设置兼容级别了,在数据库属性中可以设置。
Garnett_KG 2010-05-07
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 theoffspring 的回复:]

性能计数器怎么打开?

我运行一段查cpu占用情况的sql如下,但报错,在家里的机器不报,同样的sql server 2005 sp3:
CREATE PROC spHighestCPUTime
AS
SELECT TOP 50
total_worker_time/1000 AS [总消耗CPU 时间(ms)],execution_count [运行次数],
qs.total_wo……
[/Quote]

报错?兼容级别的原因吗?

USE [master]
GO
ALTER DATABASE yourdbname SET COMPATIBILITY_LEVEL = 90
GO

theoffspring 2010-05-07
  • 打赏
  • 举报
回复
现在主要问题是需要从一个视图中返回指定范围的数据,这个视图进行分组计算,然后查询这个视图,在where中指定符合的范围,where 字段 between 1 and 200这样。视图可以返回37W条数据,所以卡在这儿了,又不把把条件加到视图里,要能加进去就可以过滤掉很多了,真正符合条件的最后只有2000条。
theoffspring 2010-05-07
  • 打赏
  • 举报
回复
性能计数器怎么打开?

我运行一段查cpu占用情况的sql如下,但报错,在家里的机器不报,同样的sql server 2005 sp3:
CREATE PROC spHighestCPUTime
AS
SELECT TOP 50
total_worker_time/1000 AS [总消耗CPU 时间(ms)],execution_count [运行次数],
qs.total_worker_time/qs.execution_count/1000. as [平均消耗CPU 时间(ms)],
SUBSTRING(qt.text,qs.statement_start_offset/2+1,
(case when qs.statement_end_offset = -1
then DATALENGTH(qt.text)
else qs.statement_end_offset end -qs.statement_start_offset)/2 + 1)
as [使用CPU的语法], qt.text [完整语法],
qt.dbid, dbname=db_name(qt.dbid),
qt.objectid,object_name(qt.objectid,qt.dbid) ObjectName

FROM sys.dm_exec_query_stats qs
cross apply sys.dm_exec_sql_text(qs.sql_handle) as qt
ORDER BY
total_worker_time DESC

--小F-- 2010-05-06
  • 打赏
  • 举报
回复
如何写出性能优良的SQL
(1) 选择最有效率的表名顺序(只在基于规则的优化器中有效):
ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表(基础表 driving table)将被最先处理,在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表。如果有3个以上的表连接查询, 那就需要选择交叉表(intersection table)作为基础表, 交叉表是指那个被其他表所引用的表.
(2) WHERE子句中的连接顺序.:
ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前, 那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾.
(3) SELECT子句中避免使用 ‘ * ‘:
ORACLE在解析的过程中, 会将'*' 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间
(4) 减少访问数据库的次数:
ORACLE在内部执行了许多工作: 解析SQL语句, 估算索引的利用率, 绑定变量 , 读数据块等;
(5) 在SQL*Plus , SQL*Forms和Pro*C中重新设置ARRAYSIZE参数, 可以增加每次数据库访问的检索数据量 ,建议值为200
(6) 使用DECODE函数来减少处理时间:
使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表.
(7) 整合简单,无关联的数据库访问:
如果你有几个简单的数据库查询语句,你可以把它们整合到一个查询中(即使它们之间没有关系)
(8) 删除重复记录:
最高效的删除重复记录方法 ( 因为使用了ROWID)例子:
DELETE FROM EMP E WHERE E.ROWID > (SELECT MIN(X.ROWID)
FROM EMP X WHERE X.EMP_NO = E.EMP_NO);
(9) 用TRUNCATE替代DELETE:
当删除表中的记录时,在通常情况下, 回滚段(rollback segments ) 用来存放可以被恢复的信息. 如果你没有COMMIT事务,ORACLE会将数据恢复到删除之前的状态(准确地说是恢复到执行删除命令之前的状况) 而当运用TRUNCATE时, 回滚段不再存放任何可被恢复的信息.当命令运行后,数据不能被恢复.因此很少的资源被调用,执行时间也会很短. (译者按: TRUNCATE只在删除全表适用,TRUNCATE是DDL不是DML)
(10) 尽量多使用COMMIT:
只要有可能,在程序中尽量多使用COMMIT, 这样程序的性能得到提高,需求也会因为COMMIT所释放的资源而减少:
COMMIT所释放的资源:
a. 回滚段上用于恢复数据的信息.
b. 被程序语句获得的锁
c. redo log buffer 中的空间
d. ORACLE为管理上述3种资源中的内部花费
(11) 用Where子句替换HAVING子句:
避免使用HAVING子句, HAVING 只会在检索出所有记录之后才对结果集进行过滤. 这个处理需要排序,总计等操作. 如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销. (非oracle中)on、where、having这三个都可以加条件的子句中,on是最先执行,where次之,having最后,因为on是先把不符合条件的记录过滤后才进行统计,它就可以减少中间运算要处理的数据,按理说应该速度是最快的,where也应该比having快点的,因为它过滤数据后才进行sum,在两个表联接时才用on的,所以在一个表的时候,就剩下where跟having比较了。在这单表查询统计的情况下,如果要过滤的条件没有涉及到要计算字段,那它们的结果是一样的,只是where可以使用rushmore技术,而having就不能,在速度上后者要慢如果要涉及到计算的字段,就表示在没计算之前,这个字段的值是不确定的,根据上篇写的工作流程,where的作用时间是在计算之前就完成的,而having就是在计算后才起作用的,所以在这种情况下,两者的结果会不同。在多表联接查询时,on比where更早起作用。系统首先根据各个表之间的联接条件,把多个表合成一个临时表后,再由where进行过滤,然后再计算,计算完后再由having进行过滤。由此可见,要想过滤条件起到正确的作用,首先要明白这个条件应该在什么时候起作用,然后再决定放在那里
(12) 减少对表的查询:
在含有子查询的SQL语句中,要特别注意减少对表的查询.例子:
SELECT TAB_NAME FROM TABLES WHERE (TAB_NAME,DB_VER) = ( SELECT
TAB_NAME,DB_VER FROM TAB_COLUMNS WHERE VERSION = 604)
(13) 通过内部函数提高SQL效率.:
复杂的SQL往往牺牲了执行效率. 能够掌握上面的运用函数解决问题的方法在实际工作中是非常有意义的
(14) 使用表的别名(Alias):
当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上.这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误.
(15) 用EXISTS替代IN、用NOT EXISTS替代NOT IN:
在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接.在这种情况下, 使用EXISTS(或NOT EXISTS)通常将提高查询的效率. 在子查询中,NOT IN子句将执行一个内部的排序和合并. 无论在哪种情况下,NOT IN都是最低效的 (因为它对子查询中的表执行了一个全表遍历). 为了避免使用NOT IN ,我们可以把它改写成外连接(Outer Joins)或NOT EXISTS.
例子:
(高效)SELECT * FROM EMP (基础表) WHERE EMPNO > 0 AND EXISTS (SELECT ‘X' FROM DEPT WHERE DEPT.DEPTNO = EMP.DEPTNO AND LOC = ‘MELB')
(低效)SELECT * FROM EMP (基础表) WHERE EMPNO > 0 AND DEPTNO IN(SELECT DEPTNO FROM DEPT WHERE LOC = ‘MELB')
(16) 识别'低效执行'的SQL语句:
虽然目前各种关于SQL优化的图形化工具层出不穷,但是写出自己的SQL工具来解决问题始终是一个最好的方法:
SELECT EXECUTIONS , DISK_READS, BUFFER_GETS,
ROUND((BUFFER_GETS-DISK_READS)/BUFFER_GETS,2) Hit_radio,
ROUND(DISK_READS/EXECUTIONS,2) Reads_per_run,
SQL_TEXT
FROM V$SQLAREA
WHERE EXECUTIONS>0
AND BUFFER_GETS > 0
AND (BUFFER_GETS-DISK_READS)/BUFFER_GETS < 0.8
ORDER BY 4 DESC;
(17) 用索引提高效率:
索引是表的一个概念部分,用来提高检索数据的效率,ORACLE使用了一个复杂的自平衡B-tree结构. 通常,通过索引查询数据比全表扫描要快. 当ORACLE找出执行查询和Update语句的最佳路径时, ORACLE优化器将使用索引. 同样在联结多个表时使用索引也可以提高效率. 另一个使用索引的好处是,它提供了主键(primary key)的唯一性验证.。那些LONG或LONG RAW数据类型, 你可以索引几乎所有的列. 通常, 在大型表中使用索引特别有效. 当然,你也会发现, 在扫描小表时,使用索引同样能提高效率. 虽然使用索引能得到查询效率的提高,但是我们也必须注意到它的代价. 索引需要空间来存储,也需要定期维护, 每当有记录在表中增减或索引列被修改时, 索引本身也会被修改. 这意味着每条记录的INSERT , DELETE , UPDATE将为此多付出4 , 5 次的磁盘I/O . 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢.。定期的重构索引是有必要的.:
ALTER INDEX <INDEXNAME> REBUILD <TABLESPACENAME>
(18) 用EXISTS替换DISTINCT:
当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT. 一般可以考虑用EXIST替换, EXISTS 使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果. 例子:
(低效):
SELECT DISTINCT DEPT_NO,DEPT_NAME FROM DEPT D , EMP E
WHERE D.DEPT_NO = E.DEPT_NO
(高效):
SELECT DEPT_NO,DEPT_NAME FROM DEPT D WHERE EXISTS ( SELECT ‘X'
FROM EMP E WHERE E.DEPT_NO = D.DEPT_NO);
(19) sql语句用大写的;因为oracle总是先解析sql语句,把小写的字母转换成大写的再执行
(20) 在java代码中尽量少用连接符“+”连接字符串!
(21) 避免在索引列上使用NOT 通常, 
我们要避免在索引列上使用NOT, NOT会产生在和在索引列上使用函数相同的影响. 当ORACLE”遇到”NOT,他就会停止使用索引转而执行全表扫描.
SQL77 2010-05-06
  • 打赏
  • 举报
回复
查询条件还有一些是数值比较的,是通过一些字段的计算再与其比较的,如果在where后面写一些字段的运算再比较,

A-B>10

如果A有索引可以写成A>10+B
如果B有索引写成B<A-10

能这样转换的话也是可以用到的,具体不清楚你的,还

内连接了视图B两次,视图B有20多W条数据,

内联两次?不明白

27,579

社区成员

发帖
与我相关
我的任务
社区描述
MS-SQL Server 应用实例
社区管理员
  • 应用实例社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧