Use SELECT DISTINCT ON with OrmLite - servicestack

I tried writing this code to use SELECT DISTINCT ON:
var q = Db.From<WatchedUrlScanResult>();
q.Join<WatchedUrlRecord>()
.Where<WatchedUrlRecord>(x => x.TargetDomainRecordId == request.TargetDomainId)
.And(x => x.ScanDate < fromEnd)
.OrderBy(x => x.WatchedUrlRecordId)
.OrderByDescending(x => x.ScanDate);
q.SelectExpression = q.SelectExpression.Replace("SELECT",
$"SELECT DISTINCT ON ({q.Column<WatchedUrlScanResult>(x => x.WatchedUrlRecordId)})");
var fromSnapshot = Db.Select<WatchedUrlScanResult>(q);
In debugger it shows the value has set for SelectExpression but the actual SQL that runs is just SELECT without DISTINCT ON.
I tried:
q.Select(q.SelectExpression.Replace("SELECT",
$"SELECT DISTINCT ON ({q.Column<WatchedUrlScanResult>(x => x.WatchedUrlRecordId)})"));
But I get:
Potential illegal fragment detected
Is it possible to do this without making full query a string? (using {q.Column<Table>(x => x.ColumnName)} in a full string query is fine but not very readable compared to fluent syntax)

If you want to perform a SELECT DISTINCT query in OrmLite using its SqlExpression<T> you would use the SelectDistinct() API, e.g:
var results = Db.Select(q.SelectDistinct());
But as you're trying to perform your own custom SQL distinct Query you'll need to use the Custom SQL APIs, e.g:
var sql = q.ToSelectStatement().Replace(what,with);
var results = Db.SqlList<WatchedUrlScanResult>(sql);

Related

ServiceStack Ormlite using Select with NoLock

I have the following simple OrmLite select statement:
SpiderUser lSpiderUser = db.Select<SpiderUser>(
su => su.WindowsUserName == vWindowsUserName).SingleOrDefault();
(The variable 'db' is of type IDbConnection).
I would like this query to run using NoLock. Ormlite from version 5.7 has 'SqlServerTableHint.NoLock', but I do not understand how to include this hint in the above query.
Please help...
OrmLite's SqlServerTableHint are only for table joins.
You can customize the generated SQL using a Typed SqlExpression, e.g:
var q = db.From<SpiderUser>()
.Where(su => su.WindowsUserName == vWindowsUserName)
.WithSqlFilter(sql => $"{sql} WITH (NOLOCK)");
var lSpiderUser = db.Single(q);

Count distinct doesn't work when using OrderBy & join

I have the following query trying to get count of a query:
var testQuery = Db
.From<Blog>()
.LeftJoin<BlogToBlogCategory>()
.Where(x => x.IsDeleted == false)
.OrderBy(x => x.ConvertedPrice);
var testCount = Db.Scalar<int>(testQuery.Select<Blog>(x => Sql.CountDistinct(x.Id)));
var results = Db.LoadSelect(testQuery.SelectDistinct());
It gives error:
42803: column "blog.converted_price" must appear in the GROUP BY clause or be used in an aggregate function
Issue seems to be the orderby statement. If I remove it then the error goes away. Why does this stop count distinct working?
I am having to clear orderby on all queries I do like this. Is it supposed to work this way?
Also I just realised count is wrong. Results is 501 unique records and testCount is 538.
What am I doing wrong?
Whenever in doubt with what an OrmLite query is generating, you can use the BeforeExecFilter to inspect the DB command before its executed or to just output the query to the Console you can use:
OrmLiteUtils.PrintSql();
You shouldn't be using OrderBy with aggregate scalar functions like COUNT which is meaningless and will fail in your case because it needs to included the GROUP BY clause for joined table queries.
Your specifically querying for COUNT(DISTINCT Id) if you wanted the row count for the query you can instead use:
var testCount = Db.RowCount(testQuery);
If you wanted to use COUNT(*) instead, you can use:
var testCount = Db.Count(testQuery);

How to get a SELECT DISTINCT on a SelectMulti query in ServiceStack OrmLite?

I'm trying to get a distinct result set of tuples, but the Distinct never gets added to query.
Example
List<Tuple<Alpha, Beta>> results;
var q = dbConn.From<Alpha>()
.Join<Alpha, Beta>((a, b) => a.Id == b.AlphaId)
...
... more joins and Wheres
...
.SelectDistinct();
results = dbConn.SelectMulti<Alpha, Beta>(q);
Adding the SelectDistinct, or not, make no difference to the outputted SQL and hence results.
How do I get SelectMulti to work with Distinct?
Thanks.
I've just added support for this in this commit where if .SelectDistinct() is used in the SqlExpression<T> then it will execute the SQL query using SELECT DISTINCT, e.g:
var results = dbConn.SelectMulti<Alpha, Beta>(q.SelectDistinct());
This change is available from v5.4.1 that's now available on MyGet.

ServiceStack.OrmLite Using Limit in SQL.In filter

I have a parent/child table setup - Items/ItemDetails. This part works:
var q = db.From<Item>(); //various where clauses based on request
items = db.Select<Item>(q);
q = q.Select(a => a.ITEM_NO);
itemDetails = db.Select<ItemDetail>(x => Sql.In(x.ITEM_NO, q));
Trying to add paging to improve the performance of this request for large data sets, I'm having trouble getting the .Limit(skip, rows) function to work in the SQL.In statement of the child table.
var q = db.From<Item>().Limit(skip, rows);
items = db.Select<Item>(q);
q = q.Select(a => a.ITEM_NO);
itemDetails = db.Select<ItemDetail>(x => Sql.In(x.ITEM_NO, q));
It works when limiting the results in the first select, but when used in the child data pull I get "Only one expression can be specified in the select list when the subquery is not introduced with EXISTS."
The SQL that comes out changes the where subquery to:
WHERE "ITEM_NO" IN (SELECT * FROM (SELECT "ITEM_NO", ROW_NUMBER() OVER
(ORDER BY "ITEM"."ITEM_NO") As RowNum
FROM "ITEM") AS RowConstrainedResult WHERE RowNum > 5 AND RowNum <= 15)
I understand the SQL error is because I am selecting more than one column in the IN clause. Is there a better way to write this to avoid the error?
Thanks
If you're using SQL Server 2012 or later you should use SqlServer2012Dialect.Provider, e.g:
container.Register<IDbConnectionFactory>(c =>
new OrmLiteConnectionFactory(connString, SqlServer2012Dialect.Provider));
Which lets OrmLite use the paging support added in SQL Server 2012 instead of resorting to use the windowing function hack required to implement paging for earlier versions of SQL Server.

how to write SQl Query to LINQ to SQL MVC entity framework

This is my sql Query:
SELECT ParkingPlaceName, NoOfParkingPlaces, COUNT(Place.ParkingAreaID) AS NoOfCarsParked, NoOfParkingPlaces-COUNT(Place.ParkingAreaID)
FROM ParkingArea
LEFT JOIN Place ON ParkingArea.ParkingAreaID = Place.ParkingAreaID
LEFT JOIN Car ON Car.CarID = Place.CarID
GROUP BY ParkingPlaceName, NoOfParkingPlaces, Place.ParkingAreaID
how to write in LINQ to SQL Query
I think you can use DefaultIfEmpty method to perform left outer join between 3 tables with let statements to deal with group results, hence the LINQ version from your SQL query should be similar (or same) like below:
var query = from pa in ParkingArea
from pl in Place.Where(x => pa.ParkingAreaID == x.ParkingAreaID).DefaultIfEmpty()
from ca in Car.Where(x => x.CarID == pl.CarID).DefaultIfEmpty()
group new { pa, pl.ParkingAreaID, ca.CarID } by new { pa.ParkingPlaceName, pa.NoOfParkingPlaces, pl.ParkingAreaID } into grp
let ParkingPlaceName = grp.Select(x => x.ParkingPlaceName)
let NoOfParkingPlaces = grp.Select(x => x.NoOfParkingPlaces)
let NoOfCarsParked = grp.Select(x => x.ParkingAreaID).Count()
select new
{
ParkingPlaceName,
NoOfParkingPlaces,
NoOfCarsParked,
AvailableParkingPlaces = NoOfParkingPlaces - NoOfCarsParked // new alias for holding substraction
};
I decided not to use join ... in ... on ... equals ... directly after first and third from clauses, since it may lead into confusion about which table instance should be handled when creating group elements after performing join.

Resources