-
Notifications
You must be signed in to change notification settings - Fork 35
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
Is there a way to do upserts in PostgreSQL #22
Comments
Hi @exyi, I think currently this lib doesn't allow this. Adding PR would be great indeed, but is this kind of syntax available for other databases (MySQL, MSSQL)? |
I don't really know - from googling it seems that MySQL support something similar using How should we structure the API though? I'd imagine something like this:
or
Try insert will do Other option would be an Other option would be to extend the
or
|
Thanks for the info.
If there is no support across all three major database engines, then it should not be part of this library - how would DSL for that be handled on unsupported DB? |
Ok... I thought it could be part of the PostgreSQL module: https://github.com/Dzoukr/Dapper.FSharp/blob/master/src/Dapper.FSharp/PostgreSQL.fs Then the method |
If it would be only change on each module, then it's ok, but I thought there will be some new DSL keywords? I am confused now. 😄 |
Yea, I though we could add the |
There is one builder (https://github.com/Dzoukr/Dapper.FSharp/blob/master/src/Dapper.FSharp/Builders.fs) = one DSL for all three database engines (resulting in a same record definition https://github.com/Dzoukr/Dapper.FSharp/blob/master/src/Dapper.FSharp/Dapper.FSharp.fs) so that's the problem. Adding new keyword on DSL level should be done only if we can support it everywhere. |
Out of my depth here but just chiming in to say that to my knowledge MSSQL uses the MERGE statement/syntax for upserts. Possible i misunderstand the conversation though. Looking here because i'm actually attempting to use the library now for such a situation and it'd help to have. |
I am currently working with SQLite and I wanted to use its upsert capabilities. Modifying the library to be able to handle upsert across SQL dialects would be awesome, however it won't happen without someone putting in some serious architecting due diligence to make sure it fits in properly with everything else. Some background on upsertsI learned that SQLite has two slightly different variations of upsert: UPDATE ON CONFLICTThis is the main SQLite upsert. It inserts, and then if the conflict column(s) already exist it will do an update.
INSERT OR REPLACEThis is similar, except that no conflict column is specified, and it will just replace all values with the given insert values. UPDATE ON CONFLICT USAGEHere's a sample of an UPDATE ON CONFLICT upsert query: /// Inserts or updates a supervisor
member this.UpsertSupervisor(supervisor: Supervisor) =
let record =
{ db.Supervisor.EmployeeId = supervisor.Id
db.Supervisor.EmployeeNo = supervisor.Number
db.Supervisor.Email = supervisor.Email }
use conn = openConnection()
insert {
into supervisorTable
value record
}
|> Sqlite.updateOnConflict conn (nameof record.EmployeeId)
[
nameof record.EmployeeNo
nameof record.Email
]
|> Task.awaitIgnore Here is the magic /// Transforms a regular INSERT query into an UPSERT by appending "ON CONFLICT DO UPDATE SET".
/// NOTE: This can only be called on one record at a time.
let updateOnConflict (conn: SqliteConnection) (onConflictColumn: string) (columnsToUpdate: string list) (query: InsertQuery<'T>) =
let query, parameters = query |> Deconstructor.insert
let setLines =
columnsToUpdate
|> List.map (fun column -> $"{column}=@{column}0\n") // Dapper.FSharp parameter names = @FirstName0, @LastName0, etc...
|> (fun lines -> System.String.Join(",", lines))
let upsertQuery =
System.Text.StringBuilder(query)
.AppendLine($"ON CONFLICT({onConflictColumn}) DO UPDATE SET")
.AppendLine(setLines)
.ToString()
conn.ExecuteAsync(upsertQuery, parameters) Again, this is hacking the library -- it makes the assumption that parameters will be named according to the current parameter naming convention (which could always change as it's an implementation detail). INSERT OR REPLACE implementation is even easier because it's just a matter of replacing "INSERT" with "INSERT OR REPLACE": let insertOrReplace (conn: SqliteConnection) (query: InsertQuery<'T>) =
let query, parameters = query |> Deconstructor.insert
conn.ExecuteAsync(query.Replace("INSERT", "INSERT OR REPLACE"), parameters) |
MERGE in sql server has issues ….. https://sqlperformance.com/2020/09/locking/upsert-anti-pattern |
I'd rather say that SQL server is an antipattern... His amazing solution is not that great, since it will only work properly in SERIALIZABLE mode which will lead to even more problems on this joke DBMS (otherwise you might get conflict. The whole point of doing upserts is to get rid of them). If we have to support Microsoft SQL, I'm definitely not going to implement it. I'll close this issue, as it does not seem as easy to solve given the constants. Probably best to rely on custom helper functions for a specific use case... |
I'm relying on using
INSERT ... ON CONFLICT (internal_id) DO NOTHING
or ``INSERT ... ON CONFLICT (internal_id) DO UPDATE ...` operations. Especially those updates are tedious to write for wider tables (each property name repeated like 5 times...). Is there a way this library could help me?If the answer is no, but you'd like to have this functionality, I'd be happy to implement that.
The text was updated successfully, but these errors were encountered: