c#关于数据库操作的问题合集记录

c#关于数据库操作注意事项

一、垃圾回收的问题

原文链接:C#——垃圾回收(GC)_面向大象编程的博客-CSDN博客_c# 垃圾回收

①托管资源

.NET中的所有类型都是(直接或间接)从System.Object类型派生的。 通用类型系统(CTS)区分两种基本类型:值类型和引用类型。它们之间的根本区别在于它们在内存中的存储方式。.NET使用两种不同的物理内存快来存储数据------栈和托管堆

img

值类型在栈里,先进后出,值类型变量的生命有先后顺序,这个确保了值类型变量在退出作用域以前会释放资源。比引用类型更简单和高效。堆栈是从高地址往低地址分配内存。

引用类型分配在托管堆(Managed Heap)上,声明一个变量在栈上保存,当使用new创建对象时,会把对象的地址存储在这个变量里。托管堆相反,从低地址往高地址分配内存,如图:

img

②非托管资源

ApplicationContext, Brush, Component, ComponentDesigner, Container, Context, Cursor, FileStream, Font, Icon, Image, Matrix, Object, OdbcDataReader, OleDBDataReader, Pen, Regex, Socket, StreamWriter, Timer, Tooltip, 文件句柄, GDI资源, 数据库连接等等资源。

托管资源:并不需要显示释放,但是如果引用类型本身含有非托管资源,则需要进行现实释放。 非托管资源:需要显式释放的,也即需要你写代码释放。

所以一般在.Net中托管资源是不需要我们去手动释放的,我们只需要对非托管资源进行释放即可。

.Net提供了三种释放资源的方法: 1、继承IDisposable接口,使用Dispose()方法:

 public void ExcuteCommand(string connectString, string commandString)
     {
         SqlConnection myConnection = new SqlConnection(connectString);
         SqlCommand myCommand = new SqlCommand(commandString, myConnection);
         myConnection.Open();
         myCommand.ExecuteNonQuery();
         myCommand.Dispose();
         myConnection.Dispose();
 }

确保非托管资源会释放的最好方法是使用using或者try/finally。 在C#中使用using是释放非托管的最好方法,因为在using执行完成的时候,会自动调用Dispose()方法释放资源

using(SqlConnection myConnection = new SqlConnection(connectString)) 
{  
     myConnection.Open(); 
} 

也可以使用try/catch/finally语句块, 确保在finally块中关闭任何已打开的连接。

try
{
    SqlConnection myConnection = new SqlConnection(connectString);
    myConnection.Open();
}
catch
{
   //
}
finally
{   
    undefined
    myConnection.Dispose();
}

二、关于自增段

①设置列值自增

以sql举例,打开SSMS(数据库管理可视化界面软件)首先设置是否是标识量,标识种子代表每次增加的大小,一般自增段设置为1。

②在c#程序中获取插入列的自增id

1.这里强调一个返回值的概念,我们可以通过插入语句的返回值来获取。

2.在使用此格式时,values()中的值必须与表中的所有字段建立一一对应关系,自增字段除外

SELECT @ScopeId=SCOPE_IDENTITY();
Set @ScopeId=SCOPE_IDENTITY();
        
public bool Add(TaskDetailInfo t1, ref int taskId)
        {
            StringBuilder sql = new StringBuilder();
            sql.Append("insert into TaskDetailInfo (operName, devNum, devSeq, time, operType) values (@operName, @devNum, @devSeq, @time, @operType)");
            sql.Append(" select @taskId = SCOPE_IDENTITY();");
            SqlParameter[] parameter =
            {
                new SqlParameter("@operName", SqlDbType.VarChar),
                new SqlParameter("@devNum", SqlDbType.Int),
                new SqlParameter("@devSeq", SqlDbType.VarChar),
                new SqlParameter("@time", SqlDbType.DateTime),
                new SqlParameter("@taskId", SqlDbType.Int),
                new SqlParameter("@operType", SqlDbType.VarChar),
            };
            parameter[0].Value = t1.operName;
            parameter[1].Value = t1.devNum;
            parameter[2].Value = t1.devSeq;
            parameter[3].Value = t1.time;
            parameter[4].Direction = ParameterDirection.Output; //输出当前插入的taskid值,作为之后仪器的设置参数表格某列元素
            parameter[5].Value = t1.operType;

            int rows = SqlDBHelper.ExecuteSql(sql.ToString(), parameter);

            taskId = (int)parameter[4].Value;

            return rows > 0 ? true : false;
        }

