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