文档章节

SQL Server 致程序员(容易忽略的错误)

donny945
 donny945
发布于 2014/10/14 14:03
字数 4504
阅读 154
收藏 1

概述

因为每天需要审核程序员发布的SQL语句,所以收集了一些程序员的一些常见问题,还有一些平时收集的其它一些问题,这也是很多人容易忽视的问题,在以后收集到的问题会补充在文章末尾,欢迎关注,由于收集的问题很多是针对于生产数据,测试且数据量比较大,这里就不把数据共享出来了,大家理解意思就行。

 

步骤

大小写

大写T-SQL 语言的所有关键字都使用大写,规范要求。

使用“;”

使用“;”作为 Transact-SQL 语句终止符。虽然分号不是必需的,但使用它是一种好的习惯,对于合并操作MERGE语句的末尾就必须要加上“;”

(cte表表达式除外)

数据类型

避免使用ntext、text 和 image 数据类型,用 nvarchar(max)、varchar(max) 和 varbinary(max)替代

后续版本会取消ntext、text 和 image 该三种类型

 

查询条件不要使用计算列

复制代码
例如year(createdate)=2014,使用createdate>=20140101and createdate<=20141231’来取代。 
复制代码
IF OBJECT_ID('News','U') IS NOT NULL DROP TABLE News GO CREATE TABLE News
(ID INT NOT NULL PRIMARY KEY IDENTITY(1,1),
NAME NVARCHAR(100) NOT NULL,
Createdate DATETIME NOT NULL ) GO CREATE NONCLUSTERED INDEX [IX1_News] ON [dbo].[News] ( [Createdate] ASC )
INCLUDE ( [NAME]) WITH (STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] GO GO INSERT INTO News(NAME,Createdate) VALUES( '新闻','2014-08-20 00:00:00'),( '新闻','2014-08-20 00:00:00'),( '新闻','2014-08-20 00:00:00'),( '新闻','2014-08-20 00:00:00')
复制代码

---使用计算列查询(走的是索引扫描)

SELECT ID,NAME,Createdate FROM News WHERE YEAR(Createdate)=2014

---不使用计算列(走的是索引查找)

SELECT ID,NAME,Createdate FROM News WHERE CreateDate>='2014-01-01 00:00:00' and CreateDate<'2015-01-01 00:00:00'

对比两个查询显然绝大部分情况下走索引查找的查询性能要高于走索引扫描,特别是查询的数据库不是非常大的情况下,索引查找的消耗时间要远远少于索引扫描的时间,如果想详细了解索引的体系结构可以查看了我前面写的几篇关于聚集、非聚集、堆的索引体系机构的文章。

复制代码

 

请参看:http://www.cnblogs.com/chenmh/p/3780221.html

 请参看:http://www.cnblogs.com/chenmh/p/3782397.html

 

建表时字段不允许为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);
复制代码

 

分组统计时避免使用count(*)

复制代码
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);
例如:需要统计每一个顾客的订单数量 ---如果使用count(*) SELECT Customerid,COUNT(*) FROM Customer TA LEFT JOIN OrderS TB ON TA.Customerid=TB.custid GROUP BY Customerid ;

实际情况customerid=3是没有订单的,数量应该是0,但是结果是1

----正确的方法是使用count(custid) SELECT Customerid,COUNT(custid) FROM Customer TA LEFT JOIN OrderS TB ON TA.Customerid=TB.custid GROUP BY Customerid;

 

复制代码

 

子查询的表加上表别名

复制代码
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 Customer WHERE Customerid IN(SELECT Customerid FROM OrderS WHERE Orderid=2 );


正确查询结果下查询出的结果是没有customerid为3的值

为什么结果会这样呢?

大家仔细看应该会发现子查询的orders表中没有Customerid字段,所以SQL取的是Customer表的Customerid值作为相关子查询的匹配字段。

所以我们应该给子查询加上表别名,如果加上表别名,如果字段错误的话会有错误标示

 正确的写法:

SELECT Customerid FROM Customer WHERE Customerid IN(SELECT tb.custid FROM OrderS tb WHERE Orderid=2 );

建立自增列时单独再给自增列添加唯一约束

复制代码
复制代码
USE tempdb 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 tempdb.DBO.TEST(NAME) VALUES('A中'),('a名'),('Aa'),('ab'),('AA'),('az'); ----4.插入报错后,自增值依旧增加 INSERT INTO tempdb.DBO.TEST(NAME) VALUES(''); GO SELECT IDENT_CURRENT('tempdb.DBO.TEST'); SELECT * FROM tempdb.DBO.TEST; ---插入正常的数据 INSERT INTO tempdb.DBO.TEST(NAME) VALUES('cc'); SELECT IDENT_CURRENT('tempdb.DBO.TEST') SELECT * FROM tempdb.DBO.TEST; ----5.显示插入自增值 SET IDENTITY_INSERT tempdb.DBO.TEST ON INSERT INTO tempdb.DBO.TEST(ID,NAME) VALUES(8,'A中'); SET IDENTITY_INSERT tempdb.DBO.TEST OFF ----会发现ID并不是根据自增值排列的,而且根据插入的顺序排列的 SELECT IDENT_CURRENT('tempdb.DBO.TEST'); SELECT * FROM tempdb.DBO.TEST; ----6.插入重复的自增值 SET IDENTITY_INSERT tempdb.DBO.TEST ON INSERT INTO tempdb.DBO.TEST(ID,NAME) VALUES(8,'A中'); SET IDENTITY_INSERT tempdb.DBO.TEST OFF SELECT IDENT_CURRENT('tempdb.DBO.TEST') SELECT * FROM tempdb.DBO.TEST; ---所以如果要保证ID是唯一的,单单只设置自增值不行,需要给字段设置主键或者唯一约束 DROP TABLE tempdb.DBO.TEST;
复制代码

 

复制代码

 

查询时一定要制定字段查询

l  查询时一定不能使用”*”来代替字段来进行查询,无论你查询的字段有多少个,就算字段太多无法走索引也避免了解析”*”带来的额外消耗。

l  查询字段值列出想要的字段,避免出现多余的字段,字段越多查询开销越大而且可能会因为多列出了某个字段而引起查询不走索引。

创建测试数据库

复制代码
CREATE TABLE [Sales].[Customer]( [CustomerID] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL, [PersonID] [int] NULL, [StoreID] [int] NULL, [TerritoryID] [int] NULL, [AccountNumber] AS (isnull('AW'+[dbo].[ufnLeadingZeros]([CustomerID]),'')), [rowguid] [uniqueidentifier] ROWGUIDCOL NOT NULL, [ModifiedDate] [datetime] NOT NULL, CONSTRAINT [PK_Customer_CustomerID] PRIMARY KEY CLUSTERED ( [CustomerID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]
复制代码

创建索引

复制代码
CREATE NONCLUSTERED INDEX [IX1_Customer] ON [Sales].[Customer] ( [PersonID] ASC )
INCLUDE ( [StoreID], [TerritoryID], [AccountNumber], [rowguid]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] GO
复制代码

查询测试

复制代码
---使用SELECT  * 查询 SET STATISTICS IO ON SET STATISTICS TIME ON SELECT * FROM [Sales].[Customer] WHERE PersonID=1; SET STATISTICS TIME OFF SET STATISTICS IO OFF
复制代码

由于建的索引‘IX1_Customer’没有包含ModifiedDate字段,所以需要通过键查找去聚集索引中获取该字段的值

复制代码
---列出需要的字段查询,因为字段不包含不需要的列,所以走索引 SET STATISTICS IO ON SET STATISTICS TIME ON SELECT CustomerID, [PersonID] ,[StoreID] ,[TerritoryID] ,[AccountNumber] ,[rowguid] FROM [Sales].[Customer] WHERE PersonID=1; SET STATISTICS TIME OFF SET STATISTICS IO OFF
复制代码

由于查询语句中没有对ModifiedDate字段进行查询,所以只走索引查找就可以查询到需要的数据,所以建议在查询语句中列出你需要的字段而不是为了方便用*来查询所有的字段,如果真的

需要查询所有的字段也同样建议把所有的字段列出来取代‘*’。

 

使用存储过程的好处

  1. 减少网络通信量。调用一个行数不多的存储过程与直接调用SQL语句的网络通信量可能不会有很大的差别,可是如果存储过程包含上百行SQL语句,那么其性能绝对比一条一条的调用SQL语句要高得多。
  2. 执行速度更快。有两个原因:首先,在存储过程创建的时候,数据库已经对其进行了一次解析和优化。其次,存储过程一旦执行,在内存中就会保留一份这个存储过程缓存计划,这样下次再执行同样的存储过程时,可以从内存中直接调用。
  3. 更强的适应性:由于存储过程对数据库的访问是通过存储过程来进行的,因此数据库开发人员可以在不改动存储过程接口的情况下对数据库进行任何改动,而这些改动不会对应用程序造成影响。
  4. 布式工作:应用程序和数据库的编码工作可以分别独立进行,而不会相互压制。
  5. 更好的封装移植性。
  6. 安全性,它们可以防止某些类型的 SQL 插入攻击。
复制代码
PROCEDURE [dbo].[SPSalesPerson] (@option varchar(50)) AS BEGIN SET NOCOUNT ON IF @option='select' BEGIN SELECT [DatabaseLogID] ,[PostTime] ,[DatabaseUser] ,[Event] ,[Schema] ,[Object] ,[TSQL] ,[XmlEvent] FROM [dbo].[DatabaseLog] END IF @option='SalesPerson' BEGIN SELECT [BusinessEntityID] ,[TerritoryID] ,[SalesQuota] ,[Bonus] ,[CommissionPct] ,[SalesYTD] ,[SalesLastYear] ,[rowguid] ,[ModifiedDate] FROM [Sales].[SalesPerson] WHERE BusinessEntityID<300 END SET NOCOUNT OFF END
复制代码
复制代码
EXEC SPSalesPerson @option='select' EXEC SPSalesPerson @option='SalesPerson' DBCC FREEPROCCACHE----清空缓存 ---测试两个查询是否都走了缓存计划 SELECT usecounts,size_in_bytes,cacheobjtype,objtype,TEXT FROM sys.dm_exec_cached_plans  cp CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) st; --执行计划在第一次执行SQL语句时产生,缓存在内存中,这个缓存的计划一直可用,直到 SQL Server 重新启动,或直到它由于使用率较低而溢出内存。  默认情况下,存储过程将返回过程中每个语句影响的行数。如果不需要在应用程序中使用该信息(大多数应用程序并不需要),请在存储过程中使用 SET NOCOUNT ON 语句以终止该行为。根据存储过程中包含的影响行的语句的数量,这将删除客户端和服务器之间的一个或多个往返过程。尽管这不是大问题,但它可以为高流量应用程序的性能产生负面影响。
复制代码

判断一条查询是否有值

复制代码
--以下四个查询都是判断连接查询无记录时所做的操作 ---性能最差消耗0.8秒 SET STATISTICS IO ON SET STATISTICS TIME ON DECLARE @UserType INT ,@Status INT SELECT @UserType=COUNT(c.Id) FROM Customerfo t INNER JOIN Customer c ON c.Id=t.CustomerId WHERE c.customerTel='13400000000' IF(@UserType=0) BEGIN SET @Status = 2 PRINT @Status END SET STATISTICS TIME OFF SET STATISTICS IO OFF go ----性能较好消耗0.08秒  SET STATISTICS IO ON SET STATISTICS TIME ON IF NOT EXISTS(SELECT c.Id FROM Customerfo t INNER JOIN Customer c ON c.Id=t.CustomerId WHERE c.customerTel='13400000000') BEGIN DECLARE @Status int SET @Status = 2 PRINT @Status END SET STATISTICS TIME OFF SET STATISTICS IO OFF go ----性能较好消耗0.08秒  SET STATISTICS IO ON SET STATISTICS TIME ON IF NOT EXISTS(SELECT top 1 c.id FROM Customerfo t INNER JOIN Customer c ON c.Id=t.CustomerId WHERE c.customerTel='13400000000' ORDER BY NEWID() ) BEGIN DECLARE @Status int SET @Status = 2 PRINT @Status END SET STATISTICS TIME OFF SET STATISTICS IO OFF GO ---性能和上面的一样0.08秒 SET STATISTICS IO ON SET STATISTICS TIME ON IF NOT EXISTS(SELECT 1 FROM Customerfo t INNER JOIN Customer c ON c.Id=t.CustomerId WHERE c.customerTel='13410700660' ) BEGIN DECLARE @Status int SET @Status = 2 PRINT @Status END SET STATISTICS TIME OFF SET STATISTICS IO OFF 这里说一下SELECT 1,之前因为有程序员误认为查询SELECT 1无论查询的数据有多少只返回一个1,其实不是这样的,和查询字段是一样的意思只是有多少记录就返回多少个1,1也不是查询的第一个字段。
复制代码

 理解TRUNCATE和DELETE的区别

复制代码
---创建表Table1 IF OBJECT_ID('Table1','U') IS NOT NULL DROP TABLE Table1 GO CREATE TABLE Table1
(ID INT NOT NULL,
FOID INT NOT NULL) GO --插入测试数据 INSERT INTO Table1 VALUES(1,101),(2,102),(3,103),(4,104) GO ---创建表Table2 IF OBJECT_ID('Table2','U') IS NOT NULL DROP TABLE Table2 GO CREATE TABLE Table2
(
FOID INT NOT NULL) GO
--插入测试数据
INSERT INTO Table2 VALUES(101),(102),(103),(104) GO SELECT * FROM Table1 GO SELECT * FROM Table2 GO 
在Table1表中创建触发器,当表中的数据被删除时同时删除Table2表中对应的FOID
复制代码
CREATE TRIGGER TG_Table1 ON Table1
AFTER DELETE AS BEGIN DELETE FROM TA FROM Table2 TA INNER JOIN deleted TB ON TA.FOID=TB.FOID END GO
复制代码
复制代码
---测试DELETE删除操作 DELETE FROM Table1 WHERE ID=1 GO ---执行触发器成功,Table2表中的FOID=101的数据也被删除 SELECT * FROM Table1 GO SELECT * FROM Table2
复制代码
复制代码
---测试TRUNCATE删除操作 TRUNCATE TABLE Table1 GO ---Table2中的数据没有被删除 SELECT * FROM Table1 GO SELECT * FROM Table2
复制代码

复制代码
复制代码
---查看TRUNCATE和DELETE的日志记录情况 CHECKPOINT GO SELECT * FROM fn_dblog(NULL,NULL) GO DELETE FROM Table2 WHERE FOID=102 GO SELECT * FROM fn_dblog(NULL,NULL)
复制代码

在第四行记录有一个lop_delete_rows,lcx_heap的删除操作日志记录
复制代码
----TRUNCATE日志记录 CHECKPOINT GO SELECT * FROM fn_dblog(NULL,NULL) GO TRUNCATE TABLE Table2 GO SELECT * FROM fn_dblog(NULL,NULL) GO
复制代码

 TRUNCATE操作没有记录删除日志操作

主要的原因是因为TRUNCATE操作不会激活触发器,因为TRUNCATE操作不会记录各行的日志删除操作,所以当你需要删除一张表的数据时你需要考虑是否应该如有记录日志删除操作,而不是根据个人的习惯来操作。

 

事务的理解

复制代码
---创建表Table1 IF OBJECT_ID('Table1','U') IS NOT NULL DROP TABLE Table1 GO CREATE TABLE Table1
(ID INT NOT NULL PRIMARY KEY,
Age INT NOT NULL CHECK(Age>10 AND Age<50)); GO ---创建表Table2 IF OBJECT_ID('Table2','U') IS NOT NULL DROP TABLE Table2 GO CREATE TABLE Table2
(
ID INT NOT NULL) GO
复制代码

1.简单的事务提交

复制代码
BEGIN TRANSACTION INSERT INTO Table1(ID,Age) VALUES(1,20) INSERT INTO Table1(ID,Age) VALUES(2,5) INSERT INTO Table1(ID,Age) VALUES(2,20) INSERT INTO Table1(ID,Age) VALUES(3,20) COMMIT TRANSACTION GO ---第二条记录没有执行成功,其他的都执行成功 SELECT * FROM Table1
所以并不是事务中的任意一条语句报错整个事务都会回滚,其它的可执行成功的语句依然会执行成功并提交。
复制代码

2.TRY...CATCH

复制代码
DELETE FROM Table1 BEGIN TRY BEGIN TRANSACTION INSERT INTO Table1(ID,Age) VALUES(1,20) INSERT INTO Table1(ID,Age) VALUES(2,20) INSERT INTO Table1(ID,Age) VALUES(3,20) INSERT INTO Table3 VALUES(1) COMMIT TRANSACTION END TRY BEGIN CATCH ROLLBACK TRANSACTION END CATCH ----重新打开一个回话执行查询,发现由于存在对象出错BEGIN CATCH并没有收到执行报错,且事务一直处于打开状态,没有被提交,也没有执行回滚。 SELECT * FROM Table1 ---如果事务已经提交查询XACT_STATE()的状态值是0,或者执行DBCC OPENTRAN SELECT XACT_STATE() DBCC OPENTRAN ---手动执行提交或者回滚操作 ROLLBACK TRANSACTION 
复制代码
TRY...CATCH不会返回对象错误或者字段错误等类型的错误

想详细了解TRY...CATCH请参考http://www.cnblogs.com/chenmh/articles/4012506.html

 

3.打开XACT_ABORT

复制代码
SET XACT_ABORT ON BEGIN TRANSACTION INSERT INTO Table1(ID,Age) VALUES(1,20) INSERT INTO Table1(ID,Age) VALUES(2,20) INSERT INTO Table1(ID,Age) VALUES(3,20) INSERT INTO Table3 VALUES(1) COMMIT TRANSACTION SET XACT_ABORT OFF ---事务全部执行回滚操作(对象table3是不存在报错,但是也回滚所有的提交,跟上面的TRY...CATCH的区别) SELECT * FROM Table1 
复制代码

复制代码
---查询是否有打开事务 SELECT XACT_STATE() DBCC OPENTRAN
未查询到有打开事务

当 SET XACT_ABORT 为 ON 时,如果执行 Transact-SQL 语句产生运行时错误,则整个事务将终止并回滚。

当 SET XACT_ABORT 为 OFF 时,有时只回滚产生错误的 Transact-SQL 语句,而事务将继续进行处理。如果错误很严重,那么即使 SET XACT_ABORT 为 OFF,也可能回滚整个事务。OFF 是默认设置。

编译错误(如语法错误)不受 SET XACT_ABORT 的影响。

复制代码

      所以我们应该根据自己的需求选择正确的事务。

    

修改字段NOT NULL的过程

复制代码
在Address表中的有一个Address字段,该字段允许为NULL,现在需要将其修改为NOT NULL. BEGIN TRANSACTION SET QUOTED_IDENTIFIER ON SET ARITHABORT ON SET NUMERIC_ROUNDABORT OFF SET CONCAT_NULL_YIELDS_NULL ON SET ANSI_NULLS ON SET ANSI_PADDING ON SET ANSI_WARNINGS ON COMMIT BEGIN TRANSACTION GO CREATE TABLE dbo.Tmp_Address
    (
    ID int NOT NULL,
    Address nvarchar(MAX) NOT NULL ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO ALTER TABLE dbo.Tmp_Address SET (LOCK_ESCALATION = TABLE) GO IF EXISTS(SELECT * FROM dbo.Address) EXEC('INSERT INTO dbo.Tmp_Address (ID, Address)
        SELECT ID, Address FROM dbo.Address WITH (HOLDLOCK TABLOCKX)') GO DROP TABLE dbo.Address GO EXECUTE sp_rename N'dbo.Tmp_Address', N'Address', 'OBJECT' GO COMMIT ---从上面就是一个重置字段为非空的过程,从上面的语句我们可以看到首先要创建一张临时表在临时表中Address字段建成了NOT NULL,然后将原表中的数据插入到临时表当中,最后修改表名,大家可以想一下如果我要修改的表有几千万数据,那这个过程该多么长而且内存一下子就会增加很多,所以大家建表的时候就要养成设字段为NOT NULL --当你要向现有的表中增加一个字段的时候你也要不允许为NULL,可以用默认值替代空 Alter Table Address Add MemberType smallint Not Null Default (1)
复制代码

 

 

总结

后面收集到类似的问题会补充在文章的末尾,文章持续更新中....,欢迎关注讨论。

  

如果觉得文章对大家有所帮助,麻烦给个推荐,谢谢!!

 

备注:

    作者:pursuer.chen

    博客:http://www.cnblogs.com/chenmh

本站点所有文章都是原创,欢迎大家转载;但转载时必须注明文章来源,且在文章开头明显处给明链接,否则保留追究责任的权利。

《欢迎交流讨论》

本文转载自:

donny945
粉丝 9
博文 201
码字总数 33875
作品 0
海淀
程序员
私信 提问
SQL Server -- 数据库开发的二十一条军规

http://www.cnblogs.com/yank/archive/2008/01/08/1030486.html 如果你正在负责一个基于SQL Server的项目,或者你刚刚接触SQL Server,你都有可能要面临一些数据库性能的问题,这篇文章会为你...

chenhao_asd
2018/04/15
0
0
关于如何形成一个好的数据库设计

如果你正在负责一个基于SQL Server的项目,或者你刚刚接触SQL Server,你都有可能要面临一些数据库性能的问题,这篇文章会为你提供一些有用的指导(其中大多数也可以用于其它的DBMS)。在这里...

陶邦仁
2012/11/05
3.8K
13
微软SQL Server密码管理六个危险判断

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/chinahuyong/article/details/2563061 微软SQL Server密码管理六个危险判断 当管理SQL Server内在的帐户和密码...

rdiframework
2008/06/18
0
0
10件你可能不知道的关于PHP的事

小编之前也曾报导过PHP开发人员容易忽略的几点精华,除了一些精华技术方法外,很多细微之处也是程 序员们容易忽略的,下面我们为您总结了10个关于PHP你可能不知道的事情。 关于PHP更多内容,欢...

modernizr
2014/07/01
405
6
10件你可能不知道的关于PHP的事

小编之前也曾报导过PHP开发人员容易忽略的几点精华,除了一些精华技术方法外,很多细微之处也是程 序员们容易忽略的,下面我们为您总结了10个关于PHP你可能不知道的事情。 关于PHP更多内容,欢...

小编辑
2010/05/20
1K
2

没有更多内容

加载失败,请刷新页面

加载更多

Spring Cloud Alibaba 实战(二) - 关于Spring Boot你不可不知道的实情

0 相关源码 1 什么是Spring Boot 一个快速开发的脚手架 作用 快速创建独立的、生产级的基于Spring的应用程序 特性 无需部署WAR文件 提供starter简化配置 尽可能自动配置Spring以及第三方库 ...

JavaEdge
今天
7
0
TensorFlow 机器学习秘籍中文第二版(初稿)

TensorFlow 入门 介绍 TensorFlow 如何工作 声明变量和张量 使用占位符和变量 使用矩阵 声明操作符 实现激活函数 使用数据源 其他资源 TensorFlow 的方式 介绍 计算图中的操作 对嵌套操作分层...

ApacheCN_飞龙
今天
7
0
五、Java设计模式之迪米特原则

定义:一个对象应该对其他对象保持最小的了解,又叫最小知道原则 尽量降低类与类之间的耦合 优点:降低类之间的耦合 强调只和朋友交流,不和陌生人说话 朋友:出现在成员变量、方法的输入、输...

东风破2019
昨天
23
0
jvm虚拟机结构

1:jvm可操作数据类型分为原始类型和引用类型,因此存在原始值和引用值被应用在赋值,参数,返回和运算操作中,jvm希望在运行时 明确变量的类型,即编译器编译成class文件需要对变量进行类型...

xpp_ba
昨天
5
0
聊聊nacos Service的processClientBeat

序 本文主要研究一下nacos Service的processClientBeat Service.processClientBeat nacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/core/Service.java public class Service ex......

go4it
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部