多表查询SQL 语句优化

    数据多个表关联查询时,由于表之间的连接关系(内连接、外连接、交叉连接),导致数据库服务器常常从几万条甚至更多的数据记录中查找符合条件的记录,如果sql查询语句设计不好查询的复杂度就会直线上升,甚至是指数级上升,导致查询时间长甚至失去相应,这里讲两种从sql语句优化查询的方法。

1.把外连接变成交叉连接或内连接(对不起,在写改良例子的时候发现这种方法并不能改善查询的速度,这是一个错误):

证明过程如下:

我们建三个表分别是:templebonze、和woodfish

temple表示庙宇,它的字段有:

temple_id       int

temple_name   varchar(50)

location   varchar(50)

build_date       datetime

 

temple_id是主键,设为自增;temple_name是庙宇名;location是位置,就是寺庙的地址;build_date是建庙时间。

 

bonze表示和尚,它的字段有(出家人四大皆空,这些字段够了,呵呵)

bonze_id  int

temple_id        int

bonze_name    varchar(50)

register_time   datetime

bonze_id是主键,设为自增;temple_id表示这个和尚属于哪个寺庙,对应寺庙的temple_id,对于没有寺庙的流浪和尚这里暂时不考虑(写这篇文章主要是告诉一些大寺庙,他们可以采用软件管理的方式来管理寺庙,如果他们看到了我这篇文章可能会找我开发一个开发软件也未定,至于流浪和尚是不太可能的了,所以不考虑他们,呵呵,罪过、罪过)bonze_name是和尚名字,和尚本没有名字就叫法号吧;register_time是注册时间,现在做个和尚挺不容易的,难度差不多赶上高考上榜了,所以要一个注册时间。

 

woodfish表示木鱼,呵呵,木鱼的英文实在不会写,只好直译了,哪位兄弟姐妹知道了告诉我一下。木鱼的字段有:

woodfish_id    int    Unchecked

bonze_id  int    Unchecked

woodfish_num       varchar(50)    Checked

woodfish_id是主键,设为自增;bonze_id表示这个木鱼属于那个和尚,不属于任何和尚的木鱼我们也暂时不考虑,那些房子仓库就可以了,不用编号什么的;woodfish_num表示木鱼编号。

我们建立表的关系图如下(微软的SqlServerManagement应该提供可以把关系图转换成图片的功能,我还要photoshop来截图)

多表查询SQL 语句优化_第1张图片

1 数据关系图

相当简单的一个数据库。我们假设有些寺庙可以没有和尚,这就是荒庙,看过武侠小说的兄弟们应该可以了解的。和尚可以没有木鱼,可能太穷了,买不起,也可能太懒了,懒得敲。当然有些和尚也可以拥有几个木鱼,可能准备收徒弟的时候送个木鱼做见面礼什么的。

现在我们插入一些数据。一下数据仅为测试使用,并无意冒犯任何寺庙和庙里的高僧。

temple的数据为:

temple_id

temple_name

location

build_time

1

少林寺

河南省登封市嵩山

1900-2-2 0:00:00

2

大杰寺

五龙山

1933-2-3 3:03:03

3

法源寺

宣武门外教子胡同南端东侧

1941-2-3 5:04:03

4

广济寺

阜成门内大街东口

1950-3-3 3:03:03

5

碧云寺

香山东麓

1963-3-3 3:03:03

1 寺庙信息

bonze的数据为:

bonze_id

temple_id

bonze_name

register_time

1

1

悟空

2003-2-2 0:00:00

2

1

悟静

2002-2-2 0:00:00

3

1

悟能

2001-2-2 0:00:00

4

1

悟性

2004-4-4 0:00:00

5

1

悟戒

2005-5-5 0:00:00

6

2

觉原

2001-3-3 0:00:00

7

2

觉情

2005-3-3 0:00:00

8

2

觉静

2006-4-4 0:00:00

9

3

一噌

2004-4-4 0:00:00

10

5

八戒

2001-1-1 0:00:00

2 和尚信息

woodfish的数据为:

woodfish

bonze_id

woodfish_num

1

1

1111

2

1

2222

3

1

3333

4

2

4444

5

6

5555

6

8

6666

7

8

7777

8

10

8888

9

10

9999

10

10

1010

11

4

11111

12

4

1212

13

4

1313

 

好了,假设现在我们要查询某一个或几个寺庙的详细信息,即寺庙信息、和尚信息和木鱼信息。对于没有和尚的寺庙要列一条记录,和尚和木鱼的所有字段显示为空;对于没有木鱼的和尚也列一条记录,木鱼信息显示为空。

如果我们直接用内连接查询,对于没有和尚的寺庙的信息或者没有木鱼的和尚的信息就查不出来,因为数据库服务器先对所有表做交叉连接,然后在找到符合条件的数据。如果和尚信息为空,这个寺庙信息交叉后就是空的,和尚和木鱼也是同样的道理。

