天天看点

C# 中 ,sqlte数据库的附加(attach)操作

因为项目的需求,用到了这个功能,就在此记录一下,以免之后忘记。如果有不对的地方,还请各位大神及时指出,谢谢啦。

为什么会用到:

    当时在项目中,需要往一个数据库中下发一张表。这个表大概有10W左右的数据,之前的在C#中foreach循环,一条条插入,感觉很慢很慢。最后想想,能不能用线程把这10W条数据分开,每个线程往一个临时数据库中的一个临时表中只插入一部分。当然,这个临时表和用到的正式表的结构一模一样。最后把这几个临时的数据库中的记录全移动到正式的数据库中。上网找找发现有附加数据库这么个操作,就试了试。

关于sqlite需要的动态库:

我是从这个网站上下载的,可以下载一个自己需要的版本。我直接下载编译好的动态库文件。

https://www.sqlite.org/download.html

简单测试的部分代码

int tcnt = 4;//设置4个线程
int junpcnt = Convert.ToInt32(Math.Ceiling(tbSet.Tables["t_vipinfo"].Rows.Count / Convert.ToDecimal(tcnt)));//把t_vipinfo中的10W条书库平均到4个线程中,每个线程应该占几条
try
{
    ManualResetEvent[] _ManualEvents = new ManualResetEvent[tcnt];
    Thread[] _thread = new Thread[tcnt];
    int beginno, endno;
    for (int i = 1; i <= tcnt; i++)
    {
        _ManualEvents[i - 1] = new ManualResetEvent(false);
        beginno = (i - 1) * junpcnt + 1;
        endno = i * junpcnt;
        //从大表中获得每个线程用到的数据,并生成一个新的Tb,这个函数可以自己写一个
        DataTable tb = GetNewDataTable(tbSet.Tables["t_vipinfo"], "rowno >= " + beginno.ToString() + " AND rowno <= " + endno.ToString(), "");
        Thread thread = new Thread(vipThreadMethod);
        thread.IsBackground = true;
        _thread[i - 1] = thread;
        thread.Start(new DownThreadParam(i, tb, _ManualEvents[i - 1], sqlDB.DbFileName, clm));
    }
    WaitHandle.WaitAll(_ManualEvents);
    for (int i = 1; i <= tcnt; i++)
    {
         _ManualEvents[i - 1].Close();
         _ManualEvents[i - 1].Dispose();
         _thread[i - 1].DisableComObjectEagerCleanup();
    }

    string attchPath = "";
    string tmpsql = "";
    for (int i = 1; i <= tcnt; i++)
    {
         //这里就是附加数据库了,给同一个数据库附加多个数据库,这些附加的数据库的别名不能一样
         attchPath = string.Format("attach database '{0}' AS atcDB ;", AppPath + "\\tmpvip" + i.ToString() + ".db");
         tmpsql = attchPath + "insert into tmp_vipinfo  select * from atcDB.tmp_vipinfo;";
         sqlDB.Execute(tmpsql);
         sqlDB.dbConnection.Close();
         sqlDB.dbConnection.Open();
    }
}
finally
{
   for (int i = 1; i <= tcnt; i++)
   {
       if (File.Exists(AppPath + "\\tmpvip" + i.ToString() + ".db"))
          File.Delete(AppPath + "\\tmpvip" + i.ToString() + ".db");
   }
}
           
/// <summary>
        /// 获取从dt中select出来的数据集
        /// </summary>
        /// <param name="dt">源数据集</param>
        /// <param name="condition">过滤条件</param>
        /// <param name="sortstr">排序字段</param>
        /// <returns></returns>
        public static DataTable GetNewDataTable(DataTable dt, string condition, string sortstr)
        {
            DataTable newdt = new DataTable();
            newdt = dt.Clone();
            try
            {
                DataRow[] dr = dt.Select(condition, sortstr);
                for (int i = 0; i < dr.Length; i++)
                {
                    newdt.ImportRow((DataRow)dr[i]);
                }
            }
            catch
            {
                newdt.Dispose();
                newdt = null;
                return new DataTable();//返回空数据集
            }
            return newdt;//返回的查询结果
        }
           
//自己封的一个类,当做线程的入参
    public class DownThreadParam
    {
        public int i;
        public DataRow[] lstarr;
        public DataTable tb;
        public ManualResetEvent evt;
        public string dbname;
        public string clm;
        public DownThreadParam(int i, DataTable tb, ManualResetEvent evt,string dbname,string clm)
        {
            this.i = i;
            this.tb = tb;
            this.evt = evt;
            this.dbname = dbname;
            this.clm = clm;
        }
    }       



        //每个线程执行的方法
        public static void vipThreadMethod(object param)
        {
            DownThreadParam p = (DownThreadParam)param;
            string dbfile = AppPath + "\\tmpvip" + p.i.ToString() + ".db";
            NewDbFile(dbfile);//如果没有,则生成一个db库
            SqLiteHelper sqlHelper = null;
            sqlHelper = new SqLiteHelper("\\tmpvip" + p.i.ToString() + ".db");
            string attchPath = string.Format("attach database '{0}' AS atcOriDB ;", G_sPosAppPath + p.dbname);
            StringBuilder sb = new StringBuilder();
            //这就是为了偷懒,从正式库中把正式表的表结构复制过来
            sb.AppendLine(attchPath + " CREATE TABLE tmp_vipinfo AS SELECT * FROM atcOriDB.t_vipinfo where 1=2;");
            sqlHelper.Execute(sb.ToString());
            sqlHelper.CloseConnection();
            sqlHelper = null;

            sqlHelper = new SqLiteHelper("\\tmpvip" + p.i.ToString() + ".db");

            string value = "";
            Type DbType;

            sb.Clear();
            int i = 0;
            foreach (DataRow rw in p.tb.Rows)
            {
                value = "";
                foreach (DataColumn dc in p.tb.Columns)
                {
                    DbType = dc.DataType;
                    if (rw[dc.ColumnName].ToString().Equals(string.Empty))
                    {
                        if (IsNumeric(DbType))//如果是数字类型
                        {
                            value = value + ",NULL";
                        }
                        else
                        {
                            value = value + ",''";
                        }
                    }
                    else
                    {
                        if (IsNumeric(DbType))
                        {
                            value = value + "," + rw[dc.ColumnName].ToString();
                        }
                        else
                        {
                            value = value + ",'" + rw[dc.ColumnName].ToString() + "'";
                        }
                    }
                }
                sb.AppendLine("insert into tmp_vipinfo (");
                sb.AppendLine(p.clm);
                sb.AppendLine(")values(" + value.Substring(1) + ");");
                i++;
                if (i > 500)//每500条执行一次
                {
                    sqlHelper.Execute(sb.ToString());
                    sb.Clear();
                    i = 0;
                }
            }
            //如果最后不到500条,则把剩下的执行一次
            if (!string.IsNullOrEmpty(sb.ToString()))
            {
                sqlHelper.Execute(sb.ToString());
            }

            sqlHelper.CloseConnection();
            sqlHelper = null;
            ManualResetEvent e = p.evt;
            e.Set();
        }
           

最后试了试,比之前快了很多。但是还发现了sqlite有执行准备的功能,速度可能会更快,正在研究中。

      上面的代码有很多没有优化或者是比较啰嗦的地方,代码写的不美丽,望见谅。SqLiteHelperl网上上有很多,可以查找一下,自己再按照自己的需求改改。

继续阅读