【问题标题】:How to delete large amounts of data from Foxpro如何从 Foxpro 中删除大量数据
【发布时间】:2014-01-13 12:03:10
【问题描述】:

问题

我们有一个带有许多表的旧版 Visual FoxPro 预订系统。我被要求对桌子做一些整理工作以减小它们的大小。

  • 这些表设计得很糟糕,没有自动递增的主键。
  • 最大的表有 300 万行。
  • 我正在尝试删除 380,000 行。
  • 由于表中的数据量很大,我正在尝试开发一种批量删除的解决方案。

到目前为止我得到了什么

我创建了一个 C# 应用程序,它通过 vfpoledb.1 驱动程序访问数据库文件。此应用程序使用 recno() 批量删除。这是我正在使用的查询示例:

delete from TableA 
where TableA.Key in (
    select Key from TableB 
    where Departure < date(2010,01,01) and Key <> ""
) and recno() between 1 and 10000

通过 vfpoledb.1 执行此操作不会删除任何内容。执行带有相同 where 子句的 select 语句不会返回任何内容。

似乎是 recno() 函数和 in() 函数的组合导致了这个问题。依次使用每个子句测试查询会返回结果。

问题

  1. 还有其他方法可以从 Visual FoxPro 中批量删除数据吗?
  2. 为什么 recno() 和 in() 不兼容?
  3. 我还缺少什么吗?

其他信息

  • ANSI 设置为 TRUE
  • DELETED 设置为 TRUE
  • EXCLUSIVE 设置为 TRUE

【问题讨论】:

    标签: sql visual-foxpro


    【解决方案1】:

    与其批量处理这么多的记录数,不如采用更简单的方法。您希望在某个日期 (2010-01-01) 之前杀死所有内容。

    为什么不尝试从 2009 年 12 月 31 日开始,并继续回溯到您要清除的文件的最早日期。另请注意,我不知道 Departure 是日期还是日期时间,所以我将其更改为

    TTOD(Departure)(意味着将时间转换为日期部分)

    DateTime purgeDate = new DateTime(2009, 12, 31);
    
    // the "?" is a parameter place-holder in the query
    string SQLtxt = "delete from TableA "
                  + " where TableA.Key in ( "
                  + "     select Key from TableB "
                  + "        where TTOD( Departure ) <  ? and Key <> \"\" )";
    
    
    OleDbCommand oSQL = new OleDbCommand( SQLtxt, YourOleDbConnectionHandle );
    // default the "?" parameter place-holder
    oSQL.Parameters.AddWithValue( "parmDate", purgeDate );
    
    int RecordsDeleted = 0;
    
    while( purgeDate > new DateTime(2000,1,1) )
    {
       // always re-apply the updated purge date for deletion
       oSQL.Parameters[0].Value = purgeDate;
       RecordsDeleted += oSQL.ExecuteNonQuery();
    
       // keep going back one day at a time...
       purgeDate = purgeDate.AddDays(-1);
    }
    

    这样,无论您处理什么 RECNO() 都无关紧要,它只会执行特定日期的任何键。如果您一天有超过 10,000 个条目,那么我可能会采用不同的方法,但由于这更像是一次清理,我不会太在意进行 1000 多次迭代(无论多少年,每年 365 天) )通过数据...或者,您可以使用日期范围进行操作,并且可能每周进行一次,只需更改 WHERE 子句并调整参数...类似于...(2000 年 1 月 1 日的日期只是猜测数据可以追溯到多远)。此外,由于这是整个日期范围,因此无需转换出发字段的可能 TTOD()。

    DateTime purgeDate = new DateTime(2009, 12, 31);
    DateTime lessThanDate = new DateTime( 2010, 1, 1 );
    
    // the "?" is a parameter place-holder in the query
    string SQLtxt = "delete from TableA "
                  + " where TableA.Key in ( "
                  + "     select Key from TableB "
                  + "        where Departure >=  ? "
                  + "          and Departure < ? "
                  + "          and Key <> \"\" )";
    
    OleDbCommand oSQL = new OleDbCommand( SQLtxt, YourOleDbConnectionHandle );
    // default the "?" parameter place-holder
    oSQL.Parameters.AddWithValue( "parmDate", purgeDate );
    oSQL.Parameters.AddWithValue( "parmLessThanDate", LessThanDate );
    
    int RecordsDeleted = 0;
    
    while( purgeDate > new DateTime(2000,1,1) )
    {
       // always re-apply the updated purge date for deletion
       oSQL.Parameters[0].Value = purgeDate;
       oSQL.Parameters[1].Value = lessThanDate;
       RecordsDeleted += oSQL.ExecuteNonQuery();
    
       // keep going back one WEEK at a time for both the starting and less than end date of each pass
       purgeDate = purgeDate.AddDays(-7);
       lessThanDate = lessThanDate.AddDays( -7);
    }
    

    【讨论】:

    • 感谢您的回答,使用出发批处理是一个绝妙的主意。我会离开并测试它并尽快回复您。
    • @Rich,我稍作修改......我只删除了该子句中现在不必要的“AND RECNO()”部分,但您可能确实需要键入 AND KEY “”部分。仅删除 RECNO() 部分,因为即使删除记录,RECNO() 仍然保持不变。除非文件被物理“打包”,否则该位置仍然存在可能的 RECALL(取消删除)。
    • 再次感谢 DRapp,我现在有一个可行的解决方案。我以您的第二个示例为基础。只需调整日期期间即可在每次删除中获得可接受的行数。
    【解决方案2】:

    我也对实现这一目标的最佳方式感兴趣。我们使用了很多设计不佳的 dBaseIII 文件,这些文件有时非常大。

    我们经常这样做,但这是一个令人讨厌的手动过程:

    1. 使用 DTS 将 dbf 文件导入临时数据库(管理工作室导入/导出向导 2005 + 版)

    2. 使用 SSMS 运行清理脚本

    3. 导出 dbf 文件并用新修改的文​​件替换原始文件(备份它们)。

    它对我们有用。

    【讨论】:

    • 谢谢尼克,我正在研究类似于你所描述的东西。导出我们想要保留的数据,然后截断表并将数据重新导入。我决定不这样做,因为我听说有人在导入时会截断数据。
    【解决方案3】:

    您的日期条件似乎不起作用。尝试使用 CTOD() 函数而不是您使用的 DATE() 来执行 SELECT 语句。

    当您的条件有效时,您就可以运行 DELETE 语句。但请记住,作为 DELETE 执行的结果,行只会被标记为已删除。要完全删除它们,您应该在 DELETE 之后运行 PACK 语句。

    作为另一种方式,您也可以试试我们的DBF editor - DBF Commander Professional。它允许执行 SQL 查询,包括命令行(批处理)模式。例如:

    dbfcommander. exe -q "DELETE FROM 'D:\table_name.dbf' WHERE RECNO()<10000"
    dbfcommander. exe -q "PACK 'D:\table_name.dbf'"
    

    您可以在 20 天的全功能试用期内免费使用它。

    【讨论】:

    • 嗨 Oleg,我尝试将 date() 更改为 ctod()。它似乎对我看到的结果没有任何影响。
    猜你喜欢
    • 1970-01-01
    • 2013-10-27
    • 2014-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-28
    • 2011-08-08
    • 1970-01-01
    相关资源
    最近更新 更多