1、Like运算符:将字符串表达式与 SQL表达式中的模式进行比较匹配。
语法 :expression Like 'pattern' ,expression为匹配字段,pattern为匹配字符串。可以通过 Like 运算符来查找与所指定的模式相匹配的字段值。对于 pattern,可以指定完整的值(例如 Like "Smith"
),也可以使用通配符来查找某个范围内的值(例如 Like "Sm*"
)。
下表展示了如何通过 Like 来测试不同模式的表达式。
匹配类型 |
模式 |
匹配 (返回 True) |
不匹配 (返回 False) |
---|---|---|---|
多个字符 | a*a 或 a%a | aa, aBa, aBBBa | aBC |
*ab* 或 %ab% | abc, AABB, Xab | aZb, bac | |
特殊字符 | a[*]a | a*a | aaa |
多个字符 | ab* 或 ab% | abcdefg, abc | cab, aab |
单个字符 | a?a 或 a_a | aaa, a3a, aBa | aBBBa |
单个数字 | a#a | a0a, a1a, a2a | aaa, a10a |
字符范围 | [a-z] | f, p, j | 2, & |
范围之外 | [!a-z] | 9, &, % | b, a |
非数字值 | [!0-9] | A, a, &, ~ | 0, 1, 9 |
复合值 | a[!b-m]# | An9, az0, a99 | abc, aj0 |
可以用于 Like 运算符的通配符,如下表:
pattern 中的字符 | expression 中的匹配项 |
---|---|
? 或 _(下划线) | 任何单个字符 |
* 或 % | 零个或多个字符 |
# | 任何单个数字 (0 — 9) |
[charlist] | 在 charlist 中的任何单个字符。 |
[!charlist] | 不在 charlist 中的任何单个字符。 |
2、T-sql大小写
大写T-SQL 语言的所有关键字都使用大写,规范要求。
3、使用终止符" ; "
使用" ; "作为 Transact-SQL 语句终止符。虽然分号不是必需的,但使用它是一种好的习惯,对于合并操作MERGE语句的末尾就必须要加上" ; ",(cte表表达式除外)。
4、避免使用以下数据类型以下
避免使用ntext、text 和 image 数据类型,用 nvarchar(max)、varchar(max) 和 varbinary(max)替代。后续版本会取消ntext、text 和 image 该三种类型。
5、查询条件不要使用计算列
例如year(createdate)=2014 , 使用 createdate >= '20140101' and createdate <= '20141231' 来取代。
(1)使用计算列查询(走的是索引扫描);(2)不使用计算列查询(走的是索引查找)。对比两个查询显然绝大部分情况下走索引查找的查询性能要高于走索引扫描,特别是查询的数据库不是非常大的情况下,索引查找的消耗时间要远远少于索引扫描的时间。
6、建表时字段不允许为null,可设默认值
很多人在建表的时候不会注意这一点,在接下来的工作中当你需要查询数据的时候你往往需要在WHERE条件中多加一个判断条件IS NOT NULL,这样的一个条件不仅仅增加了额外的开销,而且对查询的性能产生很大的影响,有可能就因为多了这个查询条件导致你的查询变的非常的慢;还有一个比较重要的问题就是允许为空的数据可能会导致你的查询结果出现不准确的问题。如下:
T-SQL是三值逻辑(true,flase,unknown) IF OBJECT_ID('DBO.Customer','U') IS NOT NULL DROP TABLE DBO.Customer GO CREATE TABLE DBO.Customer (Customerid int not null ); GO IF OBJECT_ID('DBO.OrderS','U') IS NOT NULL DROP TABLE DBO.OrderS GO CREATE TABLE DBO.OrderS (Orderid int not null, custid int); GO INSERT INTO Customer VALUES(1),(2),(3); INSERT INTO OrderS VALUES(1,1),(2,2),(3,NULL); ----查询没有订单的顾客 SELECT Customerid FROM DBO.Customer WHERE Customerid NOT IN(SELECT custid FROM OrderS); ---分析为什么查询结果没有数据 /* 因为true,flase,unknown都是真值 因为not in 是需要结果中返回flase值,not true=flase,not flase=flase,not unknown=unknown 因为null值是unknown所以not unknownn无法判断结果是什么值所以不能返回数据 */ --可以将查询语句修改为 SELECT Customerid FROM DBO.Customer WHERE Customerid NOT IN(SELECT custid FROM OrderS WHERE custid is not null); --或者使用EXISTS,因为EXISTS是二值逻辑只有(true,flase)所以不存在未知。 SELECT Customerid FROM DBO.Customer A WHERE NOT EXISTS(SELECT custid FROM OrderS WHERE OrderS.custid=A.Customerid ); ---in查询可以返回值,因为in是true,子查询true,flase,unknown都是真值所以可以返回子查询的true SELECT Customerid FROM DBO.Customer WHERE Customerid IN(SELECT custid FROM OrderS); ----如果整形字段可以赋0,字符型可以赋值空(这里只是给建议)这里的空和NULL是不一样的意思 --增加整形字段可以这样写 ALTER TABLE TABLE_NAME ADD COLUMN_NAME INT NOT NULL DEFAULT(0) --增加字符型字段可以这样写 ALTER TABLE TABLE_NAME ADD COLUMN_NAME NVARCHAR(50) NOT NULL DEFAULT('')
7、分组统计时避免使用count(*)
---如果使用count(*) SELECT Customerid,COUNT(*) FROM Customer TA LEFT JOIN OrderS TB ON TA.Customerid=TB.custid GROUP BY Customerid ;
实际情况customerid=3是没有订单的,数量应该是0,但是结果是1,count()里面的字段是左连接右边的表字段,如果你用的是主表字段结果页是错误的。 ----正确的方法是使用count(custid) SELECT Customerid,COUNT(custid) FROM Customer TA LEFT JOIN OrderS TB ON TA.Customerid=TB.custid GROUP BY Customerid;
7、子查询的表加上表别名
执行查询:SELECT Customerid FROM Customer WHERE Customerid IN(SELECT Customerid FROM OrderS WHERE Orderid=2 );
正确查询结果下查询出的结果是没有customerid为3的值,仔细看应该会发现子查询的orders表中没有Customerid字段,所以SQL取的是Customer表的Customerid值作为相关子查询的匹配字段。
所以我们应该给子查询加上表别名,如果加上表别名,此时会有错误标示:“列名 'Customerid' 无效。”
SELECT Customerid FROM Customer WHERE Customerid IN(SELECT TB.Customerid FROM OrderS TB WHERE TB.Orderid=2 );
8、建立自增列时单独再给自增列添加唯一约束
CREATE TABLE TEST (ID INT NOT NULL IDENTITY(1,1), orderdate date NOT NULL DEFAULT(CURRENT_TIMESTAMP), NAME NVARCHAR(30) NOT NULL, CONSTRAINT CK_TEST_NAME CHECK(NAME LIKE '[A-Za-z]%' ) ); GO INSERT INTO TEST(NAME) VALUES('A中'),('a名'),('Aa'),('ab'),('AA'),('az'); ----4.插入报错后,自增值依旧增加 INSERT INTO TEST(NAME) VALUES('中'); GO SELECT IDENT_CURRENT('TEST'); SELECT * FROM TEST; ---插入正常的数据 INSERT INTO TEST(NAME) VALUES('cc'); SELECT IDENT_CURRENT('TEST') SELECT * FROM TEST; ----5.显示插入自增值 SET IDENTITY_INSERT TEST ON INSERT INTO TEST(ID,NAME) VALUES(8,'A中'); SET IDENTITY_INSERT TEST OFF ----会发现ID并不是根据自增值排列的,而且根据插入的顺序排列的 SELECT IDENT_CURRENT('TEST'); SELECT * FROM TEST; ----6.插入重复的自增值 SET IDENTITY_INSERT TEST ON INSERT INTO TEST(ID,NAME) VALUES(8,'A中'); SET IDENTITY_INSERT TEST OFF SELECT IDENT_CURRENT('TEST') SELECT * FROM TEST; ---所以如果要保证ID是唯一的,单单只设置自增值不行,需要给字段设置主键或者唯一约束 DROP TABLE TEST;
9、查询时一定要指定字段查询
(1)查询时一定不能使用" * "来代替字段来进行查询,无论你查询的字段有多少个,就算字段太多无法走索引也避免了解析" * "带来的额外消耗;
(2)查询字段值列出想要的字段,避免出现多余的字段,字段越多查询开销越大而且可能会因为多列出了某个字段而引起查询不走索引。
所以建议在查询语句中列出你需要的字段,而不是为了方便用*来查询所有的字段,如果真的需要查询所有的字段也同样建议把所有的字段列出来取代" * "。
10、尽量使用存储过程
优势:
[1]减少网络通信量。调用一个行数不多的存储过程与直接调用SQL语句的网络通信量可能不会有很大的差别,可是如果存储过程包含上百行SQL语句,那么其性能绝对比一条一条的调用SQL语句要高得多。
[2]执行速度更快。有两个原因:首先,在存储过程创建的时候,数据库已经对其进行了一次解析和优化。其次,存储过程一旦执行,在内存中就会保留一份这个存储过程缓存计划,这样下次再执行同样的存储过程时,可以从内存中直接调用。
[3]更强的适应性:由于存储过程对数据库的访问是通过存储过程来进行的,因此数据库开发人员可以在不改动存储过程接口的情况下对数据库进行任何改动,而这些改动不会对应用程序造成影响。
[4]布式工作:应用程序和数据库的编码工作可以分别独立进行,而不会相互压制。
[5]更好的封装移植性。
[6]安全性,它们可以防止某些类型的 SQL 插入攻击。
注:执行存储过程在第一次执行SQL语句时产生,缓存在内存中,这个缓存的计划一直可用,直到 SQL Server 重新启动,或直到它由于使用率较低而溢出内存。
默认情况下,存储过程将返回过程中每个语句影响的行数。如果不需要在应用程序中使用该信息,请在存储过程中使用 :SET NOCOUNT ON 语句以终止该行为。根据存储过程中包含的影响行的语句的数量,这将删除客户端和服务器之间的一个或多个往返过程。尽管这不是大问题,但它可以为高流量应用程序的性能产生负面影响。