-
-
Notifications
You must be signed in to change notification settings - Fork 501
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
adding InsertOrUpdate #103
Comments
MySQL: Sqlite: uses a special Postgres: supports Firebird: has SQL Server: doesn't have a upsert, but Oracle: doesn't appear to have upsert, but does have
|
Any update on this ? |
We are looking for something similar. Currently we are using SQL Server. This would allow use to use an Program.cs using SqlKata;
using SqlKata.Compilers;
//*****
var compiler = new SqlServerCompiler();
//*****
var upsertQuery = new Query("dbo.People")
.Where("Id", Guid.NewGuid(), true)
.Where("Version", "<", 10) //***** Concurrency;
.AsUpsert(new Dictionary<string, object>
{
{ "FirstName", "John" },
{ "LastName", "Doe" }
});
//*****
Console.WriteLine(compiler.Compile(upsertQuery).Sql);
//*****
Console.Read(); Result IF EXISTS (SELECT TOP 1 1 FROM [dbo].[People] WHERE [Id] = @p0)
UPDATE [dbo].[People] SET [FirstName] = @p1, [LastName] = @p2 WHERE [Id] = @p3 AND [Version] < @p4
ELSE
INSERT INTO [dbo].[People] ([FirstName], [LastName], [Id], [Version]) VALUES (@p5, @p6, @p7, @p8) This implementation allows for reusing all the existing components. The only required "tweak" is the addition of the It required the following changes: SqlKata.Executions\Query.Extensions.cs public static int Upsert(this Query query, IEnumerable<KeyValuePair<string, object>> values, IDbTransaction transaction = null, int? timeout = null)
{
return CreateQueryFactory(query).Execute(query.AsUpsert(values), transaction, timeout);
} QueryBuilder_Query.Upsert.cs_ public Query AsUpsert(IEnumerable<KeyValuePair<string, object>> values)
{
if (values == null || values.Any() == false)
{
throw new InvalidOperationException($"{values} cannot be null or empty");
}
Method = "upsert";
ClearComponent("upsert").AddComponent("upsert", new InsertClause
{
Columns = values.Select(x => x.Key).ToList(),
Values = values.Select(x => x.Value).ToList(),
});
return this;
} QueryBuilder\Clauses\ConditionClause.cs public abstract class AbstractCondition : AbstractClause
{
public bool IsOr { get; set; } = false;
public bool IsNot { get; set; } = false;
/// <summary>
/// Set True if the condition describes a key field being matched for equality.
/// </summary>
public bool IsKey { get; set; } = false;
} QueryBuilder\Base.Where.cs public abstract partial class BaseQuery<Q>
{
public Q Where(string column, string op, object value, bool isKey = false)
{
// omitted
return AddComponent("where", new BasicCondition
{
Column = column,
Operator = op,
Value = value,
IsOr = GetOr(),
IsNot = GetNot(),
IsKey = isKey
});
}
// omitted
} QueryBuilder\Compilers\Compiler.cs protected virtual SqlResult CompileRaw(Query query)
{
SqlResult ctx;
if (query.Method == "upsert")
{
ctx = CompileUpsertQuery(query);
}
// omitted
}
protected virtual SqlResult CompileUpsertQuery(Query query)
{
//*****
var ctx = new SqlResult { Query = query };
//***** From;
if (!(ctx.Query.GetOneComponent<AbstractFrom>("from", EngineCode) is FromClause fromClause))
throw new InvalidOperationException("No table set to upsert");
//***** Select;
var selectQuery = new Query(fromClause.Table);
var updateQuery = new Query(fromClause.Table);
var insertQuery = new Query(fromClause.Table);
//***** Conditions for select and update;
if (!ctx.Query.HasComponent("where", EngineCode))
throw new InvalidOperationException("No where conditions set. Requires at least one key column");
var conditions = ctx.Query.GetComponents<AbstractCondition>("where", EngineCode);
// for insert requires to be set as insert parameters.
var selectQueryConditions = new List<AbstractCondition>();
var updateQueryConditions = new List<AbstractCondition>();
foreach (var condition in conditions)
{
if (condition.IsKey) selectQueryConditions.Add((AbstractCondition)condition.Clone());
updateQueryConditions.Add((AbstractCondition)condition.Clone());
}
//*****
selectQuery.Clauses.AddRange(selectQueryConditions);
updateQuery.Clauses.AddRange(updateQueryConditions);
//*****
var updateInsertClause = ctx.Query.GetOneComponent<InsertClause>("upsert", EngineCode);
updateInsertClause = (InsertClause)updateInsertClause.Clone(); //***** Doesn't do deep clone;
updateInsertClause.Columns = new List<string>(updateInsertClause.Columns);
updateInsertClause.Values = new List<object>(updateInsertClause.Values);
updateInsertClause.Component = "update";
updateQuery.Clauses.Add(updateInsertClause);
//***** Add where as insert columns;
var insertInsertClause = ctx.Query.GetOneComponent<InsertClause>("upsert", EngineCode);
insertInsertClause = (InsertClause)insertInsertClause.Clone();
insertInsertClause.Columns = new List<string>(insertInsertClause.Columns);
insertInsertClause.Values = new List<object>(insertInsertClause.Values);
insertInsertClause.Component = "insert";
//***** Add conditions as insert values;
foreach (var condition in conditions)
if (condition is BasicCondition basicCondition)
{
insertInsertClause.Columns.Add(basicCondition.Column);
insertInsertClause.Values.Add(basicCondition.Value);
}
insertQuery.Clauses.Add(insertInsertClause);
//*****
selectQuery.SelectRaw("TOP 1 1");
//*****
var selectCtx = CompileSelectQuery(selectQuery);
var updateCtx = CompileUpdateQuery(updateQuery);
var insertCtx = CompileInsertQuery(insertQuery);
//*****
ctx.Bindings.AddRange(selectCtx.Bindings);
ctx.Bindings.AddRange(updateCtx.Bindings);
ctx.Bindings.AddRange(insertCtx.Bindings);
//*****
ctx.RawSql = $"IF EXISTS ({selectCtx.RawSql})\r\n\t{updateCtx.RawSql}\r\nELSE\r\n\t{insertCtx.RawSql}";
//*****
return ctx;
} Curious on your thoughts. Does anyone else have more experience with the other database systems for supporting this or similar fashion? |
@poostwoud I'm trying to help with this case but I only have experience with SQLSERVER and MySQL. I do have implemented this on the latest branch, but it looks like some thing are hardcoded in your proposal. Or.. I'm doing something wrong.. see "VariableName1" in the INSERT-statement
edit: I managed to fix the issue. |
@ahmad-moussawi , enclosed is the patch I used to implement @poostwoud changes, plus some additional.
|
Update data if certain conditions are met, insert the data otherwise.
[ ] MySql: on duplicate key update
[ ] SqlServer: #93
[ ] Postgres: (don't know)
The text was updated successfully, but these errors were encountered: