SQL Server 要避免的编程坏习惯

Posted sqlzh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQL Server 要避免的编程坏习惯相关的知识,希望对你有一定的参考价值。

摘自 <SQL Server MVP Deep Dives Vol.2>

使用select *

  • 会返回不需要的列,增加磁盘和网络开销

  • 如果view的定义里用了select *, 而底层的表有列名的变化的时候,view仍然会返回原来的列

IF OBJECT_ID(‘tb1‘) IS NOT NULL
DROP TABLE tb1
GO
CREATE table tb1(id int)
GO

IF OBJECT_ID(‘v1‘) IS NOT NULL
DROP VIEW v1
GO
CREATE view v1 
as
    select * from tb1
GO

select * from v1

--改表的定义,增加1列
ALTER TABLE tb1 ADD name sysname;

--仍然只返回id列,新加的name没有返回
select * from v1

解决的方法是使用sys.sp_refreshsqlmodule刷新定义

sys.sp_refreshsqlmodule ‘v1‘
--返回id, name两列
select * from v1

将view定义为SCHEMABINDING类型可以阻止select *的使用,同时也会阻止底层table的schema的改动,如果需要改table的schema,就需要先drop view。

声明变量varchar/nvarchar 不指定长度

有面向对象编程经验的人可能习惯了使用string类型而不用定义长度,到了SQL里面,这种习惯就会出问题

DECLARE @x CHAR = ‘foo‘;
SELECT a = @x, b = CAST(‘foo‘ AS CHAR), c = CONVERT(CHAR, ‘foo‘);

结果如下:
a | b | c
--|---|--
f | foo | foo |

原因是所有字符类型如果在声明变量时如果没有定义长度,那么长度就是1.(这是遵循ANSI标准的),把上面代码的char换成varchar,nvarchar也是一样的结果。如果你用cast或者convert函数的话,长度是30.

如果你创建表的时候字符类型的列没有指定长度,行为也是一样的,只能存1个字符串

CREATE TABLE dbo.x(y VARCHAR);
GO
INSERT dbo.x(y) SELECT ‘foo‘;

--结果
Msg 8152, Level 16, State 14, Line 2
String or binary data would be truncated.
The statement has been terminated.

如果sp的参数没有定义长度,在传参的过程中就被偷偷截断了

CREATE TABLE dbo.x(y VARCHAR);
GO
CREATE PROCEDURE dbo.x_insert
     @y VARCHAR
AS
BEGIN
     SET NOCOUNT ON;
     SELECT @y;
END
GO
EXEC dbo.x_insert @y = ‘foo‘;

--结果返回‘f‘

选择合适的数据类型

常见的错误是很多人喜欢用字符类型来存时间,理由是能保存成想要的格式,不需要做格式转换就可以直接读取到UI上. 这样与使用日期类型做存储比较,缺点是

  • 无法验证数据格式是否正确
  • 无法对数据按照时间排序
  • 无法使用日期相关的函数对数据进行处理

关于日期类型的查询

使用>, <, 而不是between

关于order by的误解

很多人以为对一个有clustered index的table 进行查询即使不指定order by也会按照clustered index的定义返回有序的数据集,这个假设是不成立的. 从执行计划上是可以看出来的




以上是关于SQL Server 要避免的编程坏习惯的主要内容,如果未能解决你的问题,请参考以下文章

为啥要避免动态 SQL 查询?有啥建议可以删除坏的部分并使用这些吗?

?编程时有哪些不想改的“坏习惯”?

要改掉的10种TypeScript 坏习惯

这18 个 Python 坏习惯,一定要趁早抛弃

打破 10 个 Typescript 编程坏习惯

SQL Server 和 SQL 注入