Функциональность многострочного добавления данных
Glossary Item Box
Общие сведения
Начиная с версии 7.12.4 в приложении появилась функциональность многострочной вставки. Она доступна на уровне класса 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-запрос:
--Для 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
Код в примерах показывает различные способы передачи параметров в запрос. При разработке проекта учитывайте, что в метод Column.Const не следует передавать параметры, контролируемые пользователем, так как это может привести к потенциальным sql-инъекциям.
Особенности использования
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-запрос:
INSERT INTO [dbo].[Table] (Column1, Column2, Column3) Values (1, 2), (1, 2, 3)
В таком случае возникнет исключение на уровне работы СУБД. Подобная валидация не лежит в зоне ответственности класса Insert и зависит только от разработчика.