Is it possible to know when an upsert query has done nothing? - c#

I have a chain of queries that start with an upsert. I would like the skip all of the following queries if the first query is found to be in conflict. Is there a way for the upsert to return a value that indicates a conflict exists? I am using the Npgsql nugget on c#

Npgsql's ExecuteNonQuery returns the number of rows affected, which will be 0 if all inserted rows had a conflict.

Related

EntityFramework BulkInsert extension and Triggers?

I'm using the EntityFramework BulkInsert extension to insert large datasets into my database. However, there is an AFTER INSERT trigger on the table that I'm inserting data into that doesn't seem to be firing. Is this a limitation of the extension or is there a way to ensure that the trigger fires when the operation completes?
It turns out I just wasn't looking hard enough. There are some overloads for the main BulkInsert method that are not listed anywhere in the documentation and I could not find them through any Google searches, but one of the overloads allow for flags. One of the flags is SqlBulkCopyOptions.FireTriggers.
It is used like: context.BulkInsert(values, SqlBulkCopyOptions.FireTriggers). Using this method, each row is processed individually by the trigger, and everything works as it should.
EDIT: Answer comment
can you explain what the CheckConstraint will do?
A constraint is when you add some checks such as the value must be between 10 and 50 to be valid and inserted.
So if you don't check constraint, a value of 5 will be inserted without a problem even if out of range. If you check constraint, an error will be throw:
The INSERT statement conflicted with the CHECK constraint "CHK_ColumnWithConstraint". The conflict occurred in database "db_2560", table "dbo.Customers", column 'ColumnWithConstraint'.
The statement has been terminated.
Here is an online example: https://dotnetfiddle.net/AMgTYQ
That's why I recommend SqlBulkCopyOptions.FireTriggers | SqlBulkCopyOptions.CheckConstraints
If the table have a trigger, you probably when to fire them. If a column has a constraint, you probably want the operation to throw an error if the value is not valid with the check.

How to know how many persistent objects were deleted using Session.Delete(query);

We are refactoring a project from plain MySQL queries to the usage of NHibernate.
In the MySQL connector there is the ExecuteNonQuery function that returns the rows affected. So
int RowsDeleted = ExecuteNonQuery("DELETE FROM `table` WHERE ...");
would show me how many rows where effectively deleted.
How can I achieve the same with NHibernate? So far I can see it is not possible with Session.Delete(query);.
My current workaround is first loading all of the objects that are about to be deleted and delete them one-by-one, incrementing a counter on each delete. But that will cost performance I may assume.
If you don't mind that nHibernate will create delete statements for each row and maybe additional statements for orphans and/or other relationships, you can use session.Delete.
For better performance I would recommend to do batch deletes (see example below).
session.Delete
If you delete many objects with session.Delete, nHibernate makes sure that the integrity is preserved, it will load everything into the session if needed anyways. So there is no real reason to count your objects or have a method to retrieve the number of objects which have been deleted, because you would simply do a query before running the delete to determine the number of objects which will be affected...
The following statement will delete all entities of type post by id.
The select statement will query the database only for the Ids so it is actually very performant...
var idList = session.Query<Post>().Select(p => p.Id).ToList<int>();
session.Delete(string.Format("from Post where Id in ({0})", string.Join(",", idList.ToArray())));
The number of objects deleted will be equal to the number of Ids in the list...
This is actually the same (in terms of queries nHibernate will fire against your database) as if you would query<T> and loop over the result and delete all of them one by one...
Batch delete
You can use session.CreateSqlQuery to run native SQL commands. It also allows you to have input and output parameters.
The following statement would simply delete everything from the table as you would expect
session.CreateSQLQuery(#"Delete from MyTableName");
To retrieve the number of rows delete, we'll use the normal TSQL ##ROWCOUNT variable and output it via select. To retrieve the selected row count, we have to add an output parameter to the created query via AddScalar and UniqueResult simple returns the integer:
var rowsAffected = session.CreateSQLQuery(#"
Delete from MyTableName;
Select ##ROWCOUNT as NumberOfRows")
.AddScalar("NumberOfRows", NHibernateUtil.Int32)
.UniqueResult();
To pass input variables you can do this with .SetParameter(<name>,<value>)
var rowsAffected = session.CreateSQLQuery(#"
DELETE from MyTableName where ColumnName = :val;
select ##ROWCOUNT NumberOfRows;")
.AddScalar("NumberOfRows", NHibernateUtil.Int32)
.SetParameter("val", 1)
.UniqueResult();
I'm not so confortable with MySQL, the example I wrote is for MSSQL, I think in MySQL the ##ROWCOUNT equivalent would be SELECT ROW_COUNT();?

ExecuteNonQuery Result in C#

How can i know if i create a database successfully? I am using "CREATE DATABASE DemoDB" as a SQL command. ExecuteNonQuery() method returns 0. What should i use to understand if i created a database successfully?
As MSDN says:
For UPDATE, INSERT, and DELETE statements, the return value is the
number of rows affected by the command. When a trigger exists on a
table being inserted or updated, the return value includes the number
of rows affected by both the insert or update operation and the number
of rows affected by the trigger or triggers. For all other types of
statements, the return value is -1. If a rollback occurs, the return
value is also -1.
ExecuteNonQuery will return 0 for a CREATE DATABASE command because it returns the rows changed by your query.
This will return some rows if the DB exists:
SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = 'DemoDB'
ExecuteNonQuery throws a MySqlException exception if there is an error. It returns 0 if successful but you don't need to check the return value; if it returns normally it has succeeded. For example here is the exception I get when I try to create a database that already exists:
Can't create database 'name'; database exists

ExecuteNonQuery doesn't return results

This is my (rough) code (DAL):
int i;
// Some other declarations
SqlCommand myCmdObject = new SqlCommand("some query");
conn.open();
i = myCmdObject.ExecuteNonQuery();
conn.close();
The problem is: Even though there is a record present on my SELECT query, the value in i remains -1.
What could be the problem?
What kind of query do you perform? Using ExecuteNonQuery is intended for UPDATE, INSERT and DELETE queries. As per the documentation:
For UPDATE, INSERT, and DELETE
statements, the return value is the
number of rows affected by the
command. When a trigger exists on a
table being inserted or updated, the
return value includes the number of
rows affected by both the insert or
update operation and the number of
rows affected by the trigger or
triggers. For all other types of
statements, the return value is -1.
Whenever you want to execute an SQL statement that shouldn't return a value or a record set, the ExecuteNonQuery should be used.
So if you want to run an update, delete, or insert statement, you should use the ExecuteNonQuery. ExecuteNonQuery returns the number of rows affected by the statement. This sounds very nice, but whenever you use the SQL Server 2005 IDE or Visual Studio to create a stored procedure it adds a small line that ruins everything.
That line is: SET NOCOUNT ON; This line turns on the NOCOUNT feature of SQL Server, which "Stops the message indicating the number of rows affected by a Transact-SQL statement from being returned as part of the results" and therefore it makes the stored procedure always to return -1 when called from the application (in my case a web application).
In conclusion, remove that line from your stored procedure, and you will now get a value indicating the number of rows affected by the statement.
Happy programming!
http://aspsoft.blogs.com/jonas/2006/10/executenonquery.html
You use EXECUTENONQUERY() for INSERT,UPDATE and DELETE.
But for SELECT you must use EXECUTEREADER().........
Because the SET NOCOUNT option is set to on. Remove the line "SET NOCOUNT ON;" in your query or stored procedure.
See more at SqlCommand.ExecuteNonQuery() returns -1 when doing Insert / Update / Delete.
Could you post the exact query? The ExecuteNonQuery method returns the ##ROWCOUNT Sql Server variable what ever it is after the last query has executed is what the ExecuteNonQuery method returns.
The ExecuteNonQuery method is used for SQL statements that are not queries, such as INSERT, UPDATE, ... You want to use ExecuteScalar or ExecuteReader if you expect your statement to return results (i.e. a query).
From MSDN: SqlCommand.ExecuteNonQuery Method
You can use the ExecuteNonQuery to
perform catalog operations (for
example, querying the structure of a
database or creating database objects
such as tables), or to change the data
in a database without using a DataSet
by executing UPDATE, INSERT, or DELETE
statements.
Although the ExecuteNonQuery returns
no rows, any output parameters or
return values mapped to parameters are
populated with data.
For UPDATE, INSERT, and DELETE
statements, the return value is the
number of rows affected by the
command. When a trigger exists on a
table being inserted or updated, the
return value includes the number of
rows affected by both the insert or
update operation and the number of
rows affected by the trigger or
triggers. For all other types of
statements, the return value is -1. If
a rollback occurs, the return value is
also -1.
You are using SELECT query, thus you get -1
If what you want is to get just a single integer from the query, use:
myCmdObject.ExecuteScalar()
if you want to run an update, delete,
or insert statement, you should use
the ExecuteNonQuery. ExecuteNonQuery
returns the number of rows affected by
the statement.
How to Set Count On

Insert a Row Only if a Row does not Exist

I am building a hit counter. I have an article directory and tracking unique visitors. When a visitor comes i insert the article id and their IP address in the database. First I check to see if the ip exists for the article id, if the ip does not exist I make the insert. This is two queries -- is there a way to make this one query
Also, I am not using stored procedures I am using regular inline sql
Here are some options:
INSERT IGNORE INTO `yourTable`
SET `yourField` = 'yourValue',
`yourOtherField` = 'yourOtherValue';
from MySQL reference manual: "If you use the IGNORE keyword, errors that occur while executing the INSERT statement are treated as warnings instead. For example, without IGNORE, a row that duplicates an existing UNIQUE index or PRIMARY KEY value in the table causes a duplicate-key error and the statement is aborted.".) If the record doesn't yet exist, it will be created.
Another option would be:
INSERT INTO yourTable (yourfield,yourOtherField) VALUES ('yourValue','yourOtherValue')
ON DUPLICATE KEY UPDATE yourField = yourField;
Doesn't throw error or warning.
Yes, you create a UNIQUE constraint on the columns article_id and ip_address. When you attempt to INSERT a duplicate the INSERT will be refused with an error. Just answered the same question here for SQLite.
IF NOT EXISTS (SELECT * FROM MyTable where IPAddress...)
INSERT...
Not with SQL Server. With T-SQL you have to check for the existence of a row, then use either INSERT or UPDATE as appropriate.
Another option is to try UPDATE first, and then examine the row count to see if there was a record updated. If not, then INSERT. Given a 50/50 chance of a row being there, you have executed a single query 50% of the time.
MySQL has a extension called REPLACE that has the capability that you seek.
The only way I can think of is execute dynamic SQL using the SqlCommand object.
IF EXISTS(SELECT 1 FROM IPTable where IpAddr=<ipaddr>)
--Insert Statement
I agree with Larry about using uniqueness, but I would implement it like this:
IP_ADDRESS, pk
ARTICLE_ID, pk, fk
This ensures that a record is unique hit. Attempts to insert duplicates would get an error from the database.
I would really use procedures! :)
But either way, this will probably work:
Create a UNIQUE index for both the IP and article ID columns, the insert query will fail if they already exist, so technically it'll work! (tested on mysql)
try this (it's a real kludge, but it should work...):
Insert TableName ([column list])
Select Distinct #PK, #valueA, #ValueB, etc. -- list all values to be inserted
From TableName
Where Not Exists
(Select * From TableName
Where PK == #PK)

Categories