正确高效的编写sql语句的境界是将一个复杂问题分解成一个一个简单的小问题。

时间:2022-11-03 17:36:41
很多人觉得sql简单,select,where的简单例子让人们产生了一种误解:sql不过如此,没有什么精妙可言。可是,我要对那些在心中没有sql地位的人说一句:sql是一门计算机语言,它的编写过程蕴含了精妙的逻辑思维,它的功能十分强大,足可以跟java,c#媲美。我瞧不起那种没有学习几天sql就对它指指点点的人,也瞧不起那种拿到sql程序上上下下看几眼就告诉别人它是干什么的人。我觉得至少需要2个月的时间,每天有2个小时的学习与实践时间才能真正的入门sql,才会体会出sql的精妙的逻辑思维。
上面我说了sql的入门,下面我想说一下sql的提高和编写sql的境界。我认为正确编写sql的境界是:将一个复杂问题分解成一个一个简单的小问题。例如我要实现三个表的联查的时候,我分为以下几个步骤:
(1)首先从port_e1表中查出e1端口的条数:
select * from port_e1;
结果为为295条。
(2)然后联查一下port表和port_e1表:
select 
`port`.`PORT_ID` AS `PORT_ID`,
`port`.`CARD_ID` AS `CARD_ID`,
`port`.`PORT_INDEX` AS `PORT_INDEX`,
`port`.`PORT_NAME` AS `PORT_NAME`,
`port_e1`.`LOOPBACK_TYPE` AS `LOOPBACK_TYPE`
from `port`
JOIN `port_e1`  ON (`port`.`PORT_ID`= `port_e1`.`PORT_ID`);
得出的记录仍然应该是295条,说明我这一步是正确的。
(3)最后根据左联查,查出e1对应的Z1_TP:
select 
`port`.`PORT_ID` AS `PORT_ID`,
`port`.`CARD_ID` AS `CARD_ID`,
`port`.`PORT_INDEX` AS `PORT_INDEX`,
`port`.`PORT_NAME` AS `PORT_NAME`,
`port_e1`.`LOOPBACK_TYPE` AS `LOOPBACK_TYPE`,
ems_cxc_common.Z1_TP AS `Z1_TP`
from `port`
JOIN `port_e1`  ON (`port`.`PORT_ID`= `port_e1`.`PORT_ID`)
LEFT JOIN ems_cxc_common ON (port_e1.PORT_ID = ems_cxc_common.Z1_TP)
最后结果仍然是295条。从而最后证明了我所编写的数据库脚本是正确的。
如果编写sql语句之前,没有清晰的条理,最后的结果一定会让你的脑袋瞬间爆炸。千万不要在脑袋里面构建一个sql的主题架构,然后立马运行脚本。别以为sql简单,自己可以掌控全局。当你执行毫无条理的sql,你面对的调试是非常困难的,你会想象不到的困难。
本文有鸟啼音本人原创。未经同意不得转载。
鸟啼音:www.birdsing.net

14 个解决方案

#1


曾经在论坛看到一个贴,具体是什么忘了,还被推荐过得,觉得里面说的挺好的一句话的大概意思是:有些问题合起来做会更好,也有一些问题分开来做会更好,做了3年开发和1年DBA,得出其中一个经验是:数据库几乎没有一成不变的铁律(当然也有,比如面向集合的操作一定比面向过程的操作高效(针对关系数据库,其他的我不加评论),清晰、必要的注释等等。)。真的要根据实际情况去具体分析和测试,比如是用表关联还是用关联子查询的问题,连《SQL.Server.2008编程入门经典(第3版)》这本书上也不能给出绝对的答案。也要视乎两表大小、结果这些来具体判断。加之在《sql语言艺术》一书上举了一个例子:Oracle的,一个存储过程,非常长,运行了20多分钟,但是经过作者的优化,20秒出来了。就是他把很多本来可以一次性做但被开发人员分步做的步骤合起来,再修改一些索引之类的。我在做DBA调优的时候,也发现很多操作其实可以何在一起,然后用case when来替代,从速度、I/O上都有明显的提升甚至几个数量级的加快, 所以我同意楼主的部分观点,但是我坚持还是要具体情况具体分析

#2


该回复于2012-07-27 10:02:39被版主删除

#3


个人观点比较偏向于楼主的观点,每个存储过程,函数尽量简洁化。
但是出于性能的考虑,在简洁化的程度上,能一起处理的就一起处理。

#4


感谢诸位的捧场。

#5


楼主支持一下,我用的sql server,有的时候感觉很怪异,我们可能自己感觉是对的,但是实际上代码报错

如下面的代码,如果不对中间括号中的select增加别名的话,则编译的时候报错



select c.username,avg(c.diff) from 
(
select b.username ,b.department  ,datediff(hour,a.timestr,a.latetime) as diff
from worktodo a 
left join  users b
on a.userid=b.truename
)  c--这个地方不加别名就报错
group by c.username



#6


引用 5 楼  的回复:
楼主支持一下,我用的sql server,有的时候感觉很怪异,我们可能自己感觉是对的,但是实际上代码报错

如下面的代码,如果不对中间括号中的select增加别名的话,则编译的时候报错



SQL code

select c.username,avg(c.diff) from 
(
select b.username ,b.department  ,datediff(hou……


这个地方肯定要加别名的,这个别名相当一个表名。你不加SQL,理解成没有表名了。

#7


如果指令设置为 CDATA,则不对包含的数据进行实体编码,而是将其放入 CDATA 部分。CDATA 属性必须没有名称。

#8


代码量与性能,你选择哪个?

#9


引用 7 楼  的回复:
如果指令设置为 CDATA,则不对包含的数据进行实体编码,而是将其放入 CDATA 部分。CDATA 属性必须没有名称。

发错贴了

楼主有什么不明的,掌握表连接,性能看执行计划

#10


该回复于2012-07-29 11:02:59被版主删除

#11


引用 5 楼  的回复:
楼主支持一下,我用的sql server,有的时候感觉很怪异,我们可能自己感觉是对的,但是实际上代码报错

如下面的代码,如果不对中间括号中的select增加别名的话,则编译的时候报错



SQL code

select c.username,avg(c.diff) from 
(
select b.username ,b.department  ,datediff(hou……


这个是你把基本语法弄错了,当查询语句作为另外一个查询的输入表时(这样解释能看明白吧),必须取别名,也就是select * from(select * from tb where ....) as t(这个t可以随意换,as可以省略)

#12


引用 11 楼  的回复:
引用 5 楼  的回复:
楼主支持一下,我用的sql server,有的时候感觉很怪异,我们可能自己感觉是对的,但是实际上代码报错

如下面的代码,如果不对中间括号中的select增加别名的话,则编译的时候报错



SQL code

select c.username,avg(c.diff) from
(
select b.username ,b.department ……


明白了,主要是看到楼主写的东西比较好,就把自己最近遇到的小困惑写了一些

不过我感觉最好是能在写完了语句以后,再回头来看看,能不能用更快的方法进行操作,这样才能有提高,先解决问题,再更好的解决问题

#13


深有同感

#14


该回复于2012-08-01 08:18:38被版主删除

#1


曾经在论坛看到一个贴,具体是什么忘了,还被推荐过得,觉得里面说的挺好的一句话的大概意思是:有些问题合起来做会更好,也有一些问题分开来做会更好,做了3年开发和1年DBA,得出其中一个经验是:数据库几乎没有一成不变的铁律(当然也有,比如面向集合的操作一定比面向过程的操作高效(针对关系数据库,其他的我不加评论),清晰、必要的注释等等。)。真的要根据实际情况去具体分析和测试,比如是用表关联还是用关联子查询的问题,连《SQL.Server.2008编程入门经典(第3版)》这本书上也不能给出绝对的答案。也要视乎两表大小、结果这些来具体判断。加之在《sql语言艺术》一书上举了一个例子:Oracle的,一个存储过程,非常长,运行了20多分钟,但是经过作者的优化,20秒出来了。就是他把很多本来可以一次性做但被开发人员分步做的步骤合起来,再修改一些索引之类的。我在做DBA调优的时候,也发现很多操作其实可以何在一起,然后用case when来替代,从速度、I/O上都有明显的提升甚至几个数量级的加快, 所以我同意楼主的部分观点,但是我坚持还是要具体情况具体分析

#2


该回复于2012-07-27 10:02:39被版主删除

#3


个人观点比较偏向于楼主的观点,每个存储过程,函数尽量简洁化。
但是出于性能的考虑,在简洁化的程度上,能一起处理的就一起处理。

#4


感谢诸位的捧场。

#5


楼主支持一下,我用的sql server,有的时候感觉很怪异,我们可能自己感觉是对的,但是实际上代码报错

如下面的代码,如果不对中间括号中的select增加别名的话,则编译的时候报错



select c.username,avg(c.diff) from 
(
select b.username ,b.department  ,datediff(hour,a.timestr,a.latetime) as diff
from worktodo a 
left join  users b
on a.userid=b.truename
)  c--这个地方不加别名就报错
group by c.username



#6


引用 5 楼  的回复:
楼主支持一下,我用的sql server,有的时候感觉很怪异,我们可能自己感觉是对的,但是实际上代码报错

如下面的代码,如果不对中间括号中的select增加别名的话,则编译的时候报错



SQL code

select c.username,avg(c.diff) from 
(
select b.username ,b.department  ,datediff(hou……


这个地方肯定要加别名的,这个别名相当一个表名。你不加SQL,理解成没有表名了。

#7


如果指令设置为 CDATA,则不对包含的数据进行实体编码,而是将其放入 CDATA 部分。CDATA 属性必须没有名称。

#8


代码量与性能,你选择哪个?

#9


引用 7 楼  的回复:
如果指令设置为 CDATA,则不对包含的数据进行实体编码,而是将其放入 CDATA 部分。CDATA 属性必须没有名称。

发错贴了

楼主有什么不明的,掌握表连接,性能看执行计划

#10


该回复于2012-07-29 11:02:59被版主删除

#11


引用 5 楼  的回复:
楼主支持一下,我用的sql server,有的时候感觉很怪异,我们可能自己感觉是对的,但是实际上代码报错

如下面的代码,如果不对中间括号中的select增加别名的话,则编译的时候报错



SQL code

select c.username,avg(c.diff) from 
(
select b.username ,b.department  ,datediff(hou……


这个是你把基本语法弄错了,当查询语句作为另外一个查询的输入表时(这样解释能看明白吧),必须取别名,也就是select * from(select * from tb where ....) as t(这个t可以随意换,as可以省略)

#12


引用 11 楼  的回复:
引用 5 楼  的回复:
楼主支持一下,我用的sql server,有的时候感觉很怪异,我们可能自己感觉是对的,但是实际上代码报错

如下面的代码,如果不对中间括号中的select增加别名的话,则编译的时候报错



SQL code

select c.username,avg(c.diff) from
(
select b.username ,b.department ……


明白了,主要是看到楼主写的东西比较好,就把自己最近遇到的小困惑写了一些

不过我感觉最好是能在写完了语句以后,再回头来看看,能不能用更快的方法进行操作,这样才能有提高,先解决问题,再更好的解决问题

#13


深有同感

#14


该回复于2012-08-01 08:18:38被版主删除