③SCOPE_IDENTITY()会更好

SQL Server 2000中,有三个比较类似的功能:SCOPE_IDENTITY、IDENT_CURRENT 和 @@IDENTITY,它们都返回插入到 IDENTITY 列中的值。

1)IDENT_CURRENT 返回为任何会话和任何作用域中的特定表最后生成的标识值,它不受作用域和会话的限制,而受限于所指定的表。 2)@@IDENTITY返回为当前会话的所有作用域中的任何表最后生成的标识值。 3) SCOPE_IDENTITY 返回为当前会话和当前作用域中的任何表最后生成的标识值。

SCOPE_IDENTITY 和 @@IDENTITY 返回在当前会话中的任何表内所生成的最后一个标识值。但是,SCOPE_IDENTITY 只返回插入到当前作用域中的值;@@IDENTITY 不受限于特定的作用域。 例如,有两个表 T1 和 T2,在 T1 上定义了一个 INSERT 触发器。当将某行插入 T1 时,触发器被激发,并在 T2 中插入一行。此例说明了两个作用域:一个是在 T1 上的插入,另一个是作为触发器的结果在 T2 上的插入。

假设 T1 和 T2 都有 IDENTITY 列,@@IDENTITY 和 SCOPE_IDENTITY 将在 T1 上的 INSERT 语句的最后返回不同的值。@@IDENTITY 返回插入到当前会话中任何作用域内的最后一个 IDENTITY 列值,该值是插入 T2 中的值。

SCOPE_IDENTITY() 返回插入 T1 中的 IDENTITY 值,该值是发生在相同作用域中的最后一个 INSERT。如果在作用域中发生插入语句到标识列之前唤醒调用 SCOPE_IDENTITY() 函数,则该函数将返回 NULL 值。

而IDENT_CURRENT('T1') 和 IDENT_CURRENT('T2') 返回的值分别是这两个表最后自增的值。

ajqc的实验:(40条本地线程,40+40条远程线程同时并发测试,插入1200W行),得出的结论是:

1.在典型的级联应用中.不能用@@IDENTITY,在CII850,256M SD的机器上1W多行时就会并发冲突.在P42.8C,512M DDR上,才6000多行时就并发冲突. 2.SCOPE_IDENTITY()是绝对可靠的,可以用在存储过程中,连触发器也不用建,没并发冲突 SELECT IDENT_CURRENT('TableName') --返回指定表中生成的最后一个标示值 SELECT IDENT_INCR('TableName')--返回指定表的标示字段增量值 SELECT IDENT_SEED('TableName')--返回指定表的标示字段种子值

④set和select的方法区别

【转】基于SQL中SET与SELECT赋值的区别详解 

原文链接:sql 中set和select区别 - hello,逗比 - 博客园

1、SELECT可以在一条语句里对多个变量同时赋值,而SET只能一次对一个变量赋值,如下:

代码如下: SELECT @VAR1=‘Y’,@VAR2=‘N’ – 而SET要达到同样的效果,需要: SET @VAR1=‘Y’ SET @VAR2=‘N’ /* 说到这个,SQL内置的变量:@@ERROR 和 @@ROWCOUNT必须要在一句SQL语句中捕获。如果用set分两句来获取它们,将获取不完整,这时就应该用select来获取值。 */ 2、表达式返回多个值时,用SET将会出错,而SELECT将取最后一个值,如下:

代码如下: ----以下假定Permission表有多个IsRight记录 SELECT @VAR1 = IsRight FROM Permission --将取最后一个值

SET @VAR1 = IsRight FROM Permission --将报错

3、表达式无返回值时,用SET将置变量值为NULL,用SELECT交保持变量值,如下:

代码如下: ----以下假定Permission记录为空 SET @VAR1 = ‘初始值’

SELECT @VAR1 = IsRight FROM Permission --此时@VAR1为’初始值’

SET @VAR1 = (SELECT IsRight FROM Permission) --此时@VAR1为NULL

4、使用标量子查询时,如果无返回值,SET和SELECT一样,都将置为NULL,如下:

代码如下: ----以下假定Permission记录为空 SET @VAR1 = ‘初始值’

SELECT @VAR1 =(SELECT IsRight FROM Permission ) --此时@VAR1为NULL

SET @VAR1 = ( SELECT IsRight FROM Permission) --此时@VAR1为NULL

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值