Функциональность многострочного добавления данных

Сложный

Функциональность многострочной вставки доступна на уровне класса Insert и ее работа определяется методом Values().

При вызове метода Values() все последующие вызовы Set() попадают в новый экземпляр ColumnsValues. При построении запроса, если в коллекции ColumnsValuesCollection встречается более одного набора данных, то будет построен запрос с несколькими блоками Values().

Пример
new Insert(UserConnection)
.Into("Table")
.Values()
    .Set("Column1", Column.Parameter(1))
    .Set("Column2", Column.Parameter(1))
    .Set("Column3", Column.Parameter(1))
.Values()
    .Set("Column1", Column.Parameter(2))
    .Set("Column2", Column.Parameter(2))
    .Set("Column3", Column.Parameter(2))
.Values()
    .Set("Column1", Column.Parameter(3))
    .Set("Column2", Column.Parameter(3))
    .Set("Column3", Column.Parameter(3))
.Execute();

В результате будет сформирован следующий SQL-запрос.

SQL-запрос
--Для MSSQL или PostgreSQL
INSERT INTO [dbo].[Table] (Column1, Column2, Column3)
VALUES (1, 1, 1),
    (2, 2, 2),
    (3, 3, 3)
    
-- Для Oracle
INSERT ALL
    into Table (column1, column2, column3) values (1, 1, 1)
    into Table (column1, column2, column3) values (2, 2, 2)
    into Table (column1, column2, column3) values (3, 3, 3)
SELECT * FROM dual

Особенности использования 

  1. При использовании Column.Parameter в выражении Set() необходимо помнить про ограничение 2100 параметров в MS SQL.
  2. Класс Insert не может самостоятельно разбивать запрос на несколько, если в нем находится больше параметров, чем нужно. Разбиение на несколько запросов должно быть реализовано разработчиком.

    Пример
    IEnumerable<IEnumerable<ImportEntity>> GetImportEntitiesChunks(IEnumerable<ImportEntity> entities,
                    IEnumerable<ImportColumn> keyColumns) {
        var entitiesList = entities.ToList();
        var columnsList = keyColumns.ToList();
        var maxParamsPerChunk = Math.Abs(MaxParametersCountPerQueryChunk / columnsList.Count + 1);
        var chunksCount = (int)Math.Ceiling(entitiesList.Count / (double)maxParamsPerChunk);
        return entitiesList.SplitOnParts(chunksCount);
    }
    
    var entitiesList = GetImportEntitiesChunks(entities, importColumns);
    entitiesList.AsParallel().AsOrdered()
        .ForAll(entitiesBatch => {
            try {
                var insertQuery = GetBufferedImportEntityInsertQuery();
                foreach (var importEntity in entitiesBatch) {
                    insertQuery.Values();
                    SetBufferedImportEntityInsertColumnValues(importEntity, insertQuery,
                            importColumns);
                    insertQuery.Set("ImportSessionId", Column.Parameter(importSessionId));
                }
                insertQuery.Execute();
            } catch (Exception e) {
                //...
            }
    });
    
  3. Класс Insert() не проводит валидацию на соответствие количества колонок и количества условий Set(). Например, есть результирующий SQL-запрос:

    SQL-запрос
    INSERT INTO [dbo].[Table] (Column1, Column2, Column3)
    Values (1, 2), (1, 2, 3)
    

В таком случае возникнет исключение на уровне работы СУБД. Подобная валидация не лежит в зоне ответственности класса Insert и зависит только от разработчика.