这时我们就要用到外连接,外连接分左连接、右连接、和全外(ALL)连接,左连接就是左边的表作为重点,把左边的表的信息全列出来,对于右边的表用空(整数用0)来补;右连接重点刚好相反,全外连接就是没有主次之分的。两边的表信息都能列举出来。

现在我们要用到的就是左连接。

左连接后的搜索记录总数可以通过以下SQL语句来检测:

SELECT     temple.temple_id, temple.temple_name, temple.location, temple.build_date, bonze.bonze_id, bonze.temple_id AS Expr1, bonze.bonze_name,

                      bonze.register_time, woodfish.woodfish_id, woodfish.bonze_id AS Expr2, woodfish.woodfish_num

FROM         temple LEFT OUTER JOIN

                      bonze ON temple.temple_id = bonze.temple_id LEFT OUTER JOIN

                      woodfish ON bonze.bonze_id = woodfish.bonze_id

 

总共有18条记录

 

改成内连接后搜索记录总数可以通过以下三个SQL语句查询结果联合得到:

SELECT     temple.temple_id, temple.temple_name, temple.location, temple.build_date, bonze.bonze_id, bonze.temple_id AS Expr1, bonze.bonze_name,

                      bonze.register_time, woodfish.woodfish_id, woodfish.bonze_id AS Expr2, woodfish.woodfish_num

FROM         temple INNER JOIN

                      bonze ON temple.temple_id = bonze.temple_id INNER JOIN

                      woodfish ON bonze.bonze_id = woodfish.bonze_id

 

寺庙既有和尚和尚又有木鱼的记录有13条记录

 

SELECT     temple.temple_id, temple.temple_name, temple.location, temple.build_date, bonze.bonze_id, bonze.temple_id AS Expr1, bonze.bonze_name,

                      bonze.register_time, 0 AS woodfish_id, 0 AS Expr2, 0 AS woodfish_num

FROM         temple INNER JOIN

                      bonze ON temple.temple_id = bonze.temple_id

WHERE     (bonze.bonze_id NOT IN

                          (SELECT     bonze_id

                            FROM          woodfish))

 

寺庙的和尚没有木鱼有4条记录

 

SELECT     temple_id, temple_name, location, build_date, 0 AS bonze_id, 0 AS Expr1, NULL AS bonze_name, NULL AS register_time, 0 AS woodfish_id,

                      0 AS Expr2, 0 AS woodfish_num

FROM         temple

WHERE     (temple_id NOT IN

                          (SELECT     temple_id

                            FROM          bonze))

 

寺庙即没有和尚又没有木鱼的记录有1

 

 

以上证明把外连接改成内连接并不能改善查询速度。

2.把内连接的where子句换成子表查询(去掉where子句):

改良前

SELECT DISTINCT

                      student.card_id, student.name, student.student_num, [order].order_id, [order].order_since, [order].persist_time, lesson.endure_time, lesson.allow_late,

                      lab.name AS labname, bench.num, bench.bench_ip

FROM         student INNER JOIN

                      [order] ON student.student_id = [order].student_id INNER JOIN

                      lesson ON [order].lesson_id = lesson.lesson_id INNER JOIN

                      bench ON [order].bench_id = bench.bench_id INNER JOIN

                      lab ON bench.lab_id = lab.lab_id

WHERE     (NOT ([order].order_id IN

                          (SELECT     order_id

                            FROM          history))) AND (student.student_num = @card_id) AND (lab.name = @labname) AND ([order].status = 0) AND ([order].editor_type = 0)

ORDER BY [order].order_since

改良后

SELECT DISTINCT

                      student1.card_id, student1.name, student1.student_num, order1.order_id, order1.order_since, order1.persist_time, lesson.endure_time,

                      lesson.allow_late, lab1.name AS labname, bench.num, bench.bench_ip

FROM         (SELECT     student_id, card_id, name, student_num

                       FROM          student

                       WHERE      (student_num = 'card_id233')) AS student1 INNER JOIN

                          (SELECT     order_id, bench_id, lesson_id, student_id, order_since, persist_time

                            FROM          [order]

                            WHERE      (NOT (order_id IN

                                                       (SELECT     order_id

                                                         FROM          history))) AND (status = 0) AND (editor_type = 0)) AS order1 ON student1.student_id = order1.student_id INNER JOIN

                      lesson ON order1.lesson_id = lesson.lesson_id INNER JOIN

                      bench ON order1.bench_id = bench.bench_id INNER JOIN

                          (SELECT     lab_id, name

                            FROM          lab

                            WHERE      (name = 'labname23')) AS lab1 ON bench.lab_id = lab1.lab_id

ORDER BY order1.order_since

你可能感兴趣的:(数据库)