so we upgraded to newer Nhibernate and Fluent Nhibernate.
now I' getting this exception:
FailedNHibernate.Hql.Ast.ANTLR.QuerySyntaxException: Exception of type 'Antlr.Runtime.NoViableAltException' was thrown. near line 1, column 459
On this hql, which worked fine before the upgrade.
SELECT s.StudId, s.StudLname, s.StudFname, s.StudMi, s.Ssn, s.Sex, s.Dob, et.EnrtypeId, et.Active, et.EnrId, sss.StaffLname, sss.StaffFname, sss.StaffMi,vas.CurrentAge FROM CIS3G.Jcdc.EO.StudentEO s , CIS3G.Jcdc.EO.EnrollmentEO e , CIS3G.Jcdc.EO.EnrollmentTypeEO et , CIS3G.Jcdc.EO.VwStaffStudentStaffEO sss, CIS3G.Jcdc.EO.VwAgeStudentEO vas WHERE ( e.EnrId = et.EnrId ) AND ( s.StudId = vas.StudId ) AND ( s.StudId = e.StudId ) AND ( et.EnrtypeId *= sss.EnrtypeId ) AND ( Isnull ( sss.StudStaffRoleCd , 1044 ) = 1044 ) AND ( s.StudId = 4000 )
Clearly it does nto like the *= syntax, I tried rewritign is as ansi sql outer join and no joy.
Can anyone tell me what ineed to change the sql to so I can get the outer join to work correctly?
Thanks,
Eric-
We got it working with the ansi sql and calling nhibernate slightly differently.
Is there any way to force antlr to allow t-sql, or to stop antlr parsing all together?
All this sql worked fine before we got the version that added antlr parsing.
In MANY places we are passing T-Sql to NHibernate, becasue we're porting from another app with all the complex SQl already written, and We'd like to avoid having to rewrite & retest all the sql as ansi standard.
Shouldn't the antrl parsing only be applied to HQL only not direct SQL anyway? You reduce the
native compatibility for other DBs, like ours.
Thanks,
Eric=
You don't need to do explicit joins with NHibernate in order to get the related entities. Instead, map those relations as many-to-one.
For example, your Student class could have a References(x => x.EnrollmentType). With that, you'd only need to select the student (you don't even need HQL if you know the Id; you can use session.Get) and you could just navigate to the other properties.
I recommend you read the NHibernate documentation
Related
Using Entity Framework, I'm trying to join two tables like this.
...
join f in ent.FTypes on Int32.Parse(c.CourseID[0].ToString()) equals f.FTypeID
...
The first character of the string CourseID is a digit, and FTypeID is an int.
This doesn't seem to work though. The exception message I get is:
LINQ to Entities does not recognize the method 'Int32 Parse(System.String)' method, and this method cannot be translated into a store expression."} System.Exception {System.NotSupportedException}
What I want to replicate is the SQL string equivalent (which works fine):
join FType f on SUBSTRING(c.CourseID, 1, 1) = f.FTypeID
Does anyone have the solution to have to do this in LINQ to Entities?
This is a rather nasty join, but I did some testing in Entity Framework with similar data and arrived at something you can test on yours.
It uses string.Substring to grab the first character from your string operand, and then uses a combination of the EF-only method SqlFunctions.StringConvert (these methods are found in System.Data.Objects.SqlClient) with a cast to double1 and finally a string.Trim2 for your integer operand.
I have tested this and confirmed that all functions are supported at least in EF 4. Several other methods proposed or featured in the question do not work, because Entity Framework does not know how to tranlsate them to the appropriate SQL equivalent.
join f in ent.FTypes
on c.CourseID.Substring(0, 1)
equals SqlFunctions.StringConvert((double)f.FTypeID).Trim()
It produces a SQL join that looks like the following:
INNER JOIN [dbo].[FTypes] AS [Extent2]
ON ((SUBSTRING([Extent1].[CourseID], 0 + 1, 1)) = (LTRIM(RTRIM(STR( CAST( [Extent2].[FTypeID] AS float))))))
OR ((SUBSTRING([Extent1].[CourseID], 0 + 1, 1) IS NULL) AND (LTRIM(RTRIM(STR( CAST( [Extent2].[FTypeID] AS float)))) IS NULL))
So based on that, you might want to do some additional filtering as necessary.
Give it a shot and see if that helps solve the problem.
1 The cast to double is necessary because SqlFunctions.StringConvert does not have an overload for integer and there is no other single best match, so I force one.
2 The resultant string needs to be trimmed because the string conversion generates some excess padding.
I'm not sure this worked 8 years ago, but modern EF6 implementation allows you to add anonymous types, so you can add the conversion to your initial select statements, and then you can just compare that new properties directly in the join.
from c in ent.Courses
select new { typeId = c.CourseId.Substring(0, 1), c }
join f in ent.FTypes.Select(t => new { stringId =
t.FTypeId.ToString(), t }
on c.typeId equals f.stringId into ...
NOTE: The join-statement does not appear to support ToString() but the select-statements do.
You could also move the CouresId.Substring into the join statement, but that may run less efficiently there.
Results in SQL like this:
INNER JOIN [ent].[FTypes] AS [Extent2] ON
(LTRIM(RTRIM(SUBSTRING([Extent1].[nvarchar(max)], 3 + 1, 1)))) =
CAST([Extent2].[Id] AS nvarchar(max))
I'm trying to build a linq query for the following sql query :
SELECT *
FROM [QryFiles2]
WHERE (left(Demande,6) IN (select NoBVR from tblRequest))
Knowing that QryFiles2 is an entity and tblRequest too I've come to something like that :
from f in db.QryFiles2
where f.Demande.Substring(0,6) /* in (select NoBVR from tblRequest) */
select f
The thing is I don't know how I could express the in sql statement using linq. Do you know how I could do that without using raw sql queries ?
Nb : I've tried to use the .contains method but I couldn't work it out with the computed substring.
Finally I've done this using a join statement like that :
from f in db.QryFiles2
join r in db.tblRequest on f.Demande.Substring(0,6) equals r.NoBVR
Super clean super fast no need for subqueries should have thought about that before :-)
I have the following sql syntax that I used in my database query (SQL Server)
SELECT Nieuwsbrief.ID
, Nieuwsbrief.Titel
, Nieuwsbrief.Brief
, Nieuwsbrief.NieuwsbriefTypeCode
, (SELECT COUNT(*) AS Expr1
FROM NieuwsbriefCommentaar
WHERE (Nieuwsbrief.ID = NieuwsbriefCommentaar.NieuwsbriefID
AND NieuwsbriefCommentaar.Goedgekeurd = 1)) AS AantalCommentaren
FROM Nieuwsbrief
I'm changing now to sql-server-ce (compact edition) which won't allow me to have subqueries like this. Proposed solution : inner join. But as I only need a count of the subtable 'NieuwsbriefCommentaar', I have to use a 'group by' clause on my base table attributes to avoid doubles in the result set.
However the 'Nieuwbrief.Brief' attribute is of datatype 'text'. Group by clauses are not allowed on 'text' datatype in sql-server-ce. 'Text' datatype is deprecated, but sql-server-ce doesn't support 'nvarchar(max)' yet...
Any idea how to solve this? Thx for your help.
I think that the solution could be easier. I don't know exactly how is your metadata but I think that this code could fit your requirements by simply using LEFT JOIN.
SELECT Nieuwsbrief.ID
, Nieuwsbrief.Titel
, Nieuwsbrief.Brief
, Nieuwsbrief.NieuwsbriefTypeCode
, COUNT(NieuwsbriefCommentaar.NieuwsbriefID) AS AantalCommentaren
FROM Nieuwsbrief
LEFT JOIN NieuwsbriefCommentaar ON (Nieuwsbrief.ID = NieuwsbriefCommentaar.NieuwsbriefID)
WHERE NieuwsbriefCommentaar.Goedgekeurd = 1
Edited: 2ndOption
SELECT N.ID, N.Titel, N.Brief, N.NieuwsbriefTypeCode, G.AantalCommentaren FROM Nieuwsbrief as N LEFT JOIN (SELECT NieuwsbriefID, COUNT(*) AS AantalCommentaren FROM NieuwsbriefCommentaar GROUP BY NieuwsbriefID) AS G ON (N.ID = G.NieuwsbriefID)
Please, let me know if this code works in order to find out another workaround..
regards,
I use NHibernate 3.2.0 and I cannot make the LINQ provider generate a proper SQL query for this statement:
var result = (from translation in session.Query<TmTranslation>()
where translation.Id > 0
group translation by translation.Language into grp
select new { Lang = grp.Key.Code }).ToList();
The generated SQL is
select tmtranslat0_.id as id32_,
tmtranslat0_.status as status32_,
tmtranslat0_.text as text32_,
tmtranslat0_.last_revision as last4_32_,
tmtranslat0_.fk_id_translation_unit as fk5_32_,
tmtranslat0_.fk_id_translator as fk6_32_,
tmtranslat0_.fk_id_last_modifier as fk7_32_,
tmtranslat0_.fk_id_last_match_category as fk8_32_,
tmtranslat0_.fk_id_language as fk9_32_
from "TRANSLATION" tmtranslat0_
where tmtranslat0_.id > 0
which, of course leads to loading all the entities from the database and grouping the result set in memory (the result itself is correct).
I would like something like this
select tmtranslat0_.fk_id_language
from "TRANSLATION" tmtranslat0_
where tmtranslat0_.id > 0
group by tmtranslat0_.fk_id_language
to be generated instead.
Am I missing something?
Thank you very much.
The only thing I can suggest, is to use QueryOver API.
Today while inside a client's production system, I found a SQL Server query that contained an unfamiliar syntax. In the below example, what does the *= operator do? I could not find any mention of it on MSDN. The query does execute and return data. As far as anyone knows, this has been in the system since they were using SQL Server 2000, but they are now running 2005.
declare #nProduct int
declare #iPricingType int
declare #nMCC int
set #nProduct = 4
set #iPricingType = 2
set #nMCC = 230
--Build SQL for factor matrix
Select distinct
base.uiBase_Price_ID,
base.nNoteRate,
base.sDeliveryOpt,
IsNull(base.nPrice,0) as nPrice,
IsNull(base.nPrice,0) + Isnull(fact.nFactor,0) as nAdjPrice,
base.iProduct_ID,
fact.iPosition as fiPosition,
base.iPosition,
CONVERT(varchar(20), base.dtDate_Updated, 101) + ' ' + CONVERT(varchar(20), base.dtDate_Updated, 108) as 'dtDate_Updated',
fact.nFactor,
fact.nTreasFactor,
product.sProduct_txt ,
pfi.sPFI_Name,
mccprod.nServicing_Fee,
fact.nNoteRate as fNoteRate,
mcc.nLRA_Charge as nLRA
From
tbl_Base_Prices base, tbl_Factors fact, tbl_Product product, tbl_PFI pfi, tbl_MCC mcc, tbl_MCC_Product mccprod
Where
base.iProduct_ID = #nProduct
And base.iProduct_ID *= fact.iProduct_ID
And base.iPosition *= fact.iPosition
And base.nNoteRate *= fact.nNoteRate
And base.iPricing_Type = #iPricingType
And fact.iMCC_ID = #nMCC
And fact.iProduct_ID = #nProduct
And mcc.iMCC_ID = #nMCC
And mcc.iPFI_ID = pfi.iPFI_ID
And mccprod.iMCC_ID = #nMCC
And mccprod.iProduct_ID = #nProduct
And base.iProduct_ID = product.iProduct_ID
and fact.iPricing_Type= #iPricingType
Order By
base.nNoteRate, base.iPosition
Remove this code immediately and replace with a left join. This code does not always interpret correctly (Sometimes SQL Server decides it is a cross join) even in SQL Server 2000 and thus can give incorrect results! Also it is deprecated for the future (Using Outer Joins, SQL Server 2000 documentation archived from the original).
I'm going to add that in adjusting to left joins you should remove all of those other implicit joins as well. The implicit join syntax has been obsolete since 1992, there is no excuse for it still being in production code. And mixing implicit and explicit joins can give unexpected results.
It is a left outer join, =* is a right outer join.
E.g. the following are equal;
SELECT * FROM Table1 LEFT OUTER JOIN Table2 ON Table1.ID = Table2.FK_ID
SELECT * FROM Table1, Table2 WHERE Table1.ID *= Table2.FK_ID
The non-ANSI syntax for outer joins (*= and =*) is on the official list of deprecated features that will be removed in the next version of SQL.
The following SQL Server Database
Engine features will not be supported
in the next version of SQL Server. Do
not use these features in new
development work, and modify
applications that currently use these
features as soon as possible.
The replacement feature is the ANSI compliant syntax of JOIN.
It's a shorthand join syntax. Take a look at this thread which covers this topic.
Transact-SQL shorthand join syntax?
I believe those are "non-ANSI outer join operators". Your database compatibility level must be 80 or lower.
That's the older ANSI (ANSI-89) syntax left outer join operator. I'd recommend not using it - the ANSI syntax is more verbose and is much more readable.