I would like to query a table having multiple rows, each with a timestamp with data coming at ten minute intervals. I would like to find the beginning of any missing data, which is where there is not a timestamp equaling the next ten minute interval, like this:
select a.[timestamp]
from [table] as a
where not exists (select 1
from [table] as b
where a.[id] = b.[id]
and b.[timestamp] = dateadd(mi, 10, a.[timestamp]))
order by a.[timestamp]
I have this so far, but I fail to see how to build the query to let me do the b.[timestamp] = dateadd(mi, 10, a.[timestamp]) in the query above:
Table tableAlias = null;
IList<DateTimeOffset> dateTimeOffsets = session.QueryOver(() => tableAlias)
.WithSubquery
.WhereNotExists(QueryOver.Of<Table>()
.Where(x => x.Id == tableAlias.Id)
.And(Restrictions.Eq(Projections.SqlFunction("addminutes",
NHibernateUtil.DateTimeOffset,
new[]
{
Projections.Property("Timestamp"),
Projections.Constant(10)
}),
<insert timestamp property again here>))
.Select(Projections.Constant(1)))
.Select(x => x.Timestamp)
.List<DateTimeOffset>();
I can not get my head round the restriction on the sqlfuntion part - Nhibernate just won't let me do the comparison of the sqlfunction and my timestamp.
I hope I am on the right track with the code above, but please correct me if I'm totally off with my attempt at solving this...
Kind regards
You are on the right track. You need to use Restrictions.EqProperty instead of Restrictions.Eq since you are comparing two projections and not a projection and a constant value.
Also, you can use an Expression to access the TimeStamp property of the inner query instead of using a string.
The following code works for me on Sql Server 2008, but it may require a bit of tweaking for other database engines:
Table a = null;
session.QueryOver<Table>(() => a)
.WithSubquery
.WhereNotExists(
QueryOver.Of<Table>()
.Where(t => t.Id == a.Id)
.And(Restrictions.EqProperty(
Projections.SqlFunction(
"addminutes",
NHibernateUtil.DateTimeOffset,
Projections.Constant(10),
Projections.Property(() => a.TimeStamp)),
Projections.Property(() => a.TimeStamp)))
.Select(Projections.Constant(1)))
.Select(t => t.TimeStamp)
.List<DateTimeOffset>();
Which should generate the following SQL (at least on Sql Server 2008):
SELECT this_.TimeStamp as y0_
FROM [Table] this_
WHERE not exists (SELECT 1 /* #p0 */ as y0_
FROM [Table] this_0_
WHERE this_0_.Id = this_.Id
and dateadd(minute, 10 /* #p1 */, this_.TimeStamp) = this_.TimeStamp)
Related
I have two tables: tableA and tableB, both have an attribute "CommissionNumber" which contains strings in the form of D123456789 (one letter followed by a fixed number of digits).
I need to find the commissionnumbers in table A, that are not in table B.
In SQL, this could look like:
SELECT *
FROM tableA
WHERE CommissionNumber NOT IN
(
select CommissionNumber from tableB
)
Which gives me no results. However, if i try this:
var tableA= dbContext.tableA.Select(x => x.CommissionNumber).ToList();
var tableB= dbContext.tableB.Select(x => x.CommissionNumber).ToList();
IEnumerable<string> missingFiles = tableA.Except(tableB);
I get 92 hits. I don't understand what's wrong, my SQL query of the use of the .Except function.
Any ideas?
You have created two LINQ queries which retrieves all data from two tables into the memory and applied Except. It is wrong if you care about performance.
If you want to create SQL in the same way, LINQ query should be written accordingly. LINQ equivalent for IN operator is Contains.
var query = dbContext.tableA
.Where(x => !dbContext.tableB.Select(b => b.CommissionNumber).Contains(x.CommissionNumber));
Or by EXISTS which have analogue Any
var query = dbContext.tableA
.Where(x => !dbContext.tableB.Any(b => b.CommissionNumber == x.CommissionNumber));
Also the same result you can achieve by LEFT JOIN
var query =
from a in dbContext.tableA
join b in dbContext.tableB on a.CommissionNumber equals b.CommissionNumber into gj
from b in gj.DefaultIfEmpty()
where b.CommissionNumber == null
select a;
gsharp was on the right track. I had some case sensitivity issues in my data which was ignored in the native SQL query but taken seriously by EF core.
var statusId = db.WorkOrder.Where(w => w.OrderType.Name == "ReadAudit" && w.WorkOrderMapping.MeterOldTag == meterTag && w.OrderStatusId != 80)
.OrderByDescending(w => w.CreationDatetime)
.Select(r => r.OrderStatusId)
.FirstOrDefault();
That produces this crazy sql:
SELECT TOP (1)
[Project1].[OrderStatusId] AS [OrderStatusId]
FROM ( SELECT
[Filter1].[OrderStatusId] AS [OrderStatusId],
[Filter1].[CreationDatetime] AS [CreationDatetime]
FROM (
SELECT [Extent1].[OrderStatusId] AS [OrderStatusId],
[Extent1].[CreationDatetime] AS [CreationDatetime],
[Extent3].[MeterOldTag] AS [MeterOldTag]
FROM [dbo].[WorkOrder] AS [Extent1]
INNER JOIN [dbo].[OrderType] AS [Extent2] ON [Extent1].[OrderTypeKey] = [Extent2].[OrderTypeKey]
LEFT OUTER JOIN [dbo].[WorkOrderMapping] AS [Extent3] ON [Extent1].[WorkOrderKey] = [Extent3].[WorkOrderMappingKey]
WHERE (80 <> [Extent1].[OrderStatusId])
AND (N'ReadAudit' = [Extent2].[Name])
) AS [Filter1]
WHERE ([Filter1].[MeterOldTag] = #p__linq__0) OR (([Filter1].[MeterOldTag] IS NULL) AND (#p__linq__0 IS NULL))
) AS [Project1]
ORDER BY [Project1].[CreationDatetime] DESC
And I'm told that its hitting the database pretty hard:
Table 'WorkOrder'. Scan count 30, logical reads 84403
Table 'WorkOrderMapping'. Scan count 9, logical reads 16516
The EF query doesn't seem that complicated. Is there a way to get the generated SQL to be more efficient?
The only thing "crazy" about that SQL Query is the predicate on MeterOldTag. It's written like that because EF, by default, writes queries to emulate C# comparison semantics for LINQ queries. If you want a simple equality comparison in the databse, set UseDatabaseNullSemantics for your DbContext.
It is just how Entity Framework works.
If you want more control, you can write the query yourself via Entity Framework Raw Queries, see here and below a (not complete) example how your query might look like.
DbRawSqlQuery<Int32> query = db.Database.SqlQuery<Int32>("SELECT OrderStatusId FROM ... ");
var statusId = query.FirstOrDefault();
Edit:
Also have a look at the actual query plan that gets executed for this query in order to find if whether the appropriate indexes are present on the tables involved.
Consider moving the constants RealAuditand 80 to variables, like below:
var orderType = "RealAudit";
var orderStatusId = 80;
var statusId = db.WorkOrder.Where(w => w.OrderType.Name == orderType
&& w.WorkOrderMapping.MeterOldTag == meterTag
&& w.OrderStatusId != orderStatusId
)
.OrderByDescending(w => w.CreationDatetime)
.Select(r => r.OrderStatusId)
.FirstOrDefault();
By doing so, these will appear as SQL parameters in the query, something like:
#p__linq__1 <> [Extent1].[OrderStatusId])
AND (#p__linq__2 = [Extent2].[Name])
This allows that a single query plan can be used for all variations of this query, whereas now you get a queryplan per separate value of the the MeterOldTag argument.
I need nhiberante query (not HQL) equivalent following SQL:
SELECT ur.*
FROM (SELECT MAX(requestTime) rt, macAddress ma
FROM UpdateRequests
GROUP BY macAddress) mur
JOIN dbo.UpdateRequests ur
ON mur.ma = ur.macAddress AND mur.rt = ur.requestTime
I had no luck with other similar examples on stackoverflow.
Having UpdateRequest mapping, it seems that is not possible with Query API, how about QueryOver?
Finally one Guru suggested me to change SQL query without changing execution plan:
SELECT ur.*
FROM [dbo].[UpdateRequests] AS ur
WHERE ur.[RequestTime] = (SELECT MAX(mur.[RequestTime])
FROM [dbo].[UpdateRequests] mur
WHERE mur.[MacAddress] = ur.[MacAddress])
So in code it transforms into:
session
.Query<UpdateRequest>()
.Where(ur => ur.RequestTime == session.Query<UpdateRequest>()
.Where(mur => mur.MacAddress == ur.MacAddress)
.Max(mur => mur.RequestTime))
.ToList();
And this is exactly what i need.
I have this query:
SELECT * FROM ScheduleGroups A
INNER JOIN ScheduleGroupResources B ON A.ScheduleGroupId=B.ScheduleGroupId
WHERE B.ScheduleGroupId IN (SELECT ScheduleGroupId
FROM ScheduleGroupResources
WHERE ScheduleResourceId=2)
I want represent with DbContext query.
Currently this:
return actualContext.ScheduleGroups
.Include(a => a.ScheduleGroupResources).ToList();
This brings me all results. I just want to filter as show in the 'where'.
Thank you so much for everything!
EDIT:
I found the solution:
return actualContext.ScheduleGroups
.Include(a => a.ScheduleGroupResources)
.Where(a => a.ScheduleGroupResources.Any(b => b.ScheduleResourceId == scheduleResourceId)).ToList();
Just FYI, the original query can be written:
SELECT *
FROM ScheduleGroups A
INNER JOIN ScheduleGroupResources B ON A.ScheduleGroupId=B.ScheduleGroupId
WHERE ScheduleResourceId=2
I'm trying to perform some simple SQL (using sql server 2005) using QueryOver in NHibernate. The query is to count how many times a serial number is repeatedly used in a list of items and then to select the serial numbers with only 1 use. Note that I don't want distinct serial numbers since I don't want to use serial numbers that have >1 use.
The SQL query to do this is:
SELECT SERNUM, expr1
FROM (SELECT SERNUM, COUNT(SERNUM) AS expr1
FROM ITEM
GROUP BY SERNUM) AS derivedtbl_1
WHERE (expr1 = 1)
What I have done so far using QueryOver is:
var query = session.QueryOver<Item>()
.Select(Projections.ProjectionList()
.Add(Projections.Count<Item>(x => x.Sernum))
.Add(Projections.GroupProperty("Sernum"))).List();
This code generates SQL (though it does not like putting the Count() column to a List). I'm not sure how to access the column of data generated by Count() to say only return where it is 1. The SQL that it does generates is:
SELECT count(this_.SERNUM) as y0_,
this_.SERNUM as y1_
FROM ITEM this_
GROUP BY this_.SERNUM
I'm not sure if I'm approaching this the right way but hopefully someone can point me to it.
var serials = session.QueryOver<Item>()
.Where(Restrictions.Eq(Projections.Count<Item>(i => i.Id), 1));
.Select(Projections.GroupProperty<Item>(i => i.Sernum))
.List();
should generate something like
SELECT SERNUM FROM ITEM GROUP BY SERNUM HAVING COUNT(*) = 1
to get the items, do something like
var subquery = QueryOver.Of<Item>()
.Where(Restrictions.Eq(Projections.Count<Item>(i => i.Id), 1));
.Select(Projections.GroupProperty<Item>(i => i.Sernum));
var items = QueryOver.Of<Item>()
.WithSubquery.Where(i => i.Sernum).In(subquery);
.List();