zoukankan      html  css  js  c++  java
  • 数据库使用Command对象进行数据库查询

     

    ADO.NET中使用Command对象来与数据库进行查询.

    一、创建Command对象

    有三种方式创建Command对象:

    1.7         第一种使用New关键字,调用Command对象的无参构造子,再进行属性设置

    1.8         第二种也是使用New关键字,但使用的构造子提供参数来指定查询字符串和Connection对象

    1.9         第三种通过Connection对象的CreateCommand方法

    二、无返回查询

    1          操作查询

    不返回结果集的查询通常称为操作查询(action query)

    2          操作查询有2种主要类型

    2.1         使用数据操纵语言(DML)查询

    也称为基于查询的更新,该类型会对数据库进行更新

    1

    Update Customers SET CompanyName='NewCompanyName' WHERE CustomerID='ALFKI'

    2

    INSERT INTO Customers (CustomerID,CompanyName) VALUES('NewID','NewCustomer')

    3

    DELETE FROM Customers WHERE CustomerID='ALFKI'

    2.2         数据定义语言(DDL)查询

    此种类型常用于更改数据库结构

    1

    CREATE TABLE table1 (Field1 int NOT NULL CONSTRAINT PK_Table1 PRIMARY KEY,

                                              Field2 varchar(32))

    2

    ALTER VIEW View1 AS SELECT Field1,Field2 FROM table1

    3

    DROP PROCEDURE StoredProcedure1

    3          VBNET中使用无返回查询代码演示

    Dim cn As New OleDbConnection()

    cn.ConnectionString=<>

    cn.Open()

    Dim cmd As OleDbCommand=cn.CreateCommand()

    cmd.CommandText="UPDATE Customers SET CompanyName='NewCompanyName' "&_

                                       " WHERE CustomerID='ALFKI'"

    cmd.ExecuteNonQuery()

    4          关于"无返回查询"(操作查询)的讨论

    4.1         操作查询是一种很有效的查询.只是它们不返回行.

    4.2         执行操作查询可能会导致失败.例如:

    CREATE TABLE table1 (Field1 int NOT NULL CONSTRAINT PK_Table1 PRIMARY KEY,

                                              Field2 varchar(32))

    1.1.7                    该查询失败的原因:

    l         已经存在同名的表格

    l         查询语法错误

    l         没有与数据库之间的开放连接

    1.1.7                    如果执行的查询没有发生错误,那么就意味着成功的创建了新的表格

    4.3         由于操作查询是设计用来修改或删除现有行的,更新的操作也可能导致失败

    UPDATE Customers SET CompanyName='NewCompanyName' WHERE CustomerID='ALFKI'

    1.1.7                    无法正确执行可能会因为该行已经被另外一个用户从表中删除.

    数据库能够执行该查询,但是由于没有满足WHERE子句条件的行,所以查询不会作出修改行为.

    对于数据库来说,这样的结果并不表示失败.

    1.        如何辨别查询是否修改了行呢?

    Sql Server的查询分析器中执行,会得到信息:(1 rows affected)

    Command对象则将查询所影响的行数作为ExecuteNonQuery方法的返回值.

    2.        代码演示

    Dim strConn As String=<连接字符串->

    Dim cn As New OleDbConnection(strConn)

    cn.Open()

    Dim cmd As OleDbCommand=cn.CreateCommand()

    cmd.CommandText="UPDATE Customers SET CompanyName='NewCompanyName' " &_

                                       "WHERE CustomerID='ALFKI'"

    Dim recAffected As Integer=cmd.ExecuteNonQuery()

    If recAffected=1 Then

           Console.WriteLine("Update Succeeded")

    Else

           Console.WriteLine("Update Failed")

    End If

    4.4         特别注意

    如果执行的是非DML查询,那么ExecuteNonQuery方法返回值是-1.

    三、用DataReader对象检查查询结果

    1.        执行一个返回结果集的查询

    Command对象提供ExecuteReader方法,返回值是DataReader对象,可以通过该对象检查查询结果

    2.        DataReader对象与.NET其他读取程序的对象(XMLReader/TextReader/StreamReader)使用起来没有区别

    3.        代码演示

    ...(省略-打开Connection对象cn的过程)...

    Dim cmd As New OleDbCommand("SELECT CustomerID,CompanyName FROM Customers",cn)

    Dim rdr as OleDbDataReader=cmd.ExecuteReader()

    While rdr.read()

           Console.WriteLine(rdr("CustomerID") & "-" & rdr("CompanyName"))

    End While

    rdr.Close()

    1.1         解释

    1.1.1                    关于DataReader对象的Read方法

    只有在你调用Read方法之后,数据的第一行才可用,所以循环中要在显示之前就调用Read方法

    调用一次Read方法,DataReader会自动移动到下一行.

    Read方法返回值为布尔类型,表示下一行是否可用.

    1.1.2                    更快的获取结果集内容

    DataReader对象的Item属性(这是个默认参数化属性)

    Console.WriteLine(rdr("CustomerID") & "-" & rdr("CompanyName"))

    这里就使用了Item属性,这样的代码效率较低.

    1.        提高此处代码性能的办法

    1.1.2.1.1                                使用基于序号的查找

    前面的代码段使用列名称查找Item属性,这将引发结果集中每一行都执行一个基于字符串的查找.

    如果改为提供列的索引或序号,将避免对字符串的查找,从而提高性能.

    此项技巧可应用于所以公开集合的对象.

    DataReader对象可以根据列名称取得其索引(GetOrdinal方法).这样你可以使用它来在循环之前取得列名在集合中的索引值,从而加速查找性能.

    l         修改后代码演示

    Dim rdr As OleDbDataReader=cmd.ExecuteReader()

    Dim customerIDOrd As Integer=rdr.GetOrdinal(“CustomerID”)

    Dim CompanyNameOrd As Integer=rdr.GetOrdinal(“CompanyName”)

    While rdr.Read()

           Console.WriteLine(rdr(customerIDOrd) & “_” & rdr(companyNameOrd))

    End While

    rdr.Close()

    1.1.2.1.2                                使用适当的类型指定Get方法

    DataReader对象提供了一系列的方法可以用来返回符合.NET类型的数据
    (
    包括:String/Int32/decimal/Double)

    大多数集合对象都隐含使用了Item属性,其返回值是Object类型.显示其中所存储的值一般需要转换为字符串(ToString方法)

    然而我们也可以使用DataReader对象提供的GetXXX方法来指定返回值类型.

    应该保持一直使用与结果集中列返回的数据相对应的类型指定的Get方法的好习惯,即使我们需要在界面控件中使用字符串,也不例外

    l         修改后代码演示

    Dim rdr As OleDbDataReader=cmd.ExecuteReader()

    Dim CustomerIDOrd As Integer=rdr.GetOrdinal(“CustomerID”)

    Dim CompanyNameOrd As Integer=rdr.GetOrdinal(“CompantName”)

    While rdr.Read()

    Console.WriteLine(rdr.GetString(CustomerIDOrd) & “_” &
     rdr.GetString(CompanyNameOrd))

    End While

    1.1.3                    获取多个结果-批查询

    某些数据库(比如SQL SERVER)支持执行返回多个结果的批查询.

    假设有如下查询:

           SELECT CustomerID,CompanyName,ContactName,Phone FROM Customers;

           SELECT OrderID,CustomerID,EmployeeID,OrderData FROM Orders;

           SELECT OrderID,ProcedureID,Quantity,UnitPrice FROM [Order Details]

    使用前面的方法,DataReader对象在循环中Read(),只能在批查询中循环第一个查询的结果.

    解决这个问题需要使用DataReader对象提供的NextResult方法,该方法可以使你移动到下一个返回行的查询结果上去.另外,该方法返回一个布尔类型,但是它不可以向Read方法那样在循环之前调用.

    l         使用NextResult()的代码演示

    cn.Open()

    Dim ssql as string=” SELECT CustomerID,CompanyName,ContactName,Phone FROM Customers;

           SELECT OrderID,CustomerID,EmployeeID,OrderData FROM Orders;

           SELECT OrderID,ProcedureID,Quantity,UnitPrice FROM [Order Details]”

    Dim cmd as new OleDbCommand(ssql,cn)

    Dim rdr as OleDbDataReader=cmd.ExecuteReader()

    Do

           Do While rdr.Read()

                  Console.WriteLine(rdr(0) & “_” & rdr(1))

           Loop

           Console.WriteLine()

    Loop While rdr.NextResult()

    1.1.4                    执行成批的操作查询

    使ADOSQL SERVER来获取由存储过程生成的结果集经常会存在以下问题.

    调用使用SQL Server OLE DB提供者的Sql Server存储过程,并且此存储过程在返回行的查询之前执行了一次查询,那么Recordset会被标记为关闭”,从而导致返回的记录集不包含返回行的查询结果.

    这个问题是有设计造成的.

    解决办法有两个:

    1.        使用NextRecordset方法

    DataReader对象的这个方法能使(当前光标)移动到下一查询的结果上去.

    因为:关闭的Recordset与查询操作相对应.更严格的说它是与查询所返回的信息性消息影响X相对应的

    2.        在存储过程中添加SET NOCOUNT ON语句

    这个语句的作用是取消影响N消息,从而能够是ADO(光标)立即移动到第一个返回行的查询结果上.

    l         该操作不仅存在于存储过程.

    l         通过执行批查询,也可以创建类似的情况

    l         代码演示(ADO2.0VB经典代码)

    Dim cn as ADODB.Connection,rs as ADODB.Recordset

    Dim strConn as string, strSql as string

    Dim RecAffected as Integer

    strConn=”Provider=SQLOLEDB; Data Source={local}"NetSDK;
                  Initial Catalog=Northwind; Trusted_Connection=Yes;”

    Set cn=New ADODB.Connection

    Cn.Open strConn

    strSql=”insert into Customers …;”&_
                  “select CustomerID,CompanyName from Customer where …;”&_
                  “update Customers set CompanyName=… where…;”&_
                  “select CustomerID,CompanyName from Customer where …”

    Set rs=cn.Execute(strSql,recAffected,adCmdText)

    Do Until rs Is Nothing

           Debug.Print “rs.State=” & rs.State & vbTab &_
                         “records affected=” & recAffected

           Set rs=rs.NextRecordset(recAffected)

    Loop

    l         代码运行分析

    循环中,初始状态下,recordset是关闭的,并且recAffected=1.原因是这个结果对应insert查询,该查询不返回行.而是对数据库记录的修改

    一旦调用了NextRecordset方法后,recordset的状态就变成开放的了.并且其中包含了第一个select查询的结果,由于select查询不修改任何记录,所以第一个select查询之后,recAffected=-1

    然后第二次调用NextRecordset方法,它将返回一个关闭的数据集,recAffected保存了Update查询影响的行数.

    然后在第三次调用NextRecordset方法是返回的是第二个select查询的结果,recAffected有等于-1.

    最后还要调用一次NextRecordset(),此时返回一个设置为Nothingrecordset,表明没有其他的结果需要处理.

    3.        ADONET处理上例的批查询

    ADONET处理批查询的方式与ADO不同.

    (为了简化批查询处理的过程)DataReader会自动移动到第一个返回行的查询结果处(第一个select查询返回行)

    但是,这个改进仍然有些顾此失彼的地方.

    DataReader对象没有提供能够检查每个单独的操作查询所影响行数的方法.

    DataReader对象的RecordsAffected属性保存的是运行总计.

    l         代码演示

    Dim strConn as string=”Provider=SQLOLEDB; Data Source={local}"NetSDK;
                  Initial Catalog=Northwind; Trusted_Connection=Yes;”

    Dim cn as new OleDbConnection(strConn)

    Cn.Open()

    Dim strSql as string=”insert into Customers …;”&_
                  “select CustomerID,CompanyName from Customer where …;”&_
                  “update Customers set CompanyName=… where…;”&_
                  “select CustomerID,CompanyName from Customer where …”

    Dim cmd as new OleDbCommand(strSql,cn)

    Dim rdr as OleDbDataReader=cmd.ExecuteReader()

    Do

           Console.WriteLine(“影响行数=” & rdr.RecordsAffected)

           Do While rdr.Read()

                  Console.WriteLine(vbTab & rdr.GetName(0) & “ - ” & rdr.GetValue(0))

           Loop

    Loop While rdr.NextResult()

    l         代码分析

    ADONET的代码和ADO代码很相似.但是,结果略有不同.

    当通过Command对象取得DataReader对象之后,DataReader对象会立即返回第一个select查询的结果

    第一次调用NextResult方法时,它立即移动到第二个select查询的结果

    第二次调用NextResult(),直接返回false,从而跳出循环.

    在性能上,DataReader对象的RecordsAffected属性直接返回所以操作查询影响的记录总和.

    实际运行中,ExecuteReader方法返回DataReader对象时,RecordsAffected属性=1;

    当调用NextResult方法后RecordsAffected属性=2

    l         切记

    DML操作查询(CREATE PROCEDURE DROP TABLE)返回行数总是-1

    1.1.5                    关闭DataReader对象

    ADONET对象模型中,记住尽可能快速的关闭DataReader对象,这非常重要.

    目前为止,一个带着开发的DataReaderConnection对象仍被认为是锁定的.

    如果在关闭DataReader对象之前,试图打开第二个,会导致异常.”需要一个开放并可用的连接

    RDO中存在相同的限制.

    ADO(Sql Server)打开两个流水游标则很正常.

    此类情形,可以归结为:试图使用一个忙连接.

    微软数据访问技术的每一代对于此情况处理都不相同.

    l         VBSQL生成错误

    l         DAO/Jet创建新连接

    l         RDO生成错误

    l         ADO创建新连接

    l         ADO.NET生成错误

    1.        使用DataReader对象是基于性能方面的原因,应该在发出查询之后尽快地将结果拉下来

    2.        开发的DataReader对象会锁定Connection对象

    3.        如果需要在各查询的结果之间前后移动,应该使用DataSet对象;
    或者考虑将查询结果存储在某类业务对象之中.

    1.1.6                    执行返回单值的查询

    查询举例:

    Select COUNT(*) from Customers

    Select CompanyName from Customers where CustomerID=’ALFKI’

    如上查询,DataReaderDataSet都有点大材小用.

    Command对象提供了一个方法(ExecuteScalar)专门获取单值查询.其返回值类型为Object

    l         代码演示

    Dim strConn as string=”Provider=SQLOLEDB; Data Source={local}"NetSDK;

                  Initial Catalog=Northwind; Trusted_Connection=Yes;”

    Dim cn as new OleDbConnection(strConn)

    cn.Open()

    Dim cmd as OleDbCommand=cn.CreateCommand()

    cmd.CommandText=”select COUNT(*) from Customers”

    Dim customerCount as Integer=Cint(cmd.ExecuteScalar())

    cmd.CommandText=”select CompanyName from Customers where CustomerID=’ALFKI’”

    Dim companyName1 as string Convert.toString(cmd.ExecuteScalar())

    1.1.7                    参数化查询

    假设:建立一个应用程序,用户可检查客户端放置的顺序.

    如果你有20个客户端,不需要编写20个独立的函数来返回客户端的顺序,只要建立参数化查询.并使它可接收有关客户的信息并为该客户返回其顺序即可.同样,还可以建立接收参数的查询,

    SELECT OrderID,CustomerID,EmployeeID,OrderDate FROM Orders WHERE CustomerID=?

    l         其中问号?是一个参数标记,(所谓参数标记是指一种在查询中指示参数的标准方式)

    l         SQL SERVER.NET数据提供者不支持通用的参数标记”?”,它需要的是带有@前缀的命名参数.

    SELECT OrderID,CustomerID,EmployeeID,OrderDate FROM Orders WHERE CustomerID=@CustomerID

    1.1.7.1      ADO.NETParameter对象

    创建ADO.NETParameter对象即可存储参数信息的一种结构.

    一般的,创建Command对象,向其Parameters集合属性中添加一个Parameter,并给它提供值,最后执行Command对象,以便获取特定客户的顺序信息.

    l         代码演示

    Dim strConn,strSql As string

    strConn=”Provider=SQLOLEDB; Data Source=(local)"NetSDK; Initial Catalog=Northwind;”&_
    “ Trusted_Connection=Yes;”

    Dim cn As New OleDbConnection(strConn)

    Cn.Open()

    strSql=”Select OrderID,CustomerID,EmployeeID,OrderData from Orders where CustomerID=?”

    Dim cmd As New OleDbCommand(strSql,cn)

    cmd.Parameters.Add(“@CustomerID”, OleDbType.Wchar,5)

    cmd.Parameters(0).Value=”ALFKI”

    Dim rdr As OleDbDataReader=cmd.ExecuteReader()

    l         总结

    使用参数化查询可以大幅度简化编程.

    更改参数的值要比通过编程连接查询字符串要容易得多.特别是,在为某参数复制的时候,无需再考虑去删除它的问题.

    1.1.8                    调用存储过程

    假设有一个存储过程,它能返回一行数据.如下(Sql Server中的)

    CREATE PROCEDURE GetCustomer(@CustomerID nchar(5) AS

    SELECT CustomerID,CompanyName,ContactName,ContactTitle

    FROM Customers WHERE CustomerID=@CustomerID

                         RETURN

    1.1.8.1      Command对象调用存储过程有两个方法

    1.1.8.1.1                                方法之一:使用Command对象的CommandType属性

    CommandType属性是CommandType枚举类型的值.

    取值如下:TextTableDirectStoredProcedure

    默认值Text

    CommandType属性设置为StoredPriocedure,表示正在调用一个存储过程.(Command对象将CommandText属性的值和Parameters集合中的信息相结合,生成调用存储过程的语法)

    l         示例代码

    Dim cn As New OleDBConnection(strConn)

    Cn.Open()

    Dim cmd as OleDbCommand=cn.CreateCommand()

    With cmd

           .CommandText=”GetCustomer”

           .CommandType=CommandType.StoredProcedure

           .Parameters.Add(“@CustomerID”,OleDbType.Wchar.5)

           .Parameters(0).Value=”ALFKI”

    End With

    Dim rdr As OleDbDataReader=cmd.ExecuteReader()

    If rdr.Read() then

           Cosole.WriteLine(rdr(“CompanyName”))

    Else

           Console.WriteLine(“No Customer Found.”)

    endIF

    rdr.Close()

    cn.Close()

    l         总结

    这种语法是调用存储过程的标准方法:

    {?=CALL MyStoredProc(?,?,?)}

    或者不准备使用返回值(就是?),形式如下:

    {CALL GetCustomer(?)}

    比较推荐这种语法,而不是向上面代码中那样依赖与CommandType属性.

    可能出现问题的地方是:如果你的查询中一个名称包含了特殊字符(比如空格),那么将CommandType属性设置为TableDirectStoredProcedure,对象名称中的分隔符无法识别.但是,SqlCommand对象可以准确的确定对象名称的间隔.

    1.1.8.1.2                                第二种方法(也是推荐的方法)不要依赖CommandType属性,尽量在CommandText属性中为查询使用恰当的语法.

    l         示例代码

    Dim cmd As New OleDbDataAdapter()

    cmd.CommandText=”{CALL GetCustomer(?)}”

    cmd.CommandType=CommandType.Text

    l         总结

    SQL Server中在Query Analyzer工具中使用EXEC语法执行存储过程,也可以在Command对象中使用;但是,并非所有的数据库都支持该语法.

    1.1.9                    从输出参数中获取数据

    并非所有的存储过程都通过结果集返回信息

    许多过程是通过输出参数来返回信息的

    l         举例

    CREATE PROCEDURE GetCustomer(@CustomerID nchar(5),

                                                                   @CompanyName nvarchar(40) OUTPUT,

                                                                   @ContactName nvarchar(40) OUTPUT,

                                                                   @ContactTitle nvarchar(30) OUTPUT) AS

                  SELECT @CompanyName=CompanyName,@ContactName=ContactName,

                                @ContactTitle=ContactTitle

                  FROM Customers WHERE CustomerID=@CustomerID

    IF @@ROWCOUNT=1

           RETURN 0

    ELSE

           RETURN -1

    1.1.9.1      如何利用Command对象从输出参数中获取数据?

    Parameter对象有一个Direction属性,能接收ParameterDirection枚举类型的值

    (ParameterDirection枚举值可能是ReturnValue,Input,InputOutput,Output)默认是Input

    要从存储过程中获取信息,需要对Parameter参数的Direction属性进行设置

    l         示例代码

    ...

    Dim cn As New OleDbConnection(strConn)

    cn.Open()

    Dim cmd As OleDbCommand=cn.CreateCommand()

    With cmd

           .CommandText=”{?=CALL GetCustomer(?,?,?,?)}”

           .Parameters.Add(“@RetVal”,OleDbType.Integer)

           .Parameters.Add(“@CustomerID”,OleDbType.WChar,5)

           .Parameters.Add(“@CompanyName”,OleDbType.VarWChar,40)

           .Parameters.Add(“@ContactName”,OleDbType,VarWChar,40)

           .Parameters.Add(“@ContactTitle”,OleDbType.VarWChar,30)

           .Parameters(“@ContactTitle”).Direction=ParameterDirection.Output

           .Parameters(“@RetVal”).Direction=ParameterDirection.ReturnValue

           .Parameters(“@CustomerID”).Value=”ALFKI”

           .Parameters(“@CompanyName”).Direction=ParameterDirection.Output

           .Parameters(“@ContactName”).Direction=ParameterDirection.Output

           .ExecuteNonQuery()

           If Convert.ToInt32(.Parameters(“@RetVal”).Value=0) Then

                  Console.WriteLine(“找到”)

           Else

                  Console.WriteLine(“没有记录”)

           End If

    End With

    1.1.10                Transaction中执行查询

    Command对象有Transaction属性,要在Transaction中执行Command需要设置该属性.

    Command对象调用BeginTransaction方法创建一个Transaction对象.

    l         示例代码

    ...

    cn.Open()

    Dim txn As OleDbTransaction=cn.BeiginTransaction()

    Dim strSql As String=”insert into Customers (...) VALUES (...)”

    Dim cmd As New OleDbCommand(strSql,cn,txn)

    Dim recAffected As Integer=cmd.ExecuteNonQuery()

    If recAffected=1 Then

           Console.WriteLine()

           txn.Commit()

    Else

           Console.WriteLine()

           txn.Rollback()

    End If

     

  • 相关阅读:
    Vue学习笔记vueelementadmin 前端学习
    Vue学习笔记Vue.js2.X 学习(三)===>组件化高级
    Vue学习笔记rest_framework_jwt 学习
    Vue学习笔记Django REST framework3后端接口API学习
    Vue学习笔记Vue.js2.X 学习(一)===>基本知识学习
    Vue学习笔记Vue.js2.X 学习(二)===>组件化开发
    Vue学习笔记Windows系统Git安装(按装vueelementadmin报错)
    跑马灯
    使用信号量的线程同步实验
    按键盘数码管显示实验
  • 原文地址:https://www.cnblogs.com/lizunicon/p/1291828.html
Copyright © 2011-2022 走看看