Optimize delete query generated by Castle ActiveRecord - castle-activerecord

Lets say I have Id (primary key) list that I want to delete (e.g 1, 2, 3, 4).
Using this query :
Console.WriteLine ("DELETE DATA :");
ActiveRecordMediator<PostgrePerson>.DeleteAll ("Id IN (1, 2, 3, 4)");
I expect the console output is :
DELETE DATA :
NHibernate: DELETE FROM Person WHERE Id IN (1, 2, 3, 4)
but, the actual console output is (I use showsql option) :
DELETE DATA :
NHibernate: select postgreper0_.Id as Id5_, postgreper0_.Name as Name5_, postgreper0_.Age as Age5_, postgreper0_.Address as Address5_ from Person postgreper0_ w
here postgreper0_.Id in (1 , 2 , 3 , 4)
NHibernate: DELETE FROM Person WHERE Id = :p0;:p0 = 1
NHibernate: DELETE FROM Person WHERE Id = :p0;:p0 = 2
NHibernate: DELETE FROM Person WHERE Id = :p0;:p0 = 3
NHibernate: DELETE FROM Person WHERE Id = :p0;:p0 = 4
What should I do to make Castle ActiveRecord generate the expected (optimized) query?
Update
This is my implementation based on accepted answer.
int[] idList = GetIdList ();
ActiveRecordMediator<PostgrePerson>.Execute ((session, obj) => {
string hql = "DELETE PostgrePerson WHERE Id IN (:idList)";
return session.CreateQuery (hql)
.SetParameterList ("idList", idList)
.ExecuteUpdate ();
}, null);

Use the Execute callback method and run a DML-style HQL DELETE on the NHibernate ISession.

Related

SQLAlchemy find IDs from a list that don't exist in a table

I have a table with some records(every record has an id - primary key). Now I need to select all ids from specified list/set that don't exist in the table. I am using postgres database and sqlalchemy as ORM. Please suggest have to perform such query.
May not be efficient for a very large set, but straightforward flow for demonstration:
my_list = [1, 2, 3, 4, 5]
missing_from_table = []
for id in my_list:
result = session.query(Model.id).get(id) # result is a tuple
if not result:
missing_from_table.append(id)
print(f'The following ids are not in the table: {missing_from_table}')
Another option would be:
my_list = [1, 2, 3, 4, 5]
all_ids = [r.id for r in session.query(Model.id).all()]
missing_from_table = [id for id in my_list if id not in all_ids]
Here's an option that operates completely in the database. It bypasses the ORM, but still utilizes SQLAlchemy's conveniences for the session and object mapping.
from sqlalchemy import text
my_list = [1, 2, 3, 4, 5, 6]
query = text("""
SELECT array_agg(id)
FROM unnest(:my_list) id
WHERE id NOT IN (
SELECT
id
FROM
insert-table-name-here
)
""")
# Belows assumes session is a defined SQLAlchemy database session
missing_ids = session.execute(query, {'my_list': my_list}).scalar()
print(f'The following ids from my_list are missing in table: {missing_ids}')
This option uses SQLAlchemy and is very efficient.
from sqlalchemy.sql import Values, select, column
new_items_values = Values(column("id"), name="new_items").data(
items_ids
)
query = (
select(new_items_values.c.id)
.outerjoin(
Model,
Model.id == new_items_values.c.id,
)
.where(Model.id == None)
)
# assumes `session` is already defined
missing_ids = set(
session.execute(
query,
{"items_ids": items_ids},
)
or []
) # a list of unary tuples, e.g. [('id1',), ('id4',)]
This generates SQL like this:
select new_items.id
from (
values ('id1'),('id2'),('id3'), ('id4')
) as new_items(id)
left join model on model.id = new_items.id
where model.id is null;
(I compared this to #Matt Graham's solution below, using WHERE id NOT IN; this solution takes 20ms; the other one took literal minutes on my database.)

How to delete rows in sqlite with a WHERE clause that references multiple tables?

Given the following tables:
acl (acl_id integer, name character)
ace (acl_id integer, users_id integer)
users (users_id integer, login character)
And some sample data:
acl (0, 'view') (1, 'sell'), (2, 'void')
ace (0, 9), (1, 9), (2, 9), (0, 8)
users (8, 'joe') (9, 'john')
I attempted the following:
DELETE FROM ace
WHERE ace.acl_id = acl.acl_id AND acl.name = ?
AND ace.users_id = users.users_id AND users.login = ?
but this fails in sqlite3_prepare_v2() with SQLITE_ERROR "no such column: acl.acl_id" (which isn't very helpful)
I get the same result with:
DELETE FROM ace
WHERE acl_id = acl.acl_id AND acl.name = ?
AND users_id = users.users_id AND users.login = ?
Thankfully, the scary:
DELETE FROM ace,acl,users
WHERE ace.acl_id = acl.acl_id AND acl.name = ?
AND ace.users_id = users.users_id AND users.login = ?
fails with a syntax error on the first comma.
If I got far enough to bind the values 'sell' and 'john' to the parameters, and to step the query, then I would expect the (1, 9) row of the ace table to be deleted.
Is this possible to accomplish without doing two SELECTs first to get the acl_id and users_id values?
With some help from linked Q/A in the comments, I was able to come up with what seems like a functional query:
DELETE FROM ace
WHERE acl_id = (
SELECT acl_id
FROM acl
WHERE name = ?
)
AND users_id = (
SELECT users_id
FROM users
WHERE login = ?
)
and I have to say, that's just ridiculous.
"SQL, the EBCDIC of query languages"

asp.net mvc5 , raw sql : Display data in Views generated using aggregate function

I am using ASP.NET MVC, EF and SQL Server 2014.
I want to generate a view which would show total sick leave and annual leave an employee with employee id. I found a LINQ query very complicated and I don't know how to use it in Asp.net MVC. I wrote a controller for this purpose and now I have no idea how to display this data in the view.
Please help me with this. If there is better way of doing this or if I am making mistakes, please let me know.
SQL queries are below:
SELECT
[EmployeeId], SUM(AssignedLeaveDay) AS Total_Annual_Leave
FROM
AssignLeaves
WHERE
[EmployeeId] = 1 AND LeaveTypeId = 4
GROUP BY
[EmployeeId]
SELECT
[EmployeeId], SUM(AssignedLeaveDay) AS Total_Sick_Leave
FROM
AssignLeaves
WHERE
[EmployeeId] = 1 AND LeaveTypeId = 5
GROUP BY
[EmployeeId]
enter image description here
//Controller side
var Data = from data in AssignLeaves.Where(s => s.EmployeeId == 1 && s.LeaveTypeId == 4)
group data by data.EmployeeId into g
select new
{
EmployeeId = g.Key,
Total_Annual_Leave = g.Sum(x => x.AssignedLeaveDay)
};
var SickLeaveData = from data in AssignLeaves.Where(s => s.EmployeeId == 1 && s.LeaveTypeId == 5)
group data by data.EmployeeId into g
select new
{
EmployeeId = g.Key,
Total_Sick_Leave = g.Sum(x =>
x.AssignedLeaveDay)
};
You get Total Annual leave by: Data.FirstOrDefault().Total_Annual_Leave ;
You get Total_Sick_Leave by: SickLeaveData.FirstOrDefault().Total_Sick_Leave;

Select specific columns with repository in Entity Framework Core

I have a big table with a binary column for picture. I need to show contents of this table in a view and make it searchable. I have tried only selecting a subset of columns that are needed in this. However, the generated SQL always has all the columns of the table in the generated query.
public IQueryable<ApplicantDTO> GetApplicantQueryable()
{
return DataContext.Applicants
.Include(a => a.Nationality)
.Select(x => new ApplicantDTO
{
Id = x.Id,
BirthDate = x.BirthDate,
Gender = x.Gender,
FirstName = x.FirstName,
LastName = x.LastName,
OtherName = x.OtherName,
MobileNo = x.MobileNo,
Age = x.Age,
Nationality = x.Nationality.National,
Admitted = x.admitted,
Completed = x.Completed
})
.Where(a => a.Admitted == false && a.Completed == true)
.OrderBy(a => a.LastName)
.AsNoTracking();
}
But instead of just specifying the above rows, the generated SQL from profiler is
SELECT
[a].[Id], [a].[BirthDate], [a].[BirthPlace], [a].[CompleteDate],
[a].[Completed], [a].[ContentType], [a].[CreateDate],
[a].[Denomination], [a].[Disability], [a].[Email],
[a].[FirstName], [a].[Gender], [a].[HomeTown], [a].[LastName],
[a].[MarryStatus], [a].[MatureApp], [a].[MobileNo], [a].[NationalityID],
[a].[OtherName], [a].[Passport], [a].[Pin], [a].[PostalAddress],
[a].[Region], [a].[Religion], [a].[ResAddress], [a].[SerialNo],
[a].[Title], [a].[VoucherID], [a].[admitted], [a.Nationality].[National]
FROM
[Applicants] AS [a]
INNER JOIN
[Nationality] AS [a.Nationality] ON [a].[NationalityID] = [a.Nationality].[Id]
WHERE
([a].[admitted] = 0)
AND ([a].[Completed] = 1)
ORDER BY
[a].[LastName]
With all the underlying columns all included in the query.
I tried putting it in an anonymous type before casting it to the ApplicantDTO but still the same effect.
What's wrong?

Linq Query Condition for multiple row

I tried to solve one query from last 2 days but didn't.
It looks easy to understand but can't made.
There are two column in Table for example:
ResourceId || MappingId
1 2
1 3
2 2
2 4
3 2
3 4
4 2
4 5
5 2
5 4
This is one table which have two fields ResourceId and MappingId.
Now I want resourceId which have Mappingid {2,4}
Means answer must be ResourceId {2,3,5}
How can I get this answer in Linq Query?
Use Contains of collection. This method can be translated by Entity Framework into SQL IN operator:
int[] mappingIds = { 2, 4 };
var resources = from t in table
where mappingIds.Contains(t.MappingId)
select t.ResourceId;
Lambda syntax:
var resources = table.Where(t => mappingIds.Contains(t.MappingId))
.Select(t => t.ResourceId);
Generated SQL will look like:
SELECT [Extent1].[ResourceId]
FROM [dbo].[TableName] AS [Extent1]
WHERE [Extent1].[MappingId] IN (2,4)
UPDATE: If you want to get resources which have ALL provided mapping ids, then
var resources = from t in table
group t by t.ResourceId into g
where mappingIds.All(id => g.Any(t => t.Id == id))
select g.Key;
Entity Framework is able to translate this query into SQL, but it will not be that beautiful as query above.
IQueryable<int> resourceIds = table
// groups items by ResourceId
.GroupBy(item => item.ResourceId)
// consider only group where: an item has 2 as MappingId value
.Where(group => group.Select(item => item.MappingId).Contains(2))
// AND where: an item has 4 as MappingId value
.Where(group => group.Select(item => item.MappingId).Contains(4))
// select Key (= ResourceId) of filtered groups
.Select(group => group.Key);

Resources