SQL性能优化,很困惑的问题

最帅马老师 2009-12-19 10:32:41
我对SQL了解不多,基本上仅限于使用,但现在不得不面临优化的问题,问题描述如下:

有一个表DbDataTable,有如下几列

id number data test_date

列number有可能出现重复记录,在每次查询时,需要检查number列中的重复记录是几条,现在用的SQL语句如下:

SELECT a.*, b.cnt FROM DbDataTable AS a INNER JOIN (SELECT number,COUNT(*) AS cnt FROM DbDataTable GROUP BY number) b ON a.number=b.number WHERE a.test_date='2009-12-18'

这个表中有近30万条记录,执行这样一次SQL查询现在至少得等1分钟,当查询结果数量超过500条时,这个时间显得太长了。

请问:

如何才能尽量缩短查询时间?
如果能说得详细点最好了,先谢谢了。100分全部相送。
...全文
134 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
luxi0194 2009-12-19
  • 打赏
  • 举报
回复
上面的各位已经说的很详细了,主要是加上合适的索引就可以了,30w的数据并不算多,其一,sql写的不够好,其二,索引不合理会使速度更慢。
最帅马老师 2009-12-19
  • 打赏
  • 举报
回复
非常感谢,问题已解决。
实在本人水平太菜,有劳各位了。
结贴。

jack_ailly 2009-12-19
  • 打赏
  • 举报
回复
number上建非聚集索引
test_date上建聚集索引
nzperfect 2009-12-19
  • 打赏
  • 举报
回复
[Quote=引用楼主 kktemp234 的回复:]
我对SQL了解不多,基本上仅限于使用,但现在不得不面临优化的问题,问题描述如下:

有一个表DbDataTable,有如下几列

id    number  data  test_date

列number有可能出现重复记录,在每次查询时,需要检查number列中的重复记录是几条,现在用的SQL语句如下:

SELECT a.*, b.cnt FROM DbDataTable AS a INNER JOIN (SELECT number,COUNT(*) AS cnt FROM DbDataTable GROUP BY number) b ON a.number=b.number WHERE a.test_date='2009-12-18'

这个表中有近30万条记录,执行这样一次SQL查询现在至少得等1分钟,当查询结果数量超过500条时,这个时间显得太长了。

请问:

如何才能尽量缩短查询时间?
如果能说得详细点最好了,先谢谢了。100分全部相送。
[/Quote]

加索引:
create index ix_test_date_number on DbDataTable(test_date,number)
create index ix_number on DbDataTable(number)
bancxc 2009-12-19
  • 打赏
  • 举报
回复
CREATE CLUSTERED INDEX IDX_test_date ON DbDataTable(test_date)
CREATE INDEX IDX_number ON DbDataTable(number)
建索引执行一次就可以了 和建表一样 他就存在数据库里了
最帅马老师 2009-12-19
  • 打赏
  • 举报
回复
再问下,我是在程序里执行SQL,是不是我每次在执行SQL前都要这样建立索引,还是只要在查询分析器建立一次,程序中直接执行SQL语句就可以了?

另外,建立索引是否可以和SQL语句一样对待,比如,我是否可以这样写:

CREATE CLUSTERED INDEX IDX_test_date ON DbDataTable(test_date)
CREATE INDEX IDX_number ON DbDataTable(number)
SELECT a.*, b.cnt FROM DbDataTable AS a INNER JOIN (SELECT number,COUNT(*) AS cnt FROM DbDataTable GROUP BY number) b ON a.number=b.number WHERE a.test_date='2009-12-18'

是否可以将上面的语句一次性当作一个SQL语句执行?
huangyu_2008 2009-12-19
  • 打赏
  • 举报
回复
谢谢分享。。。
csdyyr 2009-12-19
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 kktemp234 的回复:]
引用 5 楼 syw_java 的回复:
SELECT a.*, b.cnt FROM DbDataTable AS a INNER JOIN (SELECT number,COUNT(*)AS cnt FROM DbDataTable GROUP BY number) b ON a.number=b.number WHERE a.test_date='2009-12-18'
可以在后面这个count(*)的地方优化下,用count(number),给number加一个索引更好


