質問

I'm trying to delete all rows in a table using Castle ActiveRecord. Normally, I would do this:

DB.ParseError.DeleteAll();

However, this table happens to have somewhere around 1.9 million rows in it. What the above command will do is issue a separate SELECT and DELETE for each row in the table, looking something like this:

SELECT page0_.Id as Id29_0_, ... FROM Indexer.Pages page0_ WHERE page0_.Id=:p0;:p0 = b03665aa-37d0-4a2d-a04c-c232ebd94dbc [Type: Guid (0)]
SELECT page0_.Id as Id29_0_, ... FROM Indexer.Pages page0_ WHERE page0_.Id=:p0;:p0 = 11cb69e3-1c6a-4ac1-908b-084dfe859639 [Type: Guid (0)]
--- 1.9 million more of these
DELETE FROM Indexer.ParseErrors WHERE Id = :p0;:p0 = b03665aa-37d0-4a2d-a04c-c232ebd94dbc [Type: Guid (0)]
DELETE FROM Indexer.ParseErrors WHERE Id = :p0;:p0 = 11cb69e3-1c6a-4ac1-908b-084dfe859639 [Type: Guid (0)]
--- 1.9 million more of these

This takes... well I don't know how long it takes because I don't have the patience to actually let it finish.

What I want it to do is issue one SQL statement; this one:

DELETE FROM Indexer.ParseErrors;

Here's what I've tried so far:

ISessionFactoryHolder holder = ActiveRecordMediator.GetSessionFactoryHolder();
ISession session = holder.CreateSession(typeof(DB.ParseError));
IDbCommand cmd = session.Connection.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "DELETE FROM Indexer.ParseErrors;";
cmd.CommandTimeout = 600;
cmd.ExecuteNonQuery();
holder.ReleaseSession(session);

This appears to work, however it seems like a hack to me as it completely avoids the model and ActiveRecord framework. Plus, this probably doesn't play well with any caching NHibernate has.

Is there a more official way to delete a table, without doing 2 SQL statements per row?

役に立ちましたか?

解決

For a table with that many records, you definitely don't want it to create an entity for each row and then delete it - it will take forever.

If you don't have any cascading rules defined in your mappings that aren't enforced in your database, then the easiest is to use a SQL query:

ActiveRecordBase<ParseError>.Execute(
    (s, i) => s.CreateSQLQuery("DELETE FROM ParseErrors").ExecuteUpdate(),
                                           null);

This will invalidate the NHibernate caches (so it will play nicely with the NHibernate second level cache) but it won't do any cascades not defined in the db (I suspect that will be fine for your case). If there are any cascades that aren't in the db, then you'd have to do them manually.

If you really really want it to use the entities to delete, then use a StatelessSessionScope at least which will prevent it from adding all the entities to the session at least.

UPDATE:

The Execute() method is a protected member of ActiveRecordBase<T>, so to call it from outside the class you'll have to wrap it in a public static method.

他のヒント

You can delete all records from a table with nHibernate with the following statement. Depending on the cascade rules. It is possible that you have to clear an other table first.

session.Delete("from Indexer.ParseErrors p");

Use full link

Every time I've dealt with this in the past I've done something like:

session.CreateSQLQuery("DELETE FROM FOO").ExecuteUpdate();

Yes, it bypasses all sorts of stuff but I've never had a problem with that.

Ayende has a post on this which uses bulkcopy to do it but doesnt seem to address your concerns about caching.

Hello Mike Christensen,

yes ther is a way. It isn't a good way (I think), but it is better then plain SQL:

session.CreateQuery(@"UPDATE " + typeof(?).FullName + " SET Property = :value")
       .SetParameter("value",valueVariable)
       .ExecuteUpdate();

Batch Update in NHibernate

Greetings Juy Juka

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top