Skip to main content

Insert

nicyeAbout 5 minAbout 1352 words

Insert

IFreeSql fsql; // For creation details, please refer to the getting started document

class Topic
{
    [Column(IsIdentity = true, IsPrimary = true)]
    public int Id { get; set; }
    public int Clicks { get; set; }
    public string Title { get; set; }
    public DateTime CreateTime { get; set; }
}

var items = new List<Topic>();
for (var a = 0; a < 10; a++) items.Add(new Topic { Title = $"newtitle{a}", Clicks = a * 100 });

1. Single Insert

var t1 = fsql.Insert(items[0]).ExecuteAffrows();
//INSERT INTO `Topic`(`Clicks`, `Title`, `CreateTime`)
//VALUES(?Clicks0, ?Title0, ?CreateTime0)

2. Return Auto-Incremented Value

If the table has an auto-increment column, the ID should be returned after inserting data.

Method 1: (Raw)

long id = fsql.Insert(items[0]).ExecuteIdentity();
items[0].Id = id;

Method 2: (Using FreeSql.Repository)

IBaseRepository<Topic> repo = fsql.GetRepository<Topic>();  // Can be obtained from the IOC container
repo.Insert(items[0]);

The repository will fill the auto-incremented value into items[0].Id (supports batch insert backfill)

DbFirst mode sequence: [Column(IsIdentity = true, InsertValueSql = "seqname.nextval")]

3. Batch Insert

var t2 = fsql.Insert(items).ExecuteAffrows();
//INSERT INTO `Topic`(`Clicks`, `Title`, `CreateTime`)
//VALUES(?Clicks0, ?Title0, ?CreateTime0), (?Clicks1, ?Title1, ?CreateTime1),
//(?Clicks2, ?Title2, ?CreateTime2), (?Clicks3, ?Title3, ?CreateTime3),
//(?Clicks4, ?Title4, ?CreateTime4), (?Clicks5, ?Title5, ?CreateTime5),
//(?Clicks6, ?Title6, ?CreateTime6), (?Clicks7, ?Title7, ?CreateTime7),
//(?Clicks8, ?Title8, ?CreateTime8), (?Clicks9, ?Title9, ?CreateTime9)

It is recommended to turn off parameterization when batch inserting by using .NoneParameter() to improve execution efficiency.

When inserting a large amount of data, the internal operations are split into batches according to the following rules:

QuantityParameter Count
MySql50003000
PostgreSQL50003000
SqlServer10002100
Oracle500999
Sqlite5000999

You can also set appropriate values through BatchOptions. When no external transaction is provided, an internal transaction is opened to ensure insert integrity.

4. High-Performance BulkCopy

PackageExtension MethodDescription
FreeSql.Provider.SqlServerExecuteSqlBulkCopy
FreeSql.Provider.MySqlConnectorExecuteMySqlBulkCopy
FreeSql.Provider.OracleExecuteOracleBulkCopy
FreeSql.Provider.PostgreSQLExecutePgCopy
FreeSql.Provider.DamengExecuteDmBulkCopyDameng
FreeSql.Provider.KingbaseESExecuteKdbCopyKingbaseES

Batch Insert Performance Test Reference (52 Fields)

180K10K5K2K1K50010050
MySql 5.5 ExecuteAffrows38,4812,2341,1362842391676630
MySql 5.5 ExecuteMySqlBulkCopy28,4051,1426574514355924722
SqlServer Express ExecuteAffrows402,35524,84711,4654,9712,43791513888
SqlServer Express ExecuteSqlBulkCopy21,065578326139105796048
PostgreSQL 10 ExecuteAffrows46,7563,2942,2691,0193742095137
PostgreSQL 10 ExecutePgCopy10,09058333713688613025

180K Explanation: Inserting 180,000 rows, the numbers in the table represent execution time (ms).

Batch Insert Performance Test Reference (10 Fields)

180K10K5K2K1K50010050
MySql 5.5 ExecuteAffrows11,1718663668083502434
MySql 5.5 ExecuteMySqlBulkCopy6,504399257116871001616
SqlServer Express ExecuteAffrows47,2042,2751,1084882791233516
SqlServer Express ExecuteSqlBulkCopy4,248127713048141110
PostgreSQL 10 ExecuteAffrows9,7865683361571023496
PostgreSQL 10 ExecutePgCopy4,0811679339211242

Test results are performed under the same operating system and all have been warmed up.

5. Dynamic Table Names

fsql.Insert(items).AsTable("Topic_201903").ExecuteAffrows(); // Insert into Topic_201903 table

6. Insert Specified Columns

var t3 = fsql.Insert(items).InsertColumns(a => a.Title).ExecuteAffrows();
//INSERT INTO `Topic`(`Title`)
//VALUES(?Title0), (?Title1), (?Title2), (?Title3), (?Title4),
//(?Title5), (?Title6), (?Title7), (?Title8), (?Title9)

var t4 = fsql.Insert(items).InsertColumns(a => new { a.Title, a.Clicks }).ExecuteAffrows();
//INSERT INTO `Topic`(`Clicks`, `Title`)
//VALUES(?Clicks0, ?Title0), (?Clicks1, ?Title1), (?Clicks2, ?Title2),
//(?Clicks3, ?Title3), (?Clicks4, ?Title4), (?Clicks5, ?Title5),
//(?Clicks6, ?Title6), (?Clicks7, ?Title7), (?Clicks8, ?Title8),
//(?Clicks9, ?Title9)

7. Ignore Columns

var t5 = fsql.Insert(items).IgnoreColumns(a => a.CreateTime).ExecuteAffrows();
//INSERT INTO `Topic`(`Clicks`, `Title`)
//VALUES(?Clicks0, ?Title0), (?Clicks1, ?Title1), (?Clicks2, ?Title2),
//(?Clicks3, ?Title3), (?Clicks4, ?Title4), (?Clicks5, ?Title5),
//(?Clicks6, ?Title6), (?Clicks7, ?Title7), (?Clicks8, ?Title8),
//(?Clicks9, ?Title9)

var t6 = fsql.Insert(items).IgnoreColumns(a => new { a.Title, a.CreateTime }).ExecuteAffrows();
//INSERT INTO `Topic`(`Clicks`)
//VALUES(?Clicks0), (?Clicks1), (?Clicks2), (?Clicks3), (?Clicks4),
//(?Clicks5), (?Clicks6), (?Clicks7), (?Clicks8), (?Clicks9)

8. Dictionary Insertion

var dic = new Dictionary<string, object>();
dic.Add("id", 1);
dic.Add("name", "xxxx");

fsql.InsertDict(dic).AsTable("table1").ExecuteAffrows();
// Note: List<Dictionary<string, object>> is for batch insertion

9. Import Table

int affrows = fsql.Select<Topic>()
    .Limit(10)
    .InsertInto(null, a => new Topic2
    {
        Title = a.Title
    });
INSERT INTO `Topic2`(`Title`, `Clicks`, `CreateTime`)
SELECT a.`Title`, 0, '0001-01-01 00:00:00'
FROM `Topic` a
LIMIT 10

Note: Since Clicks and CreateTime are not selected, values set using the target entity property [Column(InsertValueSql = xx)], or default values of the target entity properties in C# will be used.

10. MySql Insert Ignore Into

fsql.Insert<Topic>().MySqlIgnoreInto().AppendData(items).ExecuteAffrows();
///INSERT IGNORE INTO `Topic`(`Clicks`)
//VALUES(?Clicks0), (?Clicks1), (?Clicks2), (?Clicks3), (?Clicks4),
//(?Clicks5), (?Clicks6), (?Clicks7), (?Clicks8), (?Clicks9)

11. MySql On Duplicate Key Update

FreeSql.Provider.MySql and FreeSql.Provider.MySqlConnector support the MySQL-specific feature On Duplicate Key Update, which implements inserting or updating data (supports batch operations).

class TestInfo
{
    [Column(IsIdentity = true)]
    public int id { get; set; }
    public string title { get; set; }
    public DateTime time { get; set; }
}

var item = new TestInfo { id = 100, title = "title-100", time = DateTime.Parse("2000-01-01") };
fsql.Insert(item)
    .NoneParameter()
    .OnDuplicateKeyUpdate().ToSql();
//INSERT INTO `TestInfo`(`id`, `title`, `time`) VALUES(100, 'title-100', '2000-01-01 00:00:00.000')
//ON DUPLICATE KEY UPDATE
//`title` = VALUES(`title`), 
//`time` = VALUES(`time`)

Methods that can be called after OnDuplicateKeyUpdate():

Method NameDescription
IgnoreColumnsIgnore the columns to be updated, similar to IUpdate.IgnoreColumns
UpdateColumnsSpecify the columns to be updated, similar to IUpdate.UpdateColumns
SetManually specify the columns to be updated, similar to IUpdate.Set
SetRawSupplement to Set method, can pass SQL strings
ToSqlReturn the SQL statement to be executed
ExecuteAffrowsExecute and return the number of affected rows

Both IInsert and OnDuplicateKeyUpdate have IgnoreColumns and UpdateColumns methods.

When inserting entities or entity collections, and the time column is ignored, the code is as follows:

fsql.Insert(item)
    .IgnoreColumns(a => a.time)
    .NoneParameter()
    .OnDuplicateKeyUpdate().ToSql();
//INSERT INTO `TestInfo`(`id`, `title`) VALUES(200, 'title-200')
//ON DUPLICATE KEY UPDATE
//`title` = VALUES(`title`), 
//`time` = '2000-01-01 00:00:00.000'

We find that the UPDATE time part becomes a constant, rather than VALUES(\time`)`. The mechanism is as follows:

When columns exist in the insert part, they will be set in the update part as VALUES(\field`)`;

When columns do not exist in the insert part, they will be set as constants in the update part. When operating on entity arrays, this constant will be executed as case when ... end (similar to IUpdate).

12. PostgreSQL On Conflict Do Update

FreeSql.Provider.PostgreSQL supports PostgreSQL 9.5+ specific feature On Conflict(id) Do Update, which is used similarly to MySQL's OnDuplicateKeyUpdate.

fsql.Insert(items)
    .IgnoreColumns(a => a.time)
    .NoneParameter()
    .OnConflictDoUpdate().ToSql();
//INSERT INTO ""TestInfo""(""id"", ""title"") VALUES(200, 'title-200'), (201, 'title-201'), (202, 'title-202')
//ON CONFLICT(""id"") DO UPDATE SET
//""title"" = EXCLUDED.""title"",
//""time"" = CASE EXCLUDED.""id""
//WHEN 200 THEN '2000-01-01 00:00:00.000000'
//WHEN 201 THEN '2000-01-01 00:00:00.000000'
//WHEN 202 THEN '2000-01-01 00:00:00.000000' END::timestamp

API

MethodReturn ValueParametersDescription
AppendData<this>T1 | IEnumerable<T1>Append entities to be inserted
InsertIdentity<this>NoneSpecify auto-increment column
InsertColumns<this>LambdaColumns to be inserted
IgnoreColumns<this>LambdaColumns to be ignored
IgnoreInsertValueSql<this>LambdaColumns with InsertValueSql to be ignored
CommandTimeout<this>intCommand timeout setting (seconds)
WithTransaction<this>DbTransactionSet transaction object
WithConnection<this>DbConnectionSet connection object
ToSqlstringReturn the SQL statement to be executed
OnDuplicateKeyUpdateOnDuplicateKeyUpdate<T1>NoneMySQL-specific feature On Duplicate Key Update
OnConflictDoUpdateOnConflictDoUpdate<T1>NonePostgreSQL-specific feature On Conflict Do Update
ExecuteAffrowslongExecute SQL statement and return the number of affected rows
ExecuteIdentitylongExecute SQL statement and return auto-increment value
ExecuteInsertedList<T1>Execute SQL statement and return the inserted records
ExecuteSqlBulkCopyvoidSQL Server-specific feature, execute SqlBulkCopy batch insert
ExecutePgCopyvoidPostgreSQL-specific feature, execute Copy batch import data