请问能否再问下,如何添加索引?是这样吗?
CREATE CLUSTERED INDEX IDX_test_date ON DbDataTable(test_date)
CREATE INDEX IDX_number ON DbDataTable(number)
但具体怎么做?
我正在看相关资料,但问题有点急,我又确实没有使用经验,请详细指点下
再次感谢
[/Quote]
直接在查詢分析器運行即可。
CREATE CLUSTERED INDEX IDX_test_date ON DbDataTable(test_date) 
CREATE INDEX IDX_number ON DbDataTable(number)
shenzhenNBA 2009-12-19
  • 打赏
  • 举报
回复
xue xi le
bancxc 2009-12-19
  • 打赏
  • 举报
回复
恩 你先按 这个试一下
最帅马老师 2009-12-19
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 syw_java 的回复:]
SELECT a.*, b.cnt FROM DbDataTable AS a INNER JOIN (SELECT number,COUNT(*)AS cnt FROM DbDataTable GROUP BY number) b ON a.number=b.number WHERE a.test_date='2009-12-18'
可以在后面这个count(*)的地方优化下,用count(number),给number加一个索引更好
[/Quote]

请问能否再问下,如何添加索引?是这样吗?
CREATE CLUSTER INDEX IDX_test_date ON DbDataTable(test_date)
CREATE INDEX IDX_number ON DbDataTable(number)
但具体怎么做?
我正在看相关资料,但问题有点急,我又确实没有使用经验,请详细指点下
再次感谢
bancxc 2009-12-19
  • 打赏
  • 举报
回复
执行4楼的语句 建索引
然后执行你的sql 会快很多
bancxc 2009-12-19
  • 打赏
  • 举报
回复
number 上建个索引 这个将会很快呵呵
--小F-- 2009-12-19
  • 打赏
  • 举报
回复
---还发一点参考下  不要嫌多 可以从中学习一些经验

如何写出性能优良的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,他就会停止使用索引转而执行全表扫描.
--小F-- 2009-12-19
  • 打赏
  • 举报
回复
要加快查询速度 一般的方法都是加索引 升级硬件 和优化语句

关于语句优化的帖子很多 这里就不说了

加索引一般在连接字段加 不要使用函数 否则会导致索引失效

升级硬件 不多说
syw_java 2009-12-19
  • 打赏
  • 举报
回复
SELECT a.*, b.cnt FROM DbDataTable AS a INNER JOIN (SELECT number,COUNT(*) AS cnt FROM DbDataTable GROUP BY number) b ON a.number=b.number WHERE a.test_date='2009-12-18'
可以在后面这个count(*)的地方优化下,用count(number),给number加一个索引更好
csdyyr 2009-12-19
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 csdyyr 的回复:]
...
[/Quote]

CREATE CLUSTERED INDEX IDX_test_date ON DbDataTable(test_date)
CREATE INDEX IDX_number ON DbDataTable(number)

csdyyr 2009-12-19
  • 打赏
  • 举报
回复
CREATE CLUSTER INDEX IDX_test_date ON DbDataTable(test_date)
CREATE INDEX IDX_number ON DbDataTable(number)
syw_java 2009-12-19
  • 打赏
  • 举报
回复
学习
--小F-- 2009-12-19
  • 打赏
  • 举报
回复
SQL Server数据库查询速度慢的原因有很多,常见的有以下几种: 

    1、没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)
    2、I/O吞吐量小,形成了瓶颈效应。
    3、没有创建计算列导致查询不优化。
    4、内存不足
    5、网络速度慢
    6、查询出的数据量过大(可以采用多次查询,其他的方法降低数据量)
    7、锁或者死锁(这也是查询慢最常见的问题,是程序设计的缺陷)
    8、sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。
    9、返回了不必要的行和列
    10、查询语句不好,没有优化

  ●可以通过以下方法来优化查询 :

  1、把数据、日志、索引放到不同的I/O设备上,增加读取速度,以前可以将Tempdb应放在RAID0上,SQL2000不在支持。数据量(尺寸)越大,提高I/O越重要。

  2、纵向、横向分割表,减少表的尺寸(sp_spaceuse)

  3、升级硬件

  4、根据查询条件,建立索引,优化索引、优化访问方式,限制结果集的数据量。注意填充因子要适当(最好是使用默认值0)。索引应该尽量小,使用字节数小的列建索引好(参照索引的创建),不要对有限的几个值的字段建单一索引如性别字段。

  5、提高网速。

  6、扩大服务器的内存,Windows 2000和SQL server 2000能支持4-8G的内存。

  配置虚拟内存:虚拟内存大小应基于计算机上并发运行的服务进行配置。运行 Microsoft SQL Server? 2000时,可考虑将虚拟内存大小设置为计算机中安装的物理内存的1.5倍。如果另外安装了全文检索功能,并打算运行Microsoft搜索服务以便执行全文索引和查询,可考虑:将虚拟内存大小配置为至少是计算机中安装的物理内存的3倍。将SQL Server max server memory服务器配置选项配置为物理内存的1.5倍(虚拟内存大小设置的一半)。

  7、增加服务器CPU个数;但是必须 明白并行处理串行处理更需要资源例如内存。使用并行还是串行程是MSSQL自动评估选择的。单个任务分解成多个任务,就可以在处理器上运行。例如耽搁查询 的排序、连接、扫描和GROUP BY字句同时执行,SQL SERVER根据系统的负载情况决定最优的并行等级,复杂的需要消耗大量的CPU的查询最适合并行处理。但是更新操作UPDATE,INSERT, DELETE还不能并行处理。

  8、如果是使用like进行查询的话,简单的使用index是不行的,但是全文索引,耗空间。 like ''a%'' 使用索引 like ''%a'' 不使用索引用 like ''%a%'' 查询时,查询耗时和字段值总长度成正比,所以不能用CHAR类型,而是VARCHAR。对于字段的值很长的建全文索引。

  9、DB Server 和APPLication Server 分离;OLTP和OLAP分离

  10、分布式分区视图可用于实现数据库服务器联合体。

  联合体是一组分开管理的服务器,但它们相互协作分担系统的处理负荷。这种通过分区数据形成数据库服务器联合体的机制能够扩大一组服务器,以支持大型的多层 Web 站点的处理需要。有关更多信息,参见设计联合数据库服务器。(参照SQL帮助文件''分区视图'')

  a、在实现分区视图之前,必须先水平分区表

  b、 在创建成员表后,在每个成员服务器上定义一个分布式分区视图,并且每个视图具有相同的名称。这样,引用分布式分区视图名的查询可以在任何一个成员服务器上 运行。系统操作如同每个成员服务器上都有一个原始表的复本一样,但其实每个服务器上只有一个成员表和一个分布式分区视图。数据的位置对应用程序是透明的。

  11、重建索引 DBCC REINDEX ,DBCC INDEXDEFRAG,收缩数据和日志 DBCC SHRINKDB,DBCC SHRINKFILE. 设置自动收缩日志.对于大的数据库不要设置数据库自动增长,它会降低服务器的性能。

  在T-sql的写法上有很大的讲究,下面列出常见的要点:首先,DBMS处理查询计划的过程是这样的:

    1、 查询语句的词法、语法检查
    2、 将语句提交给DBMS的查询优化器
    3、 优化器做代数优化和存取路径的优化
    4、 由预编译模块生成查询规划
    5、 然后在合适的时间提交给系统处理执行
    6、 最后将执行结果返回给用户。

  其次,看一下SQL SERVER的数据存放的结构:一个页面的大小为8K(8060)字节,8个页面为一个盘区,按照B树存放。

22,209

社区成员

发帖
与我相关
我的任务
社区描述
MS-SQL Server 疑难问题
社区管理员
  • 疑难问题社区
  • 尘觉
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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