SQL Server 要避免的编程坏习惯

摘自

使用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的定义返回有序的数据集,这个假设是不成立的. 从执行计划上是可以看出来的

posted @ 2018-09-14 21:18  haozhang2016  阅读(348)  评论(0编辑  收藏  举报