Rewrite SQL to FluentNHibernate - c#

I'am using NHibernate 3.3.1.4000 and FluentNHibernate 1.3.0.733 with SQL Server 2012 and I need to rewrite query to IQueryOver to do this calculation on server side without stored procedure calling.
WITH CTE_Links AS
(
SELECT Article1, MIN(Article2) Article2
FROM dbo.LinkBetweenArticles
GROUP BY Article1
HAVING Article1 > MIN(Article2)
)
SELECT COUNT(*)
FROM dbo.ArticlePlacedBySeller a
LEFT JOIN CTE_Links l ON a.Id = l.Article1
WHERE l.Article2 IS NULL
AND ConfirmedPosition_Id = 1000
Script description can be read here if you need

as far as I know it is not possible to use natively IQueryOver API to achieve this. IMHO you could use this CTE query as a custom query. You can find the solution by following the link http://refactoringaspnet.blogspot.com/2011/04/using-common-table-expressions-to.html.

Related

Select N Row with DB2 in Visual Studio

I want to get the first 95 data from DB2 in Visual Studio. I'm using table adapter and I have this query,
SELECT * FROM ASEINDTA.TRX_BWS WHERE (DKLDATE
= '2019-10-31') Fetch First 95 Rows Only
or this
SELECT * FROM ASEINDTA.TRX_BWS WHERE (DKLDATE
= '2019-10-31') ORDER BY Col[ 1 ]...Col[ n ]
Fetch First 95 Rows Only
But when I click Query Builder, this error appears.
But when I tried it in DBVisualizer, it works. How do I get that data? A help would be appreciate. Thanks
One approach would work on DB2 and most other databases would be to use ROW_NUMBER with a subquery:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY some_col) rn
FROM ASEINDTA.TRX_BWS
WHERE DKLDATE = '2019-10-31'
)
SELECT *
FROM cte
WHERE rn <= 95;
Or, the inlined version that does not use CTE:
SELECT *
FROM
(
SELECT *, ROW_NUMBER() OVER (ORDER BY some_col) rn
FROM ASEINDTA.TRX_BWS
WHERE DKLDATE = '2019-10-31'
) t
WHERE rn <= 95;
Remove the parentheses of the where clause:
SELECT * FROM ASEINDTA.TRX_BWS WHERE DKLDATE = '2019-10-31' Fetch First 95 Rows Only
It seems the issue is not on db2-server it-self, but from the tool you are using to execute the queries. You said, if you run the same in 'DBVisualizer' it works properly.
Looking at db2 documentation for this error: sqlcode -104 sqlstate 42601
it seems this error is returned by SYSPROC.ADMIN_CMD stored procedure.
This procedure is designed to run db2 administration commands in the target database, remotely. It was not designed to run queries... so, the parser for this proc is just a sub-set of db2 parser, just for specific admin commands.
So it's complaining about the FETCH token from your query.
It seems that the tool you are using 'I'm using table adapter' (no idea on what is it) , is calling this SYSPROC.ADMIN_CMD to execute the queries, but it should be using a regular CLI interface instead.
I don't know what exactly tool you are using. but try to see if it has some sort of settings, so you can change that behavior.
Here is the list of admin cmds that ADMIN_CMD proc can execute:
https://www.ibm.com/support/knowledgecenter/en/SSEPGG_11.1.0/com.ibm.db2.luw.sql.rtn.doc/doc/r0012547.html .
as you can see, no SELECT statement there.
If I try to execute a simple SELECT , using this SP, I get the same error, from db2 CLP window, directly at the server.
db2 "call SYSPROC.ADMIN_CMD('SELECT * FROM DEPARTMENT')"
SQL0104N An unexpected token "SELECT" was found following
"BEGIN-OF-STATEMENT". Expected tokens may include: "ADD". SQLSTATE=42601
Regards
Samuel Pizarro
Ok
I had a closer look at the image error you posted...
the query is wrong. It's not exactly the same queries you posted in your question.
Have a closer look at the image error, and you will see that the Fetch and Rows words are double-quoted.
SELECT ... FROM WHERE (...) "Fetch" First 95 "Rows" Only
Remove the double-quotes from them.
If you are not writing them like that, so, it looks your tool is "changing" it, before submitting to the db2 engine. Again, not a db2 server issue.
Regards

Whats the best solution to Entity Framework cores lack of moderate LINQ query support?

So basically I have a table containing a set of data. This data is then joined onto an organisation table to which multiple users can be apart of. Im then trying to get all files in the table where the user executing the query, has permission to access the organisation. To do this I'm using a where clause that checks the users permissions from the application, to the files that have them organisations linked. Im then selecting the top 100 results and counting the records returned. (I want to see if the user has access to 100+ files over all the organisations).
The problem is when I use the following LINQ query:
(from f in File
join o in Organisation on f.OrganisationId equals o.Id
where permissions.Contains(o.Id.ToString())
select f).Take(100).Count();
The take and the count aren't executed on the SQL server and are run in memory when I try a contains on a list which should convert to an IN (VALUES) query on SQL. I have 70,000+ File records and this is very slow and times out on a web server. This is expected as Entity Framework core is in early stages and does not support moderate or advanced LINQ queries yet.
My question is, is there a better alternative to raw SQL queries while still being able to filter by an array of items and still using Entity Framework core v1.1? Thanks.
Edit: I tried updating to the latest version, this still did not solve my issue as I still got the following output.
The LINQ expression '{permissions => Contains([o].Id.ToString())}' could not be translated and will be evaluated locally.
The LINQ expression 'Contains([o].Id.ToString())' could not be translated and will be evaluated locally.
The LINQ expression 'Take(__p_1)' could not be translated and will be evaluated locally.
The LINQ expression 'Count()' could not be translated and will be evaluated locally.
The warnings are misleading - the problem is the ToString() call which causes client evaluation of the query.
The following should produce the intended SQL query:
var idList = permissions.Select(int.Parse);
var result = (
from f in File
join o in Organisation on f.OrganisationId equals o.Id
where idList.Contains(o.Id)
select f).Take(100).Count();
which in my environment (EF Core v1.1.1) produces the following SQL with no warnings (as expected):
SELECT COUNT(*)
FROM (
SELECT TOP(#__p_1) [f].[Id], [f].[Name], [f].[OrganisationId]
FROM [Files] AS [f]
INNER JOIN [Organisations] AS [o] ON [f].[OrganisationId] = [o].[Id]
WHERE [o].[Id] IN (1, 3, 4)
) AS [t]

C# Linq to MySQL query with join makes bad SQL?

I'm using C# to write LINQ in to a MySQL database. I think the SQL generated might be wrong for a simple table join that I'm doing.
My nuget packages are Mysql.Data v6.9.9, Mysql.data.entities v6.8.3, and MySql.data.entity v6.9.9
The LINQ is this:
query = from peopleResult in query
join t in technologyQuery on peopleResult.Company_Id equals t.Company_Id
select peopleResult;
The SQL generated looks like this:
SELECT ...
FROM `people` AS `Extent1`
INNER JOIN `technologies` AS `Extent2` ON (`Extent1`.`Company_Id` = `Extent2`.`Company_Id`) OR ((`Extent1`.`Company_Id` IS NULL) AND (`Extent2`.`Company_Id` IS NULL))
WHERE ...
Is this part of the join right?
(`Extent1`.`Company_Id` IS NULL) AND (`Extent2`.`Company_Id` IS NULL)
The query is incredibly long running when that is included. I pulled that out of the SQL with a regex, and it runs much faster and seems to give the correct results.
Is my LINQ incorrect or missing something? Does the MySQL linq-to-sql likely have a bug?
Thank you for your time thinking about this.
It's not a MySQL connector bug, but EF feature which tries to emulate the C# equality rules for nullable types.
First, make sure to set DbContext.Configuration.UseDatabaseNullSemantics to true, for instance inside your DbContext derived class constructor:
Configuration.UseDatabaseNullSemantics = true;
By idea this should solve the issue. However they implemented it for comparison operators and forgot the joins. So you have to use the alternative join syntax with where clause:
query =
from peopleResult in query
from t in technologyQuery
where peopleResult.Company_Id == t.Company_Id
select peopleResult;
which will be translated to the desired SQL JOIN without IS NULL part.

Entity Framework v4.1 LIKE

How do I have to build my query to result in an output SQL query like:
SELECT
[viewRegisters].[Id] AS [IdRegister]
WHERE Name LIKE '%a%bc'
OR
SELECT
[viewRegisters].[Id] AS [IdRegister]
WHERE Name LIKE 'a%b%c'
OR
SELECT
[viewRegisters].[Id] AS [IdRegister]
WHERE Name LIKE 'a%b%c%'
I'm using .Net Framework 4.0, Entity Framework v4.1 and C#.
EF v4.1 converts this type of linq queries from:
((IQueryable<T>)Data).Where(z => z.Field.Contains("a%b%c%"));
Into:
SELECT
[viewRegisters].[Id] AS [Id]
WHERE Name LIKE N'a~%b~%c~%' ESCAPE N'~'
That's not what I want. I want to be able to use the 'percent' symbol as I do directly in DB.
You must use ESQL if you want full wildcard support. Linq-to-entities is not able to do that and EFv4.1 code first (without EDMX) doesn't have support for model defined functions so the solution provided by #Johann Blais cannot be used.
I guess the code to run ESQL query can look like:
string command = "SELECT VALUE e FROM ContextName.DbSetName AS e WHERE e.Field LIKE 'a%b%c%'"
ObjectContext ctx = ((IObjectContextAdapter)dbContext).ObjectContext;
ObjectQuery<EntityType> query = new ObjectQuery<EntityType>(command, ctx);
ObjectResult<EtntiyType> result = query.Execute(MergeOption.AppendOnly);
If you are using SQL Server, use the PATINDEX function to do a pattern search. You can access this function through EF using the SqlFunctions class.
For example, the following EF query
context.ViewRegisters.Where(z => SqlFunctions.PatIndex("a%b%c%", z.Name) > 0);
will translate into
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name]
FROM [dbo].[ViewRegisters] AS [Extent1]
WHERE (CAST(PATINDEX(N'a%b%c%', [Extent1].[Name]) AS int)) > 0
var query = from viewRegister in context.ViewRegisters
where viewRegister.Name.Contains("yourname")
select viewRegister;

NHibernate SQLquery for paging to HQL / ICriteria

I'm making a query that sorts and returns X rows based on row_number()
I'm using NHibernate with MSSQL and im trying to get paging working using CreateSQLQuery and i have this query:
select s.*
from(
select distinct release.[stop], survey.SurveyId, survey.Created, survey.CopyOfId, survey.DesignTemplateId, survey.UserId, survey.Template, [Row] = Row_Number() over (order by survey.[SurveyId])
from Survey as survey
inner join Release as release on release.SurveyId = survey.SurveyId
group by survey.SurveyId
, survey.Created
, survey.CopyOfId
, survey.DesignTemplateId
, survey.UserId
, survey.Template
, release.[stop]
) as s
where s.[Row] >= 0 and s.[Row] <= 20
order by s.[stop]
does anyone know how to get this working using HQL or ICriteria (even better) instead of plain SQL? The reason for this is that I want a query that is compatible with both SQLite and MS SQL Server 2005, so that i can use .SetMaxResult() og .SetFirstResult()
Thanks in advance!
Try to avoid using plain SQL in nHibernate.
On Criteria object, use SetFirstResult() and SetMaxResult() for your paging.
Pages of 10 records ? First page is criteria.SetFirstResult(0).SetMaxResult(10) and third page is criteria.SetFirstResult(20).SetMaxResult(10)
Always use the correct dialect. For exemple SQL Server 2008 has more paging features than SQL Server 2005.
Here's an excellent article to do what you want
http://www.tobinharris.com/past/2008/10/20/almost-iqueryable-with-nhibernate-hql/

Categories