]关系数据库主要以表为中心。许多操作都以这样或那样的方式在物理表、连接表或派生表上执行。为了有效地编写SQL,重要的是要理解select..From子句需要一个以逗号分隔的表引用列表,无论它们以何种形式提供。
根据表引用的复杂性,有些数据库还接受其他语句中的复杂表引用,如插入、更新、删除和合并。
解决方法:
始终将from子句作为一个整体来考虑表引用。如果写一个连接子句,把这个连接子句想象成一个复杂表引用的一部分:
select c.first_name, c.last_name, o.amount
from
customer_view c
join order_view o
on c.cust_id = o.cust_id
4.使用pre-ansi连接语法
既然已经阐明了表引用是如何工作的,那么无论如何都要避免使用pre-ansi连接语法对于执行计划,如果在join..on子句或where子句中指定连接谓词,通常没有区别。但从可读性和维护的角度来看,对过滤谓词和连接谓词都使用where子句是一个主要的障碍。考虑这个简单的例子:
select c.first_name, c.last_name, o.amount
from customer_view c,
order_view o
where o.amount > 100
and c.cust_id = o.cust_id
and c.language = 'en'
能发现连接谓词吗?如果加入几十张表呢?当为外部连接应用专有语法(例如oracle的(+)语法)时,情况会变得更糟。
解决方法:
始终使用ansi-join语法。永远不要将连接谓词放在where子句中。使用pre-ansi连接语法绝对没有好处。
5. 忘记转义like谓词的输入
SQL标准1992指定like谓词如下:
8.5
::=
[ not ] like
[ escape ]
当允许在SQL查询中使用用户输入时,几乎总是应该使用escape关键字。虽然百分比符号(%)可能很少被认为是数据的一部分,但下划线(_)很可能是:
select *
from t
where t.x like 'some!_prefix%' escape '!'
解决方法:
在使用like谓词时,始终要考虑适当的转义。
6.认为not(in(x,y))是in(x、y)的布尔逆
这一点很微妙,但对于null来说非常重要!以下回顾一下in(x,y)的真正含义:
a in (x, y)
is the same as a = any (x, y)
is the same as a = x or a = y
同时, not (a in (x, y)) 真正的含义是:
not (a in (x, y))
is the same as a not in (x, y)
is the same as a != any (x, y)
is the same as a != x and a != y
这看起来像前一个谓词的布尔逆,但实际上不是!如果x或y中的任何一个为null,则not-in谓词将导致未知,而in谓词可能仍然返回布尔值。
换句话说,当in(x,y)产生true或false时,not(a in(x、y))仍可能产生unknown,而不是false或true。注意,如果in谓词的右侧是子查询,这也是正确的。
不相信吗?看看这个sql fiddle。它表明以下查询没有产生结果:
select 1
where 1 in (null)
union all
select 2
where not(1 in (null))
解决方法:
当涉及nullable列时,要注意not in谓词!
7. 认为not(a为null)与a不为null相同
人们记得SQL在处理null值时实现了三值逻辑。这就是为什么可以使用null谓词来检查null的原因。
但即使是null谓词也很微妙。注意,以下两个谓词仅对度数为1的行值表达式等效:
not (a is null)
is not the same as a is not null
如果a是一个度数大于1的行值表达式,那么真值表转换为:
- 只有当a中的所有值都为null时,a is null才会产生true
- 只有当a中的所有值都为null时,not(a is null)才会产生false
- 只有当a中的所有值都不为null时,a is not null才会产生true
- 只有当a中的所有值都不是null时,not(a is not null) 才会产生false
解决方法:
使用行值表达式时,请注意null谓词,它可能无法按预期工作。
8.在支持行值表达式的地方没有使用行值表达式
行值表达式是一个很好的SQL特性。当SQL是一种以表为中心的语言时,表也以行为中心。行值表达式通过创建可以与具有相同度数和行类型的其他行进行比较的本地特殊行,从而更容易地描述复杂谓词。一个简单的例子是同时查询客户的姓和名。
select c.address
from customer c,
where (c.first_name, c.last_name) = (?, ?)
Where(c.first_name,c.last_name)=(?,?)
可以看到,这种语法比等价语法(谓词左边的每一列都与右边的相应列进行比较)稍微简洁一些。如果许多独立谓词与and组合在一起,则尤其如此。使用行值表达式允许将相关谓词组合为一个谓词。这对于复合外键上的连接表达式非常有用:
select c.first_name, c.last_name, a.street
from customer c
join address a
on (c.id, c.tenant_id) = (a.id, a.tenant_id)
不幸的是,并非所有数据库都以相同的方式支持行值表达式。但是SQL标准在1992年就已经定义了它们,如果使用它们,像Oracle或postgres这样复杂的数据库可以使用它们来计算更好的执行计划。
解决方法:
尽可能使用行值表达式。它们将使SQL更简洁,甚至可能更快。
9.没有定义足够的约束
要引用TomKyte,再使用索引和Luke。元数据中不能有足够的约束。首先,约束帮助防止数据损坏,这已经非常有用了。但更重要的是,约束将帮助数据库执行SQL转换,因为数据库可以决定:
- 有些值是等价的
- 有些子句是多余的
- 某些子句是“无效的”(即它们不会返回任何值)
一些开发人员可能认为约束是缓慢的。与其相反,除非插入大量大量的数据,在这种情况下,可以禁用大型操作的约束,或者使用没有约束的临时“加载表”,不受约束地将数据脱机传输到实际表。
解决方法:
定义尽可能多的约束。它们将帮助数据库在查询时更好地执行。
10.认为50ms是快速查询执行
对于NoSQL的炒作仍在继续,许多企业仍然认为他们是Twitter或Facebook,迫切需要更快、更可扩展的解决方案,逃避ACID和关系模型来横向扩展。有些可能会成功(如Twitter或Facebook)。
对于那些被迫或选择坚持使用经过验证的关系数据库的人来说,如果要和DBA相处得很好并将数据库调到最大的话,不要误以为现代数据库很慢,事实上它们非常快,并在不到一毫秒的时间内解析20kb的查询文本,计算2000行执行计划。
它们可能会变慢,因为应用程序误用了流行的对象关系映射(ORM),或者因为这一ORM不能为复杂的查询逻辑生成快速SQL。在这种情况下,可能想要选择一个更以SQL为中心的API,例如JDBC,jOOQ或MyBatis,这将重新控制SQL。
所以,不要认为50毫秒的查询执行速度很快,甚至可以接受。事实并非如此。如果在开发时获得了这些速度,确保研究了执行计划。这些数据可能会在生产环境中爆发式增长,因为生产环境中有更复杂的场景和数据。
结论
SQL非常有趣,但在许多方面也非常微妙。正如Eder之前关于10个常见错误的博客文章所表明的那样,要做到这一点并不容易。但是SQL是可以掌握的。数据是最宝贵的资产。因此需要尊重数据,并编写更好的SQL。
原文标题:10 More Common Mistakes Java Developers Make when Writing SQL,作者:Lukas Eder
当前名称:Java开发人员编写SQL时常犯的十个错误
URL链接:http://www.shufengxianlan.com/qtweb/news6/12806.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
广告
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源:
创新互联