尝试使用 where 子句中使用的 like 语句删除记录时,Advantage 数据库引发异常

Posted

技术标签:

【中文标题】尝试使用 where 子句中使用的 like 语句删除记录时,Advantage 数据库引发异常【英文标题】:Advantage database throws an exception when attempting to delete a record with a like statement used in the where clause 【发布时间】:2010-06-01 16:28:31 【问题描述】:

下面的代码显示,当sql语句为时,一条记录被删除:

select * from test where qty between 50 and 59  

但是sql语句:

select * from test where partno like 'PART/005%'

抛出异常:

Advantage.Data.Provider.AdsException: Error 5072:  Action requires read-write access to the table

如何可靠地删除应用了 where 子句的记录? 注意:我使用的是 Advantage Database v9.10.1.9、VS2008、.Net Framework 3.5 和 WinXP 32 位

using System.IO;
using Advantage.Data.Provider;
using AdvantageClientEngine;
using NUnit.Framework;

namespace NetworkEidetics.Core.Tests.Dbf

  [TestFixture]
  public class AdvantageDatabaseTests
  
    private const string DefaultConnectionString = @"data source=0;ServerType=local;TableType=ADS_CDX;LockMode=COMPATIBLE;TrimTrailingSpaces=TRUE;ShowDeleted=FALSE";
    private const string TestFilesDirectory = "./TestFiles";

    [SetUp]
    public void Setup()
    
      const string createSql = @"CREATE TABLE [0] (ITEM_NO char(4), PARTNO char(20), QTY numeric(6,0), QUOTE numeric(12,4)) ";
      const string insertSql = @"INSERT INTO [0] (ITEM_NO, PARTNO, QTY, QUOTE) VALUES('1', '2', 3, 4)";
      const string filename = "test.dbf";

      var connectionString = string.Format(DefaultConnectionString, TestFilesDirectory);

      using (var connection = new AdsConnection(connectionString)) 
        connection.Open();

        using (var transaction = connection.BeginTransaction()) 
          using (var command = connection.CreateCommand()) 
            command.CommandText = string.Format(createSql, filename);
            command.Transaction = transaction;
            command.ExecuteNonQuery();
          

          transaction.Commit();
        

        using (var transaction = connection.BeginTransaction()) 
          for (var i = 0; i < 1000; ++i) 
            using (var command = connection.CreateCommand()) 
              var itemNo = string.Format("0", i);
              var partNumber = string.Format("PART/0:d4", i);
              var quantity = i;
              var quote = i * 10;

              command.CommandText = string.Format(insertSql, filename, itemNo, partNumber, quantity, quote);
              command.Transaction = transaction;
              command.ExecuteNonQuery();
            
          
          transaction.Commit();
        

        connection.Close();
      
    

    [TearDown]
    public void TearDown()
    
      File.Delete("./TestFiles/test.dbf");
    

    [Test]
    public void CanDeleteRecord()
    
      const string sqlStatement = @"select * from test";

      Assert.AreEqual(1000, GetRecordCount(sqlStatement));
      DeleteRecord(sqlStatement, 3);
      Assert.AreEqual(999, GetRecordCount(sqlStatement));
    

    [Test]
    public void CanDeleteRecordBetween()
    
      const string sqlStatement = @"select * from test where qty between 50 and 59";

      Assert.AreEqual(10, GetRecordCount(sqlStatement));
      DeleteRecord(sqlStatement, 3);
      Assert.AreEqual(9, GetRecordCount(sqlStatement));
    

    [Test]
    public void CanDeleteRecordWithLike()
    
      const string sqlStatement = @"select * from test where partno like 'PART/005%'";

      Assert.AreEqual(10, GetRecordCount(sqlStatement));
      DeleteRecord(sqlStatement, 3);
      Assert.AreEqual(9, GetRecordCount(sqlStatement));
    

    public int GetRecordCount(string sqlStatement)
    
      var connectionString = string.Format(DefaultConnectionString, TestFilesDirectory);
      using (var connection = new AdsConnection(connectionString)) 
        connection.Open();

        using (var command = connection.CreateCommand()) 
          command.CommandText = sqlStatement;
          var reader = command.ExecuteExtendedReader();
          return reader.GetRecordCount(AdsExtendedReader.FilterOption.RespectFilters);
        
      
    

    public void DeleteRecord(string sqlStatement, int rowIndex)
    
      var connectionString = string.Format(DefaultConnectionString, TestFilesDirectory);
      using (var connection = new AdsConnection(connectionString)) 
        connection.Open();

        using (var command = connection.CreateCommand()) 
          command.CommandText = sqlStatement;

          var reader = command.ExecuteExtendedReader();

          reader.GotoBOF();
          reader.Read();

          if (rowIndex != 0) 
            ACE.AdsSkip(reader.AdsActiveHandle, rowIndex);
          
          reader.DeleteRecord();
        

        connection.Close();
      
    
  

【问题讨论】:

【参考方案1】:

LIKE 生成静态游标而不是活动游标,这意味着它是只读数据集。要在这种情况下删除一行,最好使用 SQL DELETE 语句。

DELETE FROM test where partno LIKE 'PART/005%'

我假设您的测试就是这样,只是测试。他们正在使用一些相当低效的机制来定位和删除行。

评论后更新没有关键字段:

如何使用 LEFT 标量而不是 LIKE (可能不适用于所有情况,但适用于您的示例)。如果大小始终相同,您还可以在 left(partno,8) 上添加索引以提高性能:

select * from test where left(partno,8) = 'PART/005' 

然后您可以直接在此实时结果集上使用扩展数据读取器的删除功能(无需 gotop 和跳过)。

Alex 的 ROWID 评论后更新 我不知道我们的 ROWID 来自基表,即使在静态游标中也是如此。亚历克斯的评论是解决您问题的方法。第一:

SELECT t.*, t.rowid FROM test t WHERE x LIKE 'PART/005%'

然后:

DELETE FROM test WHERE rowid = :thisid

【讨论】:

问题是在应用程序中我使用它的表没有主键列并且可能包含重复的行,所以我不能使用 SQL DELETE 语句。有没有办法获得带有类似过滤器的实时光标? 可以在表格中添加唯一标识符字段吗?如果没有,这个问题只会再次出现。如果您可以添加一个唯一的 ID 并填写它(auto-inc 字段,或生成某些内容),然后将其复制到另一个名称并使用 Delete from test where testid in(select testid from testcopy where partno like 'PART /005%;); 如何使用 LEFT 标量而不是 LIKE (可能不适用于所有情况,但适用于您的示例)。如果大小始终相同,您还可以在 left(partno,8) 上添加索引以提高性能: select * from test where left(partno,8) = 'PART/005' 然后您可以使用 Delete 函数直接在此实时结果集上的扩展数据读取器(无 gotop 和跳过)。 不幸的是,大小不同,所以 LEFT 不可行。可以使用 RecordNumber 吗?无论选择查询如何,RecordNumber 都是行的唯一标识符吗? 可以使用伪列ROWID作为主键。在 SELECT 语句中包含 ROWID 列,然后使用“DELETE FROM TABLE WHERE rowid = x”,其中 x 是 SELECT 中的 rowid。

以上是关于尝试使用 where 子句中使用的 like 语句删除记录时,Advantage 数据库引发异常的主要内容,如果未能解决你的问题,请参考以下文章

10like子句

MySQL LIKE 子句

《零基础》MySQL LIKE 子句(十六)

MySQL

SQL的where语句

Powershell where子句过滤