I'm using NHibernate to query a table that has tuples in the format: (String, Int?), where the integers can be null. So, I want to group my results by number and then sort alphabetically. I can easily do this after I get the query results, but I would like to get NHibernate to formulate a query that does it. Here's an example of the results I would like:
alpha, 1
delta, 4
golf, 3
hotel, 2
lima, 5
charlie, 0
theta, 0
beta, null
echo, null
The three groupings I'm looking for are: (int > 0), (int == 0), and (int = null). Here's the query I'm using:
var devices = session.QueryOver<Table>()
.OrderBy(item => item.Number).Desc
.OrderBy(item => item.Name).Asc
.List();
Currently, I'm sorting them after the query is done, as such:
List<Table> sortedDevices = devices.OrderBy(item => item.Name).Where(item => item.Number > 0).ToList();
sortedDevices = sortedDevices.Concat(devices.OrderBy(item => item.Name).Where(item => item.Number == 0).ToList()).ToList();
sortedDevices = sortedDevices.Concat(devices.OrderBy(item => item.Name).Where(item => item.Number == null).ToList()).ToList();
Is it possible to get NHibernate to group queries like this?
something along the lines:
session.QueryOver<User>()
.Select(Projections.Alias(Projections
.Conditional(Expression.Gt("Number", 0),
Projections.Constant(1),
Projections.Conditional(Expression.Eq("Number", 0),
Projections.Constant(0),
Projections.Constant(-1))),
"group"))
.OrderBy(Projections.Property("group")).Desc
.ThenBy(table => table.Name).Asc
.List();
Related
I have a simple view:
CREATE VIEW [dbo].[ApplicationSummary]
AS
SELECT
CONVERT(VARCHAR(50), NEWID()) AS ID,
ISNULL(AVG(ApplicationTime), 0) AS 'AvgApplicationTime',
ISNULL(AVG(ResponseTime), 0) AS 'AvgResponseTime',
ISNULL(CAST(1.0 * COUNT(CASE WHEN [IsAccepted] = 1 THEN 1 END) / COUNT(*) AS float), 0) AS 'PctAccepted'
FROM
[Application]
WHERE
(IsValid = 1)
AND (CreatedOn < CAST(GETDATE() AS date)
AND CreatedOn >= CAST(GETDATE()-30 AS date))
The idea is that it outputs 3 variables. The first 2 are simple 'averages', whereas the last one, 'PctAccepted' outputs a ratio.
I'm testing a table containing 4 rows — 3 of them are set to IsAccepted = true, so the result is 0.75 (3/4), which converts to 75%.
I'm trying to remove the need for a view and replicate it using Linq over my Entity Framework class.
Here is the important stuff from the query:
var startDate = DateTime.Today;
var endDate = DateTime.Today.AddDays(-30);
var q = r.Find(x => x.IsValid &&
x.CreatedOn < startDate && x.CreatedOn >= endDate)
.GroupBy(x => x)
.Select(g => new
{
AvgApplicationTime = (int)g.Average(i => i.ApplicationTime),
AvgResponseTime = (int)g.Average(i => i.ResponseTime),
ApprovalRatio = g.Count(i => i.IsAccepted == true) / (double)g.Count()
}).First();
return new ApplicationStats(q.AvgApplicationTime, q.AvgResponseTime, q.ApprovalRatio);
So far, I have the two averages outputting correctly, but the ratio is returning 1 (or 100%).
Initially, I thought it may be a rounding issue but I've outputted the result of this line: g.Count(i => i.IsAccepted == true) and it incorrectly returns 1.
I may have grouping in the wrong place, but am currently struggling to make it work.
Any help appreciated.
Found the answer.
Changed .GroupBy(x => x) to .GroupBy(x => 1).
Also g.Count(i => i.IsAccepted == true) can be simplified to g.Count(i => i.IsAccepted)
I have a field, that's indexed as
Index(x => x.Status, FieldIndexing.Default);
in a collection with a total of 2566 records.
Now when I query like this:
var query = _ravenSession
.Query<MyIndex.ReduceResult, MyIndex>()
.Statistics(out stats);
query = query.Where(x => x.Status != "inactive" && x.Status != "invalid" && x.Status != "sold");
I get a total result count of 2512.
But if I query like this:
query = query.Where(x => !x.Status.In("inactive", "invalid", "sold"));
I get a total result count of 2520. How can this count be different?
I can see, that the first query translates to
{((((-Status:inactive AND Status:*) AND -Status:invalid))
AND -Status:sold)}
and the second one:
{(: AND -#in`<Status>:(inactive , invalid , sold) )}
There is a difference with how this query is processed.
In the first case, you are allowing only results that have a Status, in the second, you are saying everything but those particular values. So if you have values that don't have the status property, that might explain it.
I am current having an issue with getting records from database using nhibernate.
My 'publicstring' column is having two values, one with lower case and the other with upper case.
I am trying to fetch one of them depending on what is typed.
Below is my query
private string _publicId;
var _mediaFileShare = this.Session.QueryOver<MediaFileShare>()
.Where(q => q.Publicsting == _publicstring)
.SingleOrDefault();
Answers would be appreciated..
A naive solution is:
var _mediaFileShare = this.Session.QueryOver<MediaFileShare>()
.Where(q => q.PublicString == _publicString)
.List()
.SingleOrDefault(r => string.Compare(r.PublicString, _publicString, StringComparison.Ordinal) == 0);
If you're confident that the query will always return two rows then performance should be very acceptable. Alternatively you could use a dynamic order by, something like:
var query = this.Session.QueryOver<MediaFileShare>()
.Where(q => q.PublicString == _publicString);
query = _publicString == _publicString.ToLower()
? query.OrderBy(q => q.PublicString)
: query.OrderByDescending(q => q.PublicString);
var _mediaFileShare = query.Take(1).SingleOrDefault();
The results using an order by will be dependent on your database and its collation setting.
I have some QueryOver with several joins and the result I get is OK in terms of the returned objects. This is the code>
var l = session.QueryOver<Discount>(() => discount)
.JoinQueryOver<ConPrdGrp>(r => r.ConPrdGrps)
.JoinQueryOver<PrdGroupTree>(k => k.PrdGroupTree)
.JoinQueryOver<Product>(g => g.Products)
.Where(p => p.ProductID == Product.ProductID)
.And(() => discount.FomDato <= DateTime.Now && discount.TomDato >= DateTime.Now).List();
But if I look at the SQL statement, I can see that the generated query selects ALL columns from all joined tables although the result only returns a list of Discount objects. I can get some properties of Discount using projections and the query will be much smaller. But how I can instruct NHibernate to get just columns from the Discount table and not from all joined tables?
As far as I know there is NOT a simple way like: .Select(Projections.Properties<Discount>()). Check this a bit outdated question (NHIbernate: Shortcut for projecting all properties?)
What you can do is explicitly name the columns you would like to see:
... // the QueryOver you have
.SelectList (l => l
.Select(() => discount.ID).WithAlias(() => discount.ID)
.Select(() => discount.Code).WithAlias(() => discount.Code)
...
)
.TransformUsing(Transformers.AliasToBean<Discount>())
...
The second approach could be to create the Subquery, and then just QueryOver<Discount>() 16.8. Subqueries
QueryOver<Discount> subQuery =
QueryOver.Of<Discount>(() => innerDiscount)
.JoinQueryOver<ConPrdGrp>(r => r.ConPrdGrps)
.JoinQueryOver<PrdGroupTree>(k => k.PrdGroupTree)
.JoinQueryOver<Product>(g => g.Products)
.Where(p => p.ProductID == Product.ProductID)
.And(() => innerDiscount.FomDato <= DateTime.Now
&& innerDiscount.TomDato >= DateTime.Now).List()
.Select( Projections.Property(() => innerDiscount.ID))
;
var query = session.QueryOver<Discount>(() => discount)
.Where(Subqueries.PropertyIn("ID", subQuery.DetachedCriteria))
;
I need to select count of row with a condition:
Query to collect the full count:
var searchs = searchQuery.SelectList
(list => list
.SelectGroup(order => order.Id).WithAlias(() => groupResult.GlobalId)
.SelectCount(() => _transaction.ReturnStatus).WithAlias(() => groupResult.DeclineCount)
)
I need count of transactions that equals 201. Something like this:
.SelectCount(() => _transaction.ReturnStatus == 201).WithAlias(() => groupResult.DeclineCount) //runtime error
Thanks in advance!
PS:
Original SQL Query:
SELECT TOP 100
globalOrd.ID AS GlobalId ,
SUM(CASE WHEN transact.returnStatus = 201 THEN 1
ELSE 0
END) AS DeclineCount
FROM Orders.Global globalOrd
INNER JOIN Orders.TransactionDetail transactDet ON globalOrd.ID = transactDet.DetailID
INNER JOIN Orders.[Transaction] transact ON transactDet.TransactionID = transact.ID
GROUP BY globalOrd.ID
If you don't need the total count in the same query you can simply add in the restriction before the SelectList:
var searchs = searchQuery.SelectList
(list => list
.Where(() => _transaction.ReturnStatus == 201)
.SelectGroup(order => order.Id).WithAlias(() => groupResult.GlobalId)
.SelectCount(() => _transaction.ReturnStatus).WithAlias(() => groupResult.DeclineCount)
)
If however, you want both the total and the restricted count, you would have to use a SqlProjection for the latter doing something like:
SUM(CASE {alias}.ReturnStatus WHEN 201 THEN 1 ELSE 0 END)