使用oracle的小朋友有福了 OracleBlueCopy +merge into 大数据操作不要太香哦 返回

SqlSugar
6 210
该叫什么 hotapp 发布于2021/7/20
悬赏:5 飞吻

//直接上代码


/*

OracleBlueCopy.cs


by  hotapp

OracleBlueCopy

oracle 大量数据插入    

Oracle.ManagedDataAccess.Core  3.21.1 版本亲测可用

*/

using System;

using System.Collections.Generic;

using System.Data;

using Oracle.ManagedDataAccess.Client;

using System.Linq;

using System.Text;

using System.Threading.Tasks;


namespace SqlSugar 

{

    public class OracleBlueCopy

    {

        internal List<IGrouping<int, DbColumnInfo>> DbColumnInfoList { get;   set; }

        internal SqlSugarProvider Context { get;   set; }

        internal ISqlBuilder Builder { get; set; }

        internal InsertBuilder InsertBuilder { get; set; }

        internal object[] Inserts { get;  set; }


        public int ExecuteBlueCopy()

        {

            if (DbColumnInfoList == null || DbColumnInfoList.Count == 0) return 0;


            if (Inserts.First().GetType() == typeof(DataTable))

            {

                return WriteToServer();

            }

            DataTable dt = GetCopyData();

            OracleBulkCopy bulkCopy = GetBulkCopyInstance();

            bulkCopy.DestinationTableName = InsertBuilder.GetTableNameString;

            try

            {

                bulkCopy.WriteToServer(dt);

            }

            catch (Exception ex)

            {

                CloseDb();

                throw ex;

            }

            CloseDb();

            return DbColumnInfoList.Count;

        }


        public async Task<int> ExecuteBlueCopyAsync()

        {

            if (DbColumnInfoList == null || DbColumnInfoList.Count == 0) return 0;


            if (Inserts.First().GetType() == typeof(DataTable))

            {

                return WriteToServer();

            }

            DataTable dt=GetCopyData();

            OracleBulkCopy bulkCopy = GetBulkCopyInstance();

            bulkCopy.DestinationTableName = InsertBuilder.GetTableNameString;

            try

            {

                await Task.Run(() => bulkCopy.WriteToServer(dt));

            }

            catch (Exception ex)

            {

                CloseDb();

                throw ex;

            }

            CloseDb();

            return DbColumnInfoList.Count;

        }


        private int WriteToServer()

        {

            var dt = this.Inserts.First() as DataTable;

            if (dt == null)

                return 0;

            Check.Exception(dt.TableName == "Table", "dt.TableName can't be null ");

            dt = GetCopyWriteDataTable(dt);

            OracleBulkCopy copy = GetBulkCopyInstance();

            copy.DestinationTableName = this.Builder.GetTranslationColumnName(dt.TableName);

            copy.WriteToServer(dt);

            CloseDb();

            return dt.Rows.Count;

        }

        private DataTable GetCopyWriteDataTable(DataTable dt)

        {

            var result = this.Context.Ado.GetDataTable("select * from " + this.Builder.GetTranslationColumnName(dt.TableName)+ " where 1 > 2 ");

            foreach (DataRow item in dt.Rows)

            {

                DataRow  dr= result.NewRow();

                foreach (DataColumn column in result.Columns)

                {


                    if (dt.Columns.Cast<DataColumn>().Select(it => it.ColumnName.ToLower()).Contains(column.ColumnName.ToLower()))

                    {

                        dr[column.ColumnName] = item[column.ColumnName];

                        if (dr[column.ColumnName] == null)

                        {

                            dr[column.ColumnName] = DBNull.Value;

                        }

                    }

                }

                result.Rows.Add(dr);

            }

            result.TableName = dt.TableName;

            return result;

        }

        private OracleBulkCopy GetBulkCopyInstance()

        {

            OracleBulkCopy copy;

            if (this.Context.Ado.Transaction == null)

            {

                copy = new OracleBulkCopy((OracleConnection)this.Context.Ado.Connection, Oracle.ManagedDataAccess.Client.OracleBulkCopyOptions.Default);               

            }

            else

            {

                copy = new OracleBulkCopy((OracleConnection)this.Context.Ado.Connection,OracleBulkCopyOptions.UseInternalTransaction);

            }

            if (this.Context.Ado.Connection.State == ConnectionState.Closed)

            {

                this.Context.Ado.Connection.Open();

            }

            return copy;

        }

        private DataTable GetCopyData()

        {

            var dt = this.Context.Ado.GetDataTable("select  * from " + InsertBuilder.GetTableNameString + " where 1 > 2 ");

            foreach (var rowInfos in DbColumnInfoList)

            {

                var dr = dt.NewRow();

                foreach (DataColumn item in dt.Columns)

                {

                    var rows = rowInfos.ToList();

                    var value = rows.FirstOrDefault(it =>

                                                             it.DbColumnName.Equals(item.ColumnName, StringComparison.CurrentCultureIgnoreCase) ||

                                                             it.PropertyName.Equals(item.ColumnName, StringComparison.CurrentCultureIgnoreCase)

                                                        );

                    if (value != null)

                    {

                        if (value.Value != null && UtilMethods.GetUnderType(value.Value.GetType()) == UtilConstants.DateType)

                        {

                            if (value.Value != null && value.Value.ToString() == DateTime.MinValue.ToString())

                            {

                                value.Value = Convert.ToDateTime("1900/01/01");

                            }

                        }

                        if (value.Value == null)

                        {

                            value.Value = DBNull.Value;

                        }

                        dr[item.ColumnName] = value.Value;

                    }

                }

                dt.Rows.Add(dr);

            }

            return dt;

        }

        private void CloseDb()

        {

            if (this.Context.CurrentConnectionConfig.IsAutoCloseConnection && this.Context.Ado.Transaction == null)

            {

                this.Context.Ado.Connection.Close();

            }

        }

    }

}


/*

InsertableProvider.cs


添加以下方法


*/


 public OracleBlueCopy UseOracle()

        {

            PreToSql();

            var currentType = this.Context.CurrentConnectionConfig.DbType;

            Check.Exception(currentType != DbType.Oracle, "UseSqlServer no support " + currentType);

            OracleBlueCopy result = new OracleBlueCopy();

            result.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.GroupBy(it => it.TableId).ToList();

            result.InsertBuilder = this.InsertBuilder;

            result.Builder = this.SqlBuilder;

            result.Context = this.Context;

            result.Inserts = this.InsertObjs;

            return result;

        }



/*

IInsertable.cs

添加以下接口


*/

  


 OracleBlueCopy UseOracle();

热忱回答6

  • hotapp hotapp VIP0
    2021/7/21

    测试可用的版本      需要引入 Microsoft.CSharp 包

    .net standard 2.0 版本     依赖    ----     Oracle.ManagedDataAccess.Core      2.19.110  

    .net standard 2.1 版本     依赖    ----     Oracle.ManagedDataAccess.Core      3.21.1

    0 回复
  • 白纸 白纸 VIP0
    2021/7/24

    OracleBlueCopy 这种直接datatbale 插入数据库,如果发生key重复,会很暴力地把唯一索引失效导致需要重新建议索引才可以继续插入,并且发生后,部分已经写入的数据无法rollback,又如何解决,索引已经失效无法删除已经写入的数据了,求解答

    0 回复
  • @白纸https://www.donet5.com/Home/Doc?typeId=1220  如果按文档插入方案优化1秒能插入1万 也不需要blukcopy

    0 回复
  • hotapp hotapp VIP0
    1个月前

    @白纸:对于大数据量的更新或添加,先插入到临时表中,再使用oracle或sqlserver的merge into的方式插入或更新业务表,这样可以对更新更灵活的操作,执行效率也更更高一些。相当于把串行执行更新改成了并行执行,大量数据更新上效率会更好。可以避免数据库解析大量update语句时造成的开销。

    如果只是更新少量数据,还是使用框架的update更加实用。


    0 回复
  • hotapp hotapp VIP0
    1个月前

    @fate stay night:目前这种方式主要是对框架大量数据更新的场景的优化,比如10万条记录的更新操作,耗时在50~100秒左右; 改为插入7临时表(10s),merge into update(0.1s) 操作耗时在10秒左右 , 如果能够进一步通过OracleBluKCopy或参数列表方式插入,减少插入时间(测试可以插入时间缩短到1s),那么,大量数据更新的性能大概能提高几十倍左右。 

    0 回复
  • hotapp hotapp VIP0
    1个月前

    @hotapp:当然这个只是针对ORACLE的优化,针对其他的数据库的优化方案也可以参考这种思路来进行,只是抛个砖而已。

    0 回复