sql之left join、right join、inner join的区别

left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录 

(1)LEFT JOIN 它是以第一个表里的栏位为基准 第二个表里没有的会显示空 (2)它是显示全部的 符合条件的

 --from a left join b 的话,则a里面的所有记录都会被显示不论你的记录有没有匹配,结果不是取决于on a.c_user_id=b.c_user_id字段的先后顺序



right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录
inner join(等值连接) 只返回两个表中联结字段相等的行

举例如下:
--------------------------------------------
表A记录如下:
aID     aNum
1     a20050111
2     a20050112
3     a20050113
4     a20050114
5     a20050115

表B记录如下:
bID     bName
1     2006032401
2     2006032402
3     2006032403
4     2006032404
8     2006032408

--------------------------------------------
1.left join
sql语句如下:
select * from A
left join B
on A.aID = B.bID

结果如下:
aID     aNum     bID     bName
1     a20050111    1     2006032401
2     a20050112    2     2006032402
3     a20050113    3     2006032403
4     a20050114    4     2006032404
5     a20050115    NULL     NULL

(所影响的行数为 5 行)
结果说明:
left join是以A表的记录为基础的,A可以看成左表,B可以看成右表,left join是以左表为准的.
换句话说,左表(A)的记录将会全部表示出来,而右表(B)只会显示符合搜索条件的记录(例子中为: A.aID = B.bID).
B表记录不足的地方均为NULL.
--------------------------------------------
2.right join
sql语句如下:
select * from A
right join B
on A.aID = B.bID

结果如下:
aID     aNum     bID     bName
1     a20050111    1     2006032401
2     a20050112    2     2006032402
3     a20050113    3     2006032403
4     a20050114    4     2006032404
NULL     NULL     8     2006032408

(所影响的行数为 5 行)
结果说明:
仔细观察一下,就会发现,和left join的结果刚好相反,这次是以右表(B)为基础的,A表不足的地方用NULL填充.
--------------------------------------------
3.inner join
sql语句如下:
select * from A
innerjoin B
on A.aID = B.bID

结果如下:
aID     aNum     bID     bName
1     a20050111    1     2006032401
2     a20050112    2     2006032402
3     a20050113    3     2006032403
4     a20050114    4     2006032404

结果说明:
很明显,这里只显示出了 A.aID = B.bID的记录.这说明inner join并不以谁为基础,它只显示符合条件的记录.
--------------------------------------------
注:
LEFT JOIN操作用于在任何的 FROM 子句中,组合来源表的记录。使用 LEFT JOIN 运算来创建一个左边外部联接。左边外部联接将包含了从第一个(左边)开始的两个表中的全部记录,即使在第二个(右边)表中并没有相符值的记录。

语法:FROM table1 LEFT JOIN table2 ON table1.field1 compopr table2.field2

说明:table1, table2参数用于指定要将记录组合的表的名称。
field1, field2参数指定被联接的字段的名称。且这些字段必须有相同的数据类型及包含相同类型的数据,但它们不需要有相同的名称。
compopr参数指定关系比较运算符:"=", "<", ">", "<=", ">=" 或 "<>"。
如果在INNER JOIN操作中要联接包含Memo 数据类型或 OLE Object 数据类型数据的字段,将会发生错误. 

http://www.cnblogs.com/pcjim/articles/799302.html



Oracle left join 和 where 条件应用时的效率研究

今天写了个SQL语句,但是速度总是很慢,后来做些改动速度提升了几倍,记录下来心得,一起分享讨论!

如:(1) select * from A   left join B  on A.b = B.b  left join C  on A.c = C.c  where 1=1 and A.d = 'XXXXX' and A.e = 'SSSSS'    

和   (2) select * from (select * from A   left join B  on A.b = B.b  left join C  on A.c = C.c ) aa where aa.d = 'XXXXX' and aa.e = 'SSSSS'   

两条SQL执行结果一样但是效率却截然不同,个人原因分析如下:

1、(1)语句应该是表A每left join 一次就会where 条件过滤一次,这样执行两次where过滤

     (2)语句是在所有left join 之后统一用where 条件过滤的,这样执行一次where 过滤    设想:加入A做为主表当与A表关联的表特别多时,没关联一次就where 过滤一次

这样无疑速度会慢,相比(2)无论关联多少表只where 条件过滤执行一次效率相对要高一些。

引申:   将条件写到on和where 的区别,当然是on较快,因为SQL执行顺序是 on -where 而表连接是先on过滤然后过滤后的值再做笛卡尔积,这样笛卡尔积的数值不至于较大,而条件写到where的时候无疑是两张较大的表的笛卡尔积,然后才where 条件。这样就顺序就是left join - on而不是先on 再left join 了,当然各有千秋,只是个人想法,备忘。 

Oracle数据库 Left Join 使用之我见  

2010-07-08 09:35:11|  分类:Oracle |  标签:|字号 订阅

在Oracle 9i数据库中使用LEFT JOIN这种连表查询方式的效率是极为低下的。

在项目中使用了这么一条语句:

select tmp2.company_name,sum(tmp2.radio_send_bytes + tmp2.radio_recv_bytes)/(1024*1024*1024) as flow
from
(select *
from
(select * from ap_pm_up a
where a.begin_time >= to_date('2010-06-01 00:00:00','yyyy-mm-dd hh24:mi:ss')
and a.begin_time <= to_date('2010-06-30 23:23:59','yyyy-mm-dd hh24:mi:ss')
) tmp
left join ap_info ap on ap.ap_id=tmp.ap_id
left join company c on c.company_id= ap.company_id
left join hot h on h.hp_id=ap.hp_id
left join hot_type ht on ht.hot_type_id=h.hot_type_id

where ht.hot_type_name='南宁') tmp2
group by tmp2.company_name

此语句在plSql中执行10分钟都没有结果,其中ap_pm_up 表里数据2000多万条。

而把语句中的所有LEFT JOIN去掉,换成子查询的话,语句如下:

select tmp.company_name,
(sum(tmp.radio_recv_bytes + tmp.radio_send_bytes)/(1024*1024*1024)) as flow
from
(
select (select c.company_name
          from company c
         where c.company_id in
               (select ai.company_id from ap_info ai where ai.ap_id = a.ap_id)) as company_name,
       a.radio_recv_bytes,
       a.radio_send_bytes
  from ap_pm_up a
 where a.begin_time >=
       to_date('2010-06-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')
   and a.begin_time <=
       to_date('2010-06-30 23:23:59', 'yyyy-mm-dd hh24:mi:ss')
   and a.ap_id in
       (select ap.ap_id
          from ap_info ap
         where ap.hp_id in
               (select h.hp_id
                  from hot h
                 where h.hot_type_id in
                       (select ht.hot_type_id
                          from hot_type ht
                         where ht.hot_type_name = '学校')))
                         ) tmp
                         group by tmp.company_name

这条语句执行仅用了17.4秒就得出了结果。

第一条语句在SQL Server中使用是没有问题的,但在Oracle 9i中使用,效率就很低了,所以我们在Oracle中尽量避免使用Left Join等关键字,虽然这样看起来比较直观。

http://lwbpeter.blog.163.com/blog/static/3850821120106893511821/

MYSQl left join 联合查询效率分析

发布时间:April 23, 2007 分类:MySQL

《DeDeCMS V4.0采集完成后不能导入BUG的解决》

《jQuery中文入门指南,实例,起点教程》

user表:

id | name
---------
1 | libk
2 | zyfon
3 | daodao

user_action表:

user_id | action
---------------
1 | jump
1 | kick
1 | jump
2 | run
4 | swim

sql:
select id, name, action from user as u
left join user_action a on u.id = a.user_id

result:
id | name | action
--------------------------------
1 | libk | jump ①
1 | libk | kick ②
1 | libk | jump ③
2 | zyfon | run ④
3 | daodao | null ⑤

分析:
注意到user_action中还有一个user_id=4, action=swim的纪录,但是没有在结果中出现,
而user表中的id=3, name=daodao的用户在user_action中没有相应的纪录,但是却出现在了结果集中
因为现在是left join,所有的工作以left为准.
结果1,2,3,4都是既在左表又在右表的纪录,5是只在左表,不在右表的纪录


结论:
我们可以想象left join 是这样工作的
从左表读出一条,选出所有与on匹配的右表纪录(n条)进行连接,形成n条纪录(包括重复的行,如:结果1和结果3),
如果右边没有与on条件匹配的表,那连接的字段都是null.
然后继续读下一条。

引申:
我们可以用右表没有on匹配则显示null的规律, 来找出所有在左表,不在右表的纪录, 注意用来判断的那列必须声明为not null的。
如:
sql:
select id, name, action from user as u
left join user_action a on u.id = a.user_id
where a.user_id is NULL
(注意:1.列值为null应该用is null 而不能用=NULL
2.这里a.user_id 列必须声明为 NOT NULL 的)
result:
id | name | action
--------------------------
3 | daodao | NULL

--------------------------------------------------------------------------------

Tips:
1. on a.c1 = b.c1 等同于 using(c1)
2. INNER JOIN 和 , (逗号) 在语义上是等同的
3. 当 MySQL 在从一个表中检索信息时,你可以提示它选择了哪一个索引。
如果 EXPLAIN 显示 MySQL 使用了可能的索引列表中错误的索引,这个特性将是很有用的。
通过指定 USE INDEX (key_list),你可以告诉 MySQL 使用可能的索引中最合适的一个索引在表中查找记录行。
可选的二选一句法 IGNORE INDEX (key_list) 可被用于告诉 MySQL 不使用特定的索引。
4. 一些例子:
mysql> SELECT * FROM table1,table2 WHERE table1.id=table2.id;
mysql> SELECT * FROM table1 LEFT JOIN table2 ON table1.id=table2.id;
mysql> SELECT * FROM table1 LEFT JOIN table2 USING (id);
mysql> SELECT * FROM table1 LEFT JOIN table2 ON table1.id=table2.id
-> LEFT JOIN table3 ON table2.id=table3.id;
mysql> SELECT * FROM table1 USE INDEX (key1,key2)
-> WHERE key1=1 AND key2=2 AND key3=3;
mysql> SELECT * FROM table1 IGNORE INDEX (key3)
-> WHERE key1=1 AND key2=2 AND key3=3;

7.2.9. MySQL如何优化LEFT JOIN和RIGHT JOIN
在MySQL中,A LEFT JOIN B join_condition执行过程如下:

· 根据表A和A依赖的所有表设置表B。

· 根据LEFT JOIN条件中使用的所有表(除了B)设置表A。

· LEFT JOIN条件用于确定如何从表B搜索行。(换句话说,不使用WHERE子句中的任何条件)。

· 可以对所有标准联接进行优化,只是只有从它所依赖的所有表读取的表例外。如果出现循环依赖关系,MySQL提示出现一个错误。

· 进行所有标准WHERE优化。

· 如果A中有一行匹配WHERE子句,但B中没有一行匹配ON条件,则生成另一个B行,其中所有列设置为NULL。

· 如果使用LEFT JOIN找出在某些表中不存在的行,并且进行了下面的测试:WHERE部分的col_name IS NULL,其中col_name是一个声明为 NOT NULL的列,MySQL找到匹配LEFT JOIN条件的一个行后停止(为具体的关键字组合)搜索其它行。

RIGHT JOIN的执行类似LEFT JOIN,只是表的角色反过来。

联接优化器计算表应联接的顺序。LEFT JOIN和STRAIGHT_JOIN强制的表读顺序可以帮助联接优化器更快地工作,因为检查的表交换更少。请注意这说明如果执行下面类型的查询,MySQL进行全扫描b,因为LEFT JOIN强制它在d之前读取:

SELECT *
FROM a,b LEFT JOIN c ON (c.key=a.key) LEFT JOIN d ON (d.key=a.key)
WHERE b.key=d.key;
在这种情况下修复时用a的相反顺序,b列于FROM子句中:

SELECT *
FROM b,a LEFT JOIN c ON (c.key=a.key) LEFT JOIN d ON (d.key=a.key)
WHERE b.key=d.key;
MySQL可以进行下面的LEFT JOIN优化:如果对于产生的NULL行,WHERE条件总为假,LEFT JOIN变为普通联接。

例如,在下面的查询中如果t2.column1为NULL,WHERE 子句将为false:

SELECT * FROM t1 LEFT JOIN t2 ON (column1) WHERE t2.column2=5;
因此,可以安全地将查询转换为普通联接:

SELECT * FROM t1, t2 WHERE t2.column2=5 AND t1.column1=t2.column1;
这样可以更快,因为如果可以使查询更佳,MySQL可以在表t1之前使用表t2。为了强制使用表顺序,使用STRAIGHT_JOIN。

Tags: none

http://www.ccvita.com/90.html

求助:关于left join ,group by 及子查询的效率问题

一:from子句中过滤数据后left join 跟 先left join后过滤数据的执行效率比较;
分别举例如下:

test1:

select t1.emp_no,t1.emp_name,t2.dep_no,t2.dep_name 
from (
     select t.emp_no,t.emp_name,t.dep_no 
     from employee 
     where t.emp_no < 80707999
) t1
left join department t2 on t1.dep_no = t2.dep_no

test2:

select t1.emp_no,t1.emp_name,t2.dep_no,t2.dep_name 
from employee t1
left join department t2 on t1.dep_no = t2.dep_no
where t1.emp_no < 80707999

二:from子句中先Group by 分组后 left join 跟 先leftjoin 后group by 的执行效率比较;分别举例如下:

test1:

select t2.dep_no,t2.dep_name,t1,empCount,t2.empAvgAge
from(
     select t.dep_no, count(t.emp_no) as empCount, avg(t.emp_age) empAvgAge,
     from employee t
     where t.emp_no < 80707999
     group by t.dep_no
)t1
left join department t2 on t1.dep_no=t2.dep_no

test2:

select t2.dep_no,t2.dep_name,count(t1.emp_no) as empCount, avg(t1.emp_age) empAvgAge
from  employee t1
left join department t2 on t1.dep_no=t2.dep_no
where t1.emp_no < 80707999
group by t2.dep_no,t2.dep_name

    请问一、二中的两句sql分别哪一句效率高??

    因本人没有现成的大量数据进行测试且自信心不足,请高手们根据经验给与指点,谢谢!
收藏 分享 评分
回复 引用

订阅 TOP

要看数据的分布情况、比对的量多少、机器的效能、资源是否充足来决定是否走nested loop join还是hash join,变量太多了。除非你能把情况都说出来,不然只有神能回答了
个人是觉的,上线后,每个星期看一下执行计划,然后数据量较稳定时,再进行调整
回复 引用

TOP

 
我来回答第二个问题,我想说得是group by 语句放在子查询里面和外面产生的结果会一样的!!



Oracle left join 和 where 条件应用时的效率研究

今天写了个SQL语句,但是速度总是很慢,后来做些改动速度提升了几倍,记录下来心得,一起分享讨论!

如:(1) select * from A   left join B  on A.b = B.b  left join C  on A.c = C.c  where 1=1 and A.d = 'XXXXX' and A.e = 'SSSSS'    

和   (2) select * from (select * from A   left join B  on A.b = B.b  left join C  on A.c = C.c ) aa where aa.d = 'XXXXX' and aa.e = 'SSSSS'   

两条SQL执行结果一样但是效率却截然不同,个人原因分析如下:

1、(1)语句应该是表A每left join 一次就会where 条件过滤一次,这样执行两次where过滤

     (2)语句是在所有left join 之后统一用where 条件过滤的,这样执行一次where 过滤    设想:加入A做为主表当与A表关联的表特别多时,没关联一次就where 过滤一次

这样无疑速度会慢,相比(2)无论关联多少表只where 条件过滤执行一次效率相对要高一些。

引申:   将条件写到on和where 的区别,当然是on较快,因为SQL执行顺序是 on -where 而表连接是先on过滤然后过滤后的值再做笛卡尔积,这样笛卡尔积的数值不至于较大,而条件写到where的时候无疑是两张较大的表的笛卡尔积,然后才where 条件。这样就顺序就是left join - on而不是先on 再left join 了,当然各有千秋,只是个人想法,备忘。 


【原创】oracle中大数据量join操作的试验

通过关联订购关系这个操作做了一个关于join操作的试验。

以前采用上下行表直接关联,2个表数据量大约是2200w左右和1400w左右,并且2个表都是属于宽表,字段内容多,占用空间大,但join的时候用到的字段很少(2个左右),因此很多内存都耗在了存储不必要的字段值。每次关联操作耗时在2个小时以上。

通过细化join相关表后,首先减少了单表的数据元数目,并且只在细化表中只存储了join操作必须的字段。因此对订购关系这张表来说,数据量减少了70%以上。

  由于sga得到了充分利用,因此join的效率也数10倍的得到了提升。

下面是采用细化分表后在join的详细实施过程:

减少join表的数据量后的详细操作记录:

#Elapsed: (ORDER)00:02:25.63   Elapsed: 00:00:48.45    13916602
create table test.tmp_user_info
as
select user_num,service_id,SUBCHANNEL_CODE SUB_CHANNEL_CODE,CHANNEL_CODE,REG_MODE,UNREG_MODE,ENABLE_STATUS,
  FIRST_REG_TIME,LAST_REG_TIME,LAST_UNREG_TIME from mqq.t_wx_user_info2 where (last_unreg_time like '2007-02-05 %' and enable_status='N') or enable_status='Y'


#Elapsed: 00:06:38.53 22674664
create table test.tmp_oicall_info_20070206
tablespace tbs_sub
as
select t1.MISC_MSG_ID,
  t1.MSG_ID,
  t1.MSG_TYPE,
  t1.FEE_TYPE,
  t1.FEE_VALUE,
  t1.SEND_ADDRESS,
  t1.RECV_ADDRESS,
  t1.FEE_ADDRESS,
  t1.DOWN_STATION,
  t1.OUTTER_ID,
  t1.SERVICE_ID,
  t1.OSS_RECV_CODE,
  t1.MT_TYPE,
  t1.MT_CONTENT,
  t1.MT_TIME,
  t1.CARRY_MSG,
  t1.CARRY_ID,
  t1.RPT_FLAG,
  t1.RPT_STATE,
  t1.GATEWAY_RPT_STATE,
  t1.link_id,
  t1.err_detail from mqqflow.t_mt_sms_200702 partition(p_mt_sms_20070206) t1
#Elapsed: 00:05:07.04


#Elapsed: 00:04:29.64 Elapsed: 00:04:34.68  (说明前面create table的时候,不需要order by)
create table test.tmp_sms_mt_info
as
select t1.user_num,t1.service_id,SUB_CHANNEL_CODE,t1.channel_code channel_code,REG_MODE,UNREG_MODE,ENABLE_STATUS,
  FIRST_REG_TIME,LAST_REG_TIME,LAST_UNREG_TIME
  from  test.tmp_user_info_2 t1,test.tmp_oicall_info t2 where t1.service_id=t2.service_id and t1.user_num=t2.recv_address
CREATE INDEX IDX_tmp_sms_mt_info ON test.tmp_sms_mt_info
(USER_NUM, service_id)
NOLOGGING
TABLESPACE TBS_SUB
PCTFREE    10
INITRANS   2
MAXTRANS   255
STORAGE    (
            INITIAL          64K
            MINEXTENTS       1
            MAXEXTENTS       2147483645
            PCTINCREASE      0
            BUFFER_POOL      DEFAULT
           )
NOPARALLEL;
///
select count(*) from mqqflow.t_sso_order_2007 partition(p_sso_order_200702) where stat_date=to_date('2007-02-07', 'YYYY-MM-DD')  2044 行
///
create table tmp_mt_sms
TABLESPACE TBS_STAT
PCTUSED    0
PCTFREE    10
INITRANS   1
MAXTRANS   255
STORAGE    (
            INITIAL          64K
            MINEXTENTS       1
            MAXEXTENTS       2147483645
            PCTINCREASE      0
            BUFFER_POOL      DEFAULT
           )
LOGGING
NOCOMPRESS
NOCACHE
NOPARALLEL
MONITORING
as
select
  t1.MISC_MSG_ID,
  t1.MSG_ID,
  t1.MSG_TYPE,
  t1.FEE_TYPE,
  t1.FEE_VALUE,
  t1.SEND_ADDRESS,
  t1.RECV_ADDRESS,
  t1.FEE_ADDRESS,
  t1.DOWN_STATION,
  t1.OUTTER_ID,
  t1.SERVICE_ID,
  t1.OSS_RECV_CODE,
  t1.MT_TYPE,
  t1.MT_CONTENT,
  t1.MT_TIME,
  t1.CARRY_MSG,
  t1.CARRY_ID,
  t2.channel_code,
  t2.SUB_CHANNEL_CODE,
  t2.user_num,
  t2.REG_MODE,
  t2.UNREG_MODE,
  t2.ENABLE_STATUS,
  t2.FIRST_REG_TIME,
  t2.LAST_REG_TIME,
  t2.LAST_UNREG_TIME,
  t1.RPT_FLAG,
  t1.RPT_STATE,
  t1.GATEWAY_RPT_STATE,
  t1.link_id,
  t1.err_detail
  from  mqqflow.t_mt_sms_200702 partition(p_mt_sms_20070207)  t1
  left join
  test.tmp_sms_mt_info t2
  on t2.user_num = t1.recv_address
  and t1.service_id = t2.service_id

# Elapsed: 00:13:05.74
create table test. tmp_mt_sms
tablespace tbs_sub
as
  select
  t1.MISC_MSG_ID,
  t1.MSG_ID,
  t1.MSG_TYPE,
  t1.FEE_TYPE,
  t1.FEE_VALUE,
  t1.SEND_ADDRESS,
  t1.RECV_ADDRESS,
  t1.FEE_ADDRESS,
  t1.DOWN_STATION,
  t1.OUTTER_ID,
  t1.SERVICE_ID,
  t1.OSS_RECV_CODE,
  t1.MT_TYPE,
  t1.MT_CONTENT,
  t1.MT_TIME,
  t1.CARRY_MSG,
  t1.CARRY_ID,
  decode(t3.link_id,null,t2.channel_code,'WEB') channel_code,
  t2.SUB_CHANNEL_CODE,
  t2.REG_MODE,
  t2.UNREG_MODE,
  t2.ENABLE_STATUS,
  t2.FIRST_REG_TIME,
  t2.LAST_REG_TIME,
  t2.LAST_UNREG_TIME,
  t1.RPT_FLAG,
  t1.RPT_STATE,
  t1.GATEWAY_RPT_STATE,
  t1.link_id,
  t1.err_detail
  from test.tmp_oicall_info_20070206 t1
  left join test.tmp_sso_mt
  t3
  on t3.stat_date=to_date('2007-02-06', 'YYYY-MM-DD')
  and t3.link_id = t1.link_id
  left join
  test.tmp_user_info t2
  on t2.user_num = t1.RECV_ADDRESS
  and t1.service_id = t2.service_id



下面是例子分析
表A记录如下: 
aID aNum 
1 a20050111 
2 a20050112 
3 a20050113 
4 a20050114 
5 a20050115 


表B记录如下: 
bID bName 
1 2006032401 
2 2006032402 
3 2006032403 
4 2006032404 
8 2006032408 


创建这两个表SQL语句如下: 
CREATE TABLE a 
aID int( 1 ) AUTO_INCREMENT PRIMARY KEY , 
aNum char( 20 ) 

CREATE TABLE b( 
bID int( 1 ) NOT NULL AUTO_INCREMENT PRIMARY KEY , 
bName char( 20 ) 



INSERT INTO a 
VALUES ( 1, 'a20050111' ) , ( 2, 'a20050112' ) , ( 3, 'a20050113' ) , ( 4, 'a20050114' ) , ( 5, 'a20050115' ) ; 


INSERT INTO b 
VALUES ( 1, ' 2006032401' ) , ( 2, '2006032402' ) , ( 3, '2006032403' ) , ( 4, '2006032404' ) , ( 8, '2006032408' ) ; 


实验如下: 
1.left join(左联接) 


sql语句如下: 
SELECT * FROM a 
LEFT JOIN b 
ON a.aID =b.bID 


结果如下: 
aID aNum bID bName 
1 a20050111 1 2006032401 
2 a20050112 2 2006032402 
3 a20050113 3 2006032403 
4 a20050114 4 2006032404 
5 a20050115 NULL NULL 
(所影响的行数为 5 行) 


结果说明: 
left join是以A表的记录为基础的,A可以看成左表,B可以看成右表,left join是以左表为准的. 
换句话说,左表(A)的记录将会全部表示出来,而右表(B)只会显示符合搜索条件的记录(例子中为: A.aID = B.bID). 
B表记录不足的地方均为NULL. 


2.right join(右联接) 


sql语句如下: 
SELECT * FROM a 
RIGHT JOING b 
ON a.aID = b.bID 


结果如下: 
aID aNum bID bName 
1 a20050111 1 2006032401 
2 a20050112 2 2006032402 
3 a20050113 3 2006032403 
4 a20050114 4 2006032404 
NULL NULL 8 2006032408 
(所影响的行数为 5 行) 


结果说明: 
仔细观察一下,就会发现,和left join的结果刚好相反,这次是以右表(B)为基础的,A表不足的地方用NULL填充. 


3.inner join(相等联接或内联接) 


sql语句如下: 
SELECT * FROM a 
INNER JOIN b 
ON a.aID =b.bID 


等同于以下SQL句: 
SELECT * 
FROM a,b 
WHERE a.aID = b.bID 


结果如下: 
aID aNum bID bName 
1 a20050111 1 2006032401 
2 a20050112 2 2006032402 
3 a20050113 3 2006032403 
4 a20050114 4 2006032404 


结果说明: 
很明显,这里只显示出了 A.aID = B.bID的记录.这说明inner join并不以谁为基础,它只显示符合条件的记录. 
LEFT JOIN操作用于在任何的 FROM 子句中, 


组合来源表的记录。使用 LEFT JOIN 运算来创建一个左边外部联接。左边外部联接将包含了从第一个(左边)开始的两个表中的全部记录,即 
使在第二个(右边)表中并没有相符值的记录。 


语法:FROM table1 LEFT JOIN table2 ON table1.field1 compopr table2.field2 
说明:table1, table2参数用于指定要将记录组合的表的名称。 
field1, field2参数指定被联接的字段的名称。且这些字段必须有相同的数据类型及包含相同类型的数据,但它们不需要有相同的 
名称。 
compopr参数指定关系比较运算符:"=", "<", ">", "<=", ">=" 或 "<>"。 
如果在INNER JOIN操作中要联接包含Memo 数据类型或 OLE Object 数据类型数据的字段,将会发生错误。 
详细出处参考:http://www.jb51.net/article/15386.htm


sql的left join 命令详解
给个通俗的解释吧.
例表a
aid adate
1 a1
2 a2
3 a3
表b
bid bdate
1 b1
2 b2
4 b4
两个表a,b相连接,要取出id相同的字段
select * from a inner join b on a.aid = b.bid这是仅取出匹配的数据.
此时的取出的是:
1 a1 b1
2 a2 b2
那么left join 指:
select * from a left join b on a.aid = b.bid
首先取出a表中所有数据,然后再加上与a,b匹配的的数据
此时的取出的是:
1 a1 b1
2 a2 b2
3 a3 空字符
同样的也有right join
指的是首先取出b表中所有数据,然后再加上与a,b匹配的的数据
此时的取出的是:
1 a1 b1
2 a2 b2
4 空字符 b4

LEFT JOIN 或 LEFT OUTER JOIN。
左向外联接的结果集包括 LEFT OUTER 子句中指定的左表的所有行,而不仅仅是联接列所匹配的行。如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表列均为空值。 




SQL LEFT JOIN 关键字

LEFT JOIN 关键字会从左表 (table_name1) 那里返回所有的行,即使在右表 (table_name2) 中没有匹配的行。

LEFT JOIN 关键字语法

SELECT column_name(s)
FROM table_name1
LEFT JOIN table_name2 
ON table_name1.column_name=table_name2.column_name

注释:在某些数据库中, LEFT JOIN 称为 LEFT OUTER JOIN。

原始的表 (用在例子中的):

"Persons" 表:

Id_P LastName FirstName Address City
1 Adams John Oxford Street London
2 Bush George Fifth Avenue New York
3 Carter Thomas Changan Street Beijing

"Orders" 表:

Id_O OrderNo Id_P
1 77895 3
2 44678 3
3 22456 1
4 24562 1
5 34764 65

左连接(LEFT JOIN)实例

现在,我们希望列出所有的人,以及他们的定购 - 如果有的话。

您可以使用下面的 SELECT 语句:

SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo
FROM Persons
LEFT JOIN Orders
ON Persons.Id_P=Orders.Id_P
ORDER BY Persons.LastName

结果集:

LastName FirstName OrderNo
Adams John 22456
Adams John 24562
Carter Thomas 77895
Carter Thomas 44678
Bush George  

LEFT JOIN 关键字会从左表 (Persons) 那里返回所有的行,即使在右表 (Orders) 中没有匹配的行。

http://www.w3school.com.cn/sql/sql_join_left.asp



SQL RIGHT JOIN 关键字

RIGHT JOIN 关键字会右表 (table_name2) 那里返回所有的行,即使在左表 (table_name1) 中没有匹配的行。

RIGHT JOIN 关键字语法

SELECT column_name(s)
FROM table_name1
RIGHT JOIN table_name2 
ON table_name1.column_name=table_name2.column_name

注释:在某些数据库中, RIGHT JOIN 称为 RIGHT OUTER JOIN。

原始的表 (用在例子中的):

"Persons" 表:

Id_P LastName FirstName Address City
1 Adams John Oxford Street London
2 Bush George Fifth Avenue New York
3 Carter Thomas Changan Street Beijing

"Orders" 表:

Id_O OrderNo Id_P
1 77895 3
2 44678 3
3 22456 1
4 24562 1
5 34764 65

右连接(RIGHT JOIN)实例

现在,我们希望列出所有的定单,以及定购它们的人 - 如果有的话。

您可以使用下面的 SELECT 语句:

SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo
FROM Persons
RIGHT JOIN Orders
ON Persons.Id_P=Orders.Id_P
ORDER BY Persons.LastName

结果集:

LastName FirstName OrderNo
Adams John 22456
Adams John 24562
Carter Thomas 77895
Carter Thomas 44678
    34764

RIGHT JOIN 关键字会从右表 (Orders) 那里返回所有的行,即使在左表 (Persons) 中没有匹配的行。

SQL FULL JOIN 关键字

只要其中某个表存在匹配,FULL JOIN 关键字就会返回行。

FULL JOIN 关键字语法

SELECT column_name(s)
FROM table_name1
FULL JOIN table_name2 
ON table_name1.column_name=table_name2.column_name

注释:在某些数据库中, FULL JOIN 称为 FULL OUTER JOIN。

原始的表 (用在例子中的):

"Persons" 表:

Id_P LastName FirstName Address City
1 Adams John Oxford Street London
2 Bush George Fifth Avenue New York
3 Carter Thomas Changan Street Beijing

"Orders" 表:

Id_O OrderNo Id_P
1 77895 3
2 44678 3
3 22456 1
4 24562 1
5 34764 65

全连接(FULL JOIN)实例

现在,我们希望列出所有的人,以及他们的定单,以及所有的定单,以及定购它们的人。

您可以使用下面的 SELECT 语句:

SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo
FROM Persons
FULL JOIN Orders
ON Persons.Id_P=Orders.Id_P
ORDER BY Persons.LastName

结果集:

LastName FirstName OrderNo
Adams John 22456
Adams John 24562
Carter Thomas 77895
Carter Thomas 44678
Bush George  
    34764

FULL JOIN 关键字会从左表 (Persons) 和右表 (Orders) 那里返回所有的行。如果 "Persons" 中的行在表 "Orders" 中没有匹配,或者如果 "Orders" 中的行在表 "Persons" 中没有匹配,这些行同样会列出。








通过关联订购关系这个操作做了一个关于join操作的试验。

以前采用上下行表直接关联,2个表数据量大约是2200w左右和1400w左右,并且2个表都是属于宽表,字段内容多,占用空间大,但join的时候用到的字段很少(2个左右),因此很多内存都耗在了存储不必要的字段值。每次关联操作耗时在2个小时以上。

通过细化join相关表后,首先减少了单表的数据元数目,并且只在细化表中只存储了join操作必须的字段。因此对订购关系这张表来说,数据量减少了70%以上。

  由于sga得到了充分利用,因此join的效率也数10倍的得到了提升。

下面是采用细化分表后在join的详细实施过程:

减少join表的数据量后的详细操作记录:

#Elapsed: (ORDER)00:02:25.63   Elapsed: 00:00:48.45    13916602
create table test.tmp_user_info
as
select user_num,service_id,SUBCHANNEL_CODE SUB_CHANNEL_CODE,CHANNEL_CODE,REG_MODE,UNREG_MODE,ENABLE_STATUS,
  FIRST_REG_TIME,LAST_REG_TIME,LAST_UNREG_TIME from mqq.t_wx_user_info2 where (last_unreg_time like '2007-02-05 %' and enable_status='N') or enable_status='Y'


#Elapsed: 00:06:38.53 22674664
create table test.tmp_oicall_info_20070206
tablespace tbs_sub
as
select t1.MISC_MSG_ID,
  t1.MSG_ID,
  t1.MSG_TYPE,
  t1.FEE_TYPE,
  t1.FEE_VALUE,
  t1.SEND_ADDRESS,
  t1.RECV_ADDRESS,
  t1.FEE_ADDRESS,
  t1.DOWN_STATION,
  t1.OUTTER_ID,
  t1.SERVICE_ID,
  t1.OSS_RECV_CODE,
  t1.MT_TYPE,
  t1.MT_CONTENT,
  t1.MT_TIME,
  t1.CARRY_MSG,
  t1.CARRY_ID,
  t1.RPT_FLAG,
  t1.RPT_STATE,
  t1.GATEWAY_RPT_STATE,
  t1.link_id,
  t1.err_detail from mqqflow.t_mt_sms_200702 partition(p_mt_sms_20070206) t1
#Elapsed: 00:05:07.04


#Elapsed: 00:04:29.64 Elapsed: 00:04:34.68  (说明前面create table的时候,不需要order by)
create table test.tmp_sms_mt_info
as
select t1.user_num,t1.service_id,SUB_CHANNEL_CODE,t1.channel_code channel_code,REG_MODE,UNREG_MODE,ENABLE_STATUS,
  FIRST_REG_TIME,LAST_REG_TIME,LAST_UNREG_TIME
  from  test.tmp_user_info_2 t1,test.tmp_oicall_info t2 where t1.service_id=t2.service_id and t1.user_num=t2.recv_address
CREATE INDEX IDX_tmp_sms_mt_info ON test.tmp_sms_mt_info
(USER_NUM, service_id)
NOLOGGING
TABLESPACE TBS_SUB
PCTFREE    10
INITRANS   2
MAXTRANS   255
STORAGE    (
            INITIAL          64K
            MINEXTENTS       1
            MAXEXTENTS       2147483645
            PCTINCREASE      0
            BUFFER_POOL      DEFAULT
           )
NOPARALLEL;
///
select count(*) from mqqflow.t_sso_order_2007 partition(p_sso_order_200702) where stat_date=to_date('2007-02-07', 'YYYY-MM-DD')  2044 行
///
create table tmp_mt_sms
TABLESPACE TBS_STAT
PCTUSED    0
PCTFREE    10
INITRANS   1
MAXTRANS   255
STORAGE    (
            INITIAL          64K
            MINEXTENTS       1
            MAXEXTENTS       2147483645
            PCTINCREASE      0
            BUFFER_POOL      DEFAULT
           )
LOGGING
NOCOMPRESS
NOCACHE
NOPARALLEL
MONITORING
as
select
  t1.MISC_MSG_ID,
  t1.MSG_ID,
  t1.MSG_TYPE,
  t1.FEE_TYPE,
  t1.FEE_VALUE,
  t1.SEND_ADDRESS,
  t1.RECV_ADDRESS,
  t1.FEE_ADDRESS,
  t1.DOWN_STATION,
  t1.OUTTER_ID,
  t1.SERVICE_ID,
  t1.OSS_RECV_CODE,
  t1.MT_TYPE,
  t1.MT_CONTENT,
  t1.MT_TIME,
  t1.CARRY_MSG,
  t1.CARRY_ID,
  t2.channel_code,
  t2.SUB_CHANNEL_CODE,
  t2.user_num,
  t2.REG_MODE,
  t2.UNREG_MODE,
  t2.ENABLE_STATUS,
  t2.FIRST_REG_TIME,
  t2.LAST_REG_TIME,
  t2.LAST_UNREG_TIME,
  t1.RPT_FLAG,
  t1.RPT_STATE,
  t1.GATEWAY_RPT_STATE,
  t1.link_id,
  t1.err_detail
  from  mqqflow.t_mt_sms_200702 partition(p_mt_sms_20070207)  t1
  left join
  test.tmp_sms_mt_info t2
  on t2.user_num = t1.recv_address
  and t1.service_id = t2.service_id

# Elapsed: 00:13:05.74
create table test. tmp_mt_sms
tablespace tbs_sub
as
  select
  t1.MISC_MSG_ID,
  t1.MSG_ID,
  t1.MSG_TYPE,
  t1.FEE_TYPE,
  t1.FEE_VALUE,
  t1.SEND_ADDRESS,
  t1.RECV_ADDRESS,
  t1.FEE_ADDRESS,
  t1.DOWN_STATION,
  t1.OUTTER_ID,
  t1.SERVICE_ID,
  t1.OSS_RECV_CODE,
  t1.MT_TYPE,
  t1.MT_CONTENT,
  t1.MT_TIME,
  t1.CARRY_MSG,
  t1.CARRY_ID,
  decode(t3.link_id,null,t2.channel_code,'WEB') channel_code,
  t2.SUB_CHANNEL_CODE,
  t2.REG_MODE,
  t2.UNREG_MODE,
  t2.ENABLE_STATUS,
  t2.FIRST_REG_TIME,
  t2.LAST_REG_TIME,
  t2.LAST_UNREG_TIME,
  t1.RPT_FLAG,
  t1.RPT_STATE,
  t1.GATEWAY_RPT_STATE,
  t1.link_id,
  t1.err_detail
  from test.tmp_oicall_info_20070206 t1
  left join test.tmp_sso_mt
  t3
  on t3.stat_date=to_date('2007-02-06', 'YYYY-MM-DD')
  and t3.link_id = t1.link_id
  left join
  test.tmp_user_info t2
  on t2.user_num = t1.RECV_ADDRESS
  and t1.service_id = t2.service_id





SQL查询优化 LEFT JOIN和INNER JOIN:
1,
连接了八个数据库表,而且全部使用LEFT JOIN,如下所示:
Resource_Resources A
LEFT JOIN Resource_Clients B ON A.ResourceId = B.ResourceId  
LEFT JOIN Resource_Files C on B.ClientId=C.ClientId 
LEFT JOIN Resource_ClientsModels D ON B.ClientId = D.ClientId
LEFT JOIN Mobile_Models E ON D.ModelId = E.ModelId
LEFT JOIN dbo.Resource_Images F ON A.ResourceId = F.ResourceId
LEFT JOIN dbo.Resource_Tags G ON G.ResourceId = A.ResourceId
LEFT JOIN Website_Tags H ON G.TagId = H.TagId
LEFT JOIN dbo.Resource_Categories I ON A.CategoryId = I.CategoryId

WHERE 部分有四个查询条件
A.Name LIKE
C.Extend LIKE
D.ModelId =
H.Name LIKE
I.Code LIKE
E.Name LIKE

此时的查询比较费力,经历了将近一分钟的漫长查询,通过WEB访问已经超时。只好将部分查询条件去掉。

其中A表20000条记录,B表记录数大于A表记录数,C表记录数大于B表记录数,H表记录数较大

经过修改后,表连接减少为六个,将部分LEFT JOIN改为INNER JOIN,如下所示:
Resource_Resources A
INNER JOIN dbo.Resource_Clients B ON A.ResourceId = B.ResourceId  
INNER JOIN dbo.Resource_Files C on B.ClientId = C.ClientId 
LEFT JOIN dbo.Resource_ClientsModels D ON B.ClientId = D.ResourceClientId
LEFT JOIN dbo.Resource_Tags G ON G.ResourceId = A.ResourceId
INNER JOIN dbo.Website_Tags H ON G.TagId = H.TagId
INNER JOIN dbo.Resource_Categories I ON A.CategoryId = I.CategoryId

WHERE 部分查询条件也有所减少,仅保留两个查询条件:
A.Name LIKE
H.Name LIKE

经过上面的修改后,查询在1秒内完成,基本达到通过WEB访问的要求,没有长时间等待。

2,
原来的:

SELECT 
 M.clientid, 
 M.CardFaceID, 
 N.NormalBanalce, 
 D.DateWorth, 
 T.TimesWorth, 
 B.BookingWorth,
 B.BookingTimesManyBalance
FROM  
 ( 
 SELECT 
   clientid,CardFaceID
 FROM  
  cimain  
 ) M   
 Left Join 
  (SELECT  
   clientid, 
   sum( case when IfGive='是' then Balance * ItemZkl else Balance end) as NormalBanalce  
  FROM ccNormal   
  Group By  clientid  ) N on M.clientid=N.clientid  
 Left Join           
  (SELECT  
   clientid, 
   sum(   ConsumeBalance * ItemZkl  )  as DateWorth
  FROM ccDate    
  Group By  clientid  ) D on M.clientid=D.clientid  
 Left Join 
  (SELECT  
   clientid, 
   sum(   AveragePrice * TimesBalance * ItemZKL   )  as TimesWorth      
  FROM ccTimes   
  Group By  clientid  ) T on M.clientid=T.clientid  
 Left Join
  (SELECT  
   clientid, 
   sum(   PriceDiscount * TimesBalance   )  as BookingWorth, 
   sum(TimesBalance) as BookingTimesManyBalance         
  FROM ccBooking   
  Group By  clientid  ) B on M.clientid=B.clientid

优化后:
SELECT
    M.clientid  , 
    M.CardFaceID, 
    (SELECT sum(case IfGive when '是' then Balance*ItemZkl else Balance end) FROM ccNormal WHERE clientid=M.clientid) AS NormalBanalce,
    (SELECT sum(ConsumeBalance*ItemZkl) FROM ccDate WHERE clientid=M.clientid) AS DateWorth, 
    (SELECT sum(AveragePrice*TimesBalance*ItemZKL) FROM ccTimes WHERE clientid=M.clientid) AS TimesWorth, 
    (SELECT sum(PriceDiscount*TimesBalance) FROM ccBooking WHERE clientid=M.clientid) AS BookingWorth,
    (SELECT sum(TimesBalance) FROM ccBooking WHERE clientid=M.clientid) AS BookingTimesManyBalance
FROM  
    cimain M

3,
SELECT  
COUNT(DISTINCT T1.A1) + COUNT(DISTINCT T2.B1) + 
COUNT(DISTINCT T3.C1)  FROM T1  
LEFT JOIN T2 on T1.A1 = T2.A1  
LEFT JOIN T3 on  T1.A1 = T3.A1  
LEFT JOIN T4 on  T3.C1 = T4.C1  
GROUP BY T1.A2, T1.A3

优化:
1、因为T1表式主表,所以
【select COUNT(DISTINCT T1.A1) from T1】和你求出的
【COUNT(DISTINCT T1.A1)】值是一样的。
2、而由于T2等是从表并且你使用了【COUNT(DISTINCT T2.B1)】因此null值会被排除掉,实际上和下面的语句求出的值是一样的
select COUNT(DISTINCT T2.B1) from T1 inner join T2 on T1.A1 = T2.A1;
3、从上面的分析可以看出你使用【left join】的目的只有一个就是得到【T1】表全部数据的【COUNT(DISTINCT T1.A1)】,所以试试改成下面的sql是否性能能够快些

select cnt1+cnt2+cnt3 from(
(select COUNT(DISTINCT T1.A1) cnt1 from T1 GROUP BY T1.A2, T1.A3)t1,
(select COUNT(DISTINCT T2.B1) cnt2 from T1 inner join T2 on T1.A1 = T2.A1 GROUP BY T1.A2, T1.A3)t2,
(select COUNT(DISTINCT T3.C1) cnt3 from T1 inner join T3 on T1.A1 = T3.A1 inner join T4 on  T3.C1 = T4.C1 GROUP BY T1.A2, T1.A3)t3;

4,Left.join优化规则的研究.doc:
    一、概述 
  对于left join的优化,是应用开发人员、数据库内核开发人员关注的问题之一。
  应用开发人员关注是因为:并不是每个数据库的内核都支持left join的内部转化,这时候需要应用开发人员进行手工地转化。
  内核开发人员关注是因为:并不假定每个应用开发人员都能够熟练地将left join转化掉。因此数据库有必要对这种情况,进行数据库内部的优化。
  我当初对left join进行分析归纳,后来阅读mysql时发现sql_select.cpp文件中的simplify_joins()函数的实现方法也是这样的,大家可以参考该函数。
  二、left join优化规则的研究
  t1 left t2 on t1.col1=t2.col1
  对于类似的表达式,在什么样的情况下才可以去掉left join呢?
  我们首先创建三张表:
  create table t1(c1 int,c2 int);
  create table t2(d1 int,d2 int);
  create table t3(e1 int,e2 int);
  2.1 优化的基本策略
  对于left join的查询语句,比如:
  select * from t1 left join t2 on t1.c1=t2.d2 where condition1 [{and conditonN}];(N的取值为2,3,……) (语句1)
  什么情况下,才能优化为语句:
  select * from t1 inner join t2 on on t1.c1=t2.d2 where condition1 [{and conditonN}]; (语句2)
  备注:语句2等价于语句:
  select * from t1,t2 where t1.c1=t2.d2 and condition1 [{and conditonN}]; (语句3)
  回答:
  只要where中的至少有一个conditionK(N的取值为1,2,……)满足如下非NULL条件,就可以将语句1优化为语句2(语句3):
  1)conditionK包含t2表的列(任意列)
  2)conditionK的类型只要不为: t2.column is null。
  其它的任何类型都行:比如t2.d2=t1.c2,再比如t2.d2 is not null。
  例1:
  select * from t1 left join t2 on t1.c1=t2.d2 where t2.d1=2; (t2.d1=2满足非NULL条件,可以优化)
  <==>等价于: select * from t1 inner join t2 on t1.c1=t2.d2 where t2.d1=2;
  <==>等价于: select * from t1,t2 where t1.c1=t2.d2 and t2.d1=2;
  例2:select * from t1 left join t2 on t1.c1=t2.d2 where t2.d1+1>t1.c1; (t2.d1+1>t1.c1满足非NULL条件,可以优化)
  <==>等价于: select * from t1 inner join t2 on t1.c1=t2.d2 where t2.d1+1>t1.c1;
  <==>等价于: select * from t1,t2 where t1.c1=t2.d2 and t2.d1+1>t1.c1;
  2.2思路扩展
  a left join b on condition1 {and conditionM}
  left join c on contion2_1 {and contion2_N}
  --优化的思路和上文提出的观点完全一样。
  例3:
  select * from t1 left join t2 on c1=d1 left join t3 on d2=e1 where e1=1; (e1满足非NULL条件,可以优化,甚至这里可以为:e2 in (select ……))
  <==>等价于:select * from t1 left join t2 on c1=d1 inner join t3 on d2=e1 where e1=1; //inner转换
  <==>等价于:select * from t1 left join t2 on c1=d1,t3 where d2=e1 and e1=1; //等价调整,然后(d2=e1满足非NULL条件,可以优化)
  <==>等价于:select * from t1 inner join t2 on c1=d1,t3 where d2=e1 and e1=1; //inner转换
  <==>等价于:select * from t1,t2,t3 where c1=d1 and d2=e1 and e1=1;

5,
Sybase SQL Server索引的使用和优化:
本文就SQL Server索引的性能问题进行了一些分析和实践。

  一、聚簇索引(clustered indexes)的使用

  聚簇索引是一种对磁盘上实际数据重新组织以按指定的一个或多个列的值排序。由于聚簇索引的索引页面指针指向数据页面,所以使用聚簇索引查找数据几乎总是比使用非聚簇索引快。每张表只能建一个聚簇索引,并且建聚簇索引需要至少相当该表120%的附加空间,以存放该表的副本和索引中间页。建立聚簇索引的思想是:

  1、大多数表都应该有聚簇索引或使用分区来降低对表尾页的竞争,在一个高事务的环境中,对最后一页的封锁严重影响系统的吞吐量。

  2、在聚簇索引下,数据在物理上按顺序排在数据页上,重复值也排在一起,因而在那些包含范围检查(between、<、<=、>、>=)或使用group by或order by的查询时,一旦找到具有范围中第一个键值的行,具有后续索引值的行保证物理上毗连在一起而不必进一步搜索,避免了大范围扫描,可以大大提高查询速度。

  3、在一个频繁发生插入操作的表上建立聚簇索引时,不要建在具有单调上升值的列(如IDENTITY)上,否则会经常引起封锁冲突。

  4、在聚簇索引中不要包含经常修改的列,因为码值修改后,数据行必须移动到新的位置。

  5、选择聚簇索引应基于where子句和连接操作的类型。聚簇索引的侯选列是:

    1、主键列,该列在where子句中使用并且插入是随机的。

    2、按范围存取的列,如pri_order > 100 and pri_order < 200。

    3、在group by或order by中使用的列。

    4、不经常修改的列。

    5、在连接操作中使用的列。

 


  二、非聚簇索引(nonclustered indexes)的使用

  SQL Server缺省情况下建立的索引是非聚簇索引,由于非聚簇索引不重新组织表中的数据,而是对每一行存储索引列值并用一个指针指向数据所在的页面。换句话说非聚簇索引具有在索引结构和数据本身之间的一个额外级。一个表如果没有聚簇索引时,可有250个非聚簇索引。每个非聚簇索引提供访问数据的不同排序顺序。在建立非聚簇索引时,要权衡索引对查询速度的加快与降低修改速度之间的利弊。另外,还要考虑这些问题:

  1、索引需要使用多少空间。

  2、合适的列是否稳定。

  3、索引键是如何选择的,扫描效果是否更佳。

  4、是否有许多重复值。

  对更新频繁的表来说,表上的非聚簇索引比聚簇索引和根本没有索引需要更多的额外开销。对移到新页的每一行而言,指向该数据的每个非聚簇索引的页级行也必须更新,有时可能还需要索引页的分理。从一个页面删除数据的进程也会有类似的开销,另外,删除进程还必须把数据移到页面上部,以保证数据的连续性。所以,建立非聚簇索引要非常慎重。非聚簇索引常被用在以下情况:

  1、某列常用于集合函数(如Sum,....)。

  2、某列常用于join,order by,group by。

  3、查寻出的数据不超过表中数据量的20%。

  三、覆盖索引(covering indexes)的使用

  覆盖索引是指那些索引项中包含查寻所需要的全部信息的非聚簇索引,这种索引之所以比较快也正是因为索引页中包含了查寻所必须的数据,不需去访问数据页。如果非聚簇索引中包含结果数据,那么它的查询速度将快于聚簇索引。

  但是由于覆盖索引的索引项比较多,要占用比较大的空间。而且update操作会引起索引值改变。所以如果潜在的覆盖查询并不常用或不太关键,则覆盖索引的增加反而会降低性能。

 

  四、索引的选择技术

  p_detail是住房公积金管理系统中记录个人明细的表,有890000行,观察在不同索引下的查询运行效果,测试在C/S环境下进行,客户机是IBM PII350(内存64M),服务器是DEC Alpha1000A(内存128M),数据库为SYBASE11.0.3。

  1、 select count(*) from p_detail where op_date>’19990101’ and op_date<’19991231’ and pri_surplus1>300

  2、 select count(*),sum(pri_surplus1) from p_detail where op_date>’19990101’ and pay_month between‘199908’ and’199912’

  不建任何索引查询1 1分15秒

  查询2 1分7秒

  在op_date上建非聚簇索引查询1 57秒

  查询2 57秒

  在op_date上建聚簇索引查询1 <1秒

  查询2 52秒

  在pay_month、op_date、pri_surplus1上建索引查询1 34秒

  查询2 <1秒

  在op_date、pay_month、pri_surplus1上建索引查询1 <1秒

  查询2 <1秒

  从以上查询效果分析,索引的有无,建立方式的不同将会导致不同的查询效果,选择什么样的索引基于用户对数据的查询条件,这些条件体现于where从句和join表达式中。一般来说建立索引的思路是:

  (1)、主键时常作为where子句的条件,应在表的主键列上建立聚簇索引,尤其当经常用它作为连接的时候。

  (2)、有大量重复值且经常有范围查询和排序、分组发生的列,或者非常频繁地被访问的列,可考虑建立聚簇索引。

  (3)、经常同时存取多列,且每列都含有重复值可考虑建立复合索引来覆盖一个或一组查询,并把查询引用最频繁的列作为前导列,如果可能尽量使关键查询形成覆盖查询。

  (4)、如果知道索引键的所有值都是唯一的,那么确保把索引定义成唯一索引。

  (5)、在一个经常做插入操作的表上建索引时,使用fillfactor(填充因子)来减少页分裂,同时提高并发度降低死锁的发生。如果在只读表上建索引,则可以把fillfactor置为100。

  (6)、在选择索引键时,设法选择那些采用小数据类型的列作为键以使每个索

  引页能够容纳尽可能多的索引键和指针,通过这种方式,可使一个查询必须遍历的索引页面降到最小。此外,尽可能地使用整数为键值,因为它能够提供比任何数据类型都快的访问速度。

 

  五、索引的维护

  上面讲到,某些不合适的索引影响到SQL Server的性能,随着应用系统的运行,数据不断地发生变化,当数据变化达到某一个程度时将会影响到索引的使用。这时需要用户自己来维护索引。索引的维护包括:

  1、重建索引

  随着数据行的插入、删除和数据页的分裂,有些索引页可能只包含几页数据,另外应用在执行大块I/O的时候,重建非聚簇索引可以降低分片,维护大块I/O的效率。重建索引实际上是重新组织B-树空间。在下面情况下需要重建索引:

  (1)、数据和使用模式大幅度变化。

  (2)、排序的顺序发生改变。

  (3)、要进行大量插入操作或已经完成。

  (4)、使用大块I/O的查询的磁盘读次数比预料的要多。

  (5)


您正在看的sybase教程是:Sybase SQL Server索引的使用和优化。、由于大量数据修改,使得数据页和索引页没有充分使用而导致空间的使用超出估算。

  (6)、dbcc检查出索引有问题。

  当重建聚簇索引时,这张表的所有非聚簇索引将被重建.

  2、索引统计信息的更新

  当在一个包含数据的表上创建索引的时候,SQL Server会创建分布数据页来存放有关索引的两种统计信息:分布表和密度表。优化器利用这个页来判断该索引对某个特定查询是否有用。但这个统计信息并不动态地重新计算。这意味着,当表的数据改变之后,统计信息有可能是过时的,从而影响优化器追求最有工作的目标。因此,在下面情况下应该运行update statistics命令:

  (1)、数据行的插入和删除修改了数据的分布。

  (2)、对用truncate table删除数据的表上增加数据行。

  (3)、修改索引列的值。

  六、结束语

  实践表明,不恰当的索引不但于事无补,反而会降低系统的执行性能。因为大量的索引在插入、修改和删除操作时比没有索引花费更多的系统时间。例如下面情况下建立的索引是不恰当的:

  1、在查询中很少或从不引用的列不会受益于索引,因为索引很少或从来不必搜索基于这些列的行。

  2、只有两个或三个值的列,如男性和女性(是或否),从不会从索引中得到好处。

  另外,鉴于索引加快了查询速度,但减慢了数据更新速度的特点。可通过在一个段上建表,而在另一个段上建其非聚簇索引,而这两段分别在单独的物理设备上来改善操作性能。

 


你可能感兴趣的:(sql之left join、right join、inner join的区别)