All,
Apologies upfront. This may sound a bit like a rant. It probably is. I expected more from the latest (7.0.0-rc1-final) EF7. Any remarks or tips are welcome though!
Feel free to ask about the tables/fields structure, although I hope it will be clear from the naming alone.
My first EF7 query:
var enrichedProducts = this.productContext.Products
.AsNoTracking()
.Where(p => productNumbers.Contains(p.SAPProductNumber))
.Select(p => new
{
SAPProductNumber = p.SAPProductNumber,
DisplayName = p.DisplayName,
Brand = p.Brand,
Type = p.Type,
MultimediaValueEpochTime = p.ProductMultimedias
.Where(pm => pm.Visible == true
&& pm.ValueEpochTime != null)
.OrderByDescending(pm => pm.MainItem == true)
.ThenBy(pm => pm.SortOrder)
.Select(pm => pm.ValueEpochTime)
.FirstOrDefault()
})
.ToList();
This query yields an SqlException: Incorrect syntax near '='.
Cause was the OrderByDescending(pm => pm.MainItem == true. pm.MainItem is a nullable bit in de database. EF7 translates this into ORDER BY [pm].[MainItem] = 1 DESC which SQL Server doesn't like. EF6 translates this quite elaborately but at least it works:
CASE WHEN (1 = [Extent2].[MainItem]) THEN cast(1 as bit) WHEN ( NOT ((1 = [Extent2].[MainItem]) AND ([Extent2].[MainItem] IS NOT NULL))) THEN cast(0 as bit) END AS [C1]
and
ORDER BY [Project1].[C1] DESC
But there is something worse!
After removing == true from the OrderBy, the query works. To my surprise 9 queries were fired upon SQL Server! It was easy to explain that '9'. There were 8 productnumbers in the productNumbers collection. Oh my...
And worse: These 8 queries are identical; they don't even have one of the 8 product numbers in their where clauses! Also these 8 queries are not tied to the Products table at all. How strange this all is...
EF6 nicely translates this into 1 query with an OUTER APPLY and a SELECT TOP 1.
Attempt to fix:
Since the entities have been reused from an existing EF6 project, I tried to use fluent instead of annotations where possible. No improvement.
This bug has been fixed in the latest codebase, and the fix will be available in RC2
Related
I am trying to make a fairly simple query with an optional parameter in Interbase. I am using Firedac in Delphi 10 Seattle to call it.
SELECT STATUS_ID FROM TABLENAME
WHERE
STATUS_ID=:STATUSID OR :STATUSID IS NULL
fails with Dynamic SQL Error Code = -804 Unknown Datatype.
I can isolate just the :STATUSID IS NULL part and it fails.
Setting the parameter to null with just STATUS_ID=:STATUSID works just fine so it is the :STATUSID IS NULL part that is throwing the error.
Just remembered:
some_field = :some_param or :some_param is null
can be replaced by
some_field = coalesce(:some_param, some_field)
Yet another solution to try.
The answer I came up with is to use zero as my 'all' clause.
WHERE
STATUS_ID=:STATUSID OR :STATUSID=0;
that works. Null is better because it is correctly asking for No Entity, but it seems the implementation in Firedac does not support it.
I know it's very late answer. But I had a similar problem.
I guess your problem is because you test STATUS_ID=:STATUSID before :STATUSID IS NULL. Maybe it'll work with (:STATUSID IS NULL) OR (STATUS_ID=:STATUSID)
For numeric fields, I came up with a slightly different solution. For integer PKeys, often you never have a value of 0. Meaning you can either pass NULL or 0 and this will still return all rows. Lil more flexible.
SELECT * FROM WHATEVER
WHERE
((:ID IS NULL) OR (:ID <= 0) OR (:ID = ID))
For string fields, either NULL or '' (empty string) will return all rows.
SELECT * FROM WHATEVER
WHERE
((:STR IS NULL) OR (TRIM(:STR) = CAST('' AS VARCHAR(10))) OR (:STR = CLIENT_NI))
You can even chain them up like
SELECT * FROM WHATEVER
WHERE
((:ID IS NULL) OR (:ID <= 0) OR (:ID = ID)) AND
((:STR IS NULL) OR (TRIM(:STR) = CAST('' AS VARCHAR(10))) OR (:STR = CLIENT_NI))
Probably a little less efficient than having a separate query for all scenarios, but if you're using an OK RDB, it'll optimize the parameter WHERE clause as static, and you won't lose much performance. I love this solution.
You could use a cast
SELECT STATUS_ID
FROM TABLENAME
WHERE STATUS_ID=:STATUSID OR cast( :STATUSID as integer ) IS NULL
This works in firebird, so I guess that it should work in Interbase as well.
Who can help me write the right WHERE AND condition? Or if it's right, what is the difference?
1.
get_primary_status = Trustee.select(:id).where(:secret_key => seckey, :user_id => user_id, :primary_trustee => '1')
2.
get_primary_status = Trustee.select(:id).where(:secret_key => seckey).where(:user_id => user_id).where(:primary_trustee => '1')
3.
get_primary_status = Trustee.select(:id).where('secret_key = (?) AND user_id = (?) AND primary_trustee = (?)', seckey, user, '1')
There is no difference between these three snippets (if you don't use any JOINS that can make column names in third one ambiguous).
You can confirm it by calling them in Rails console and looking at generated SQL code.
I think the first one is most readable.
The first one.
For 2nd: as long, as you don't need dynamically add conditions, avoid chaining of 'where' functions.
For 3rd: whiting raw SQL may be good idea in some cases. But what if you will use SQLite for prototype, and PostgreSQL for going to production? Yep, they use SQL language as a standard, but Arel gives good abstraction over it.
I'd recommend using the Squeel gem, even though ActiveRecord works fine here, if you were to have multiple ORs and ANDs in the query, the command is not always very readable
This is how your request would look in Squeel
Trustee.select(:id).where { (secret_key == seckey) & (user_id == my_user_id) & (primary_trustee == '1') }
I've been using NEO4j database for a recent project at work.
Recently, we noticed that Cypher queries run faster than Gremlin so we decided to convert our queries. We are running the queries using the .Net graph client.
There is one Gremlin query that I'm having trouble converting to Cypher.
----Gremlin(this works and produces a result of Node(CustomerNode)....should be brackets around CustomerNode but the editor won't take it)
var results2 = graphClient.RootNode
.Out<ApplicationNode>("HasApplications")
.OutE("HasCompanies")
.InV<CompanyNode>(p => p.Id == companyId, StringComparison.Ordinal)
.Out<ConfigurationRootNode>("HasConfigurationRoot")
.Out<CustomerNode>("HasCustomers")
.As<CustomerNode>("Customer")
.OutE("HasConfigurationStatuses")
.InV<ConfigurationStatusNode>(p => p.Status == configStatus, StringComparison.Ordinal)
.OutE("HasOpportunities")
.InV<OpportunityNode>(p => p.ConfigurationId == configurationId, StringComparison.Ordinal)
.BackV<CustomerNode>("Customer");
var testResults2 = results2.ToList();
--------Cypher(see below...this doesn't work)
var results = graphClient.Cypher.Start("root", "node(0)")
.Match("(root)-[ha:HasApplications]->(Application)-[hs:HasCompanies]->Company-[hcr:HasConfigurationRoot]->(ConfigurationRoot)-[hc:HasCustomers]->(Customer)<-[hcs:HasConfigurationStatuses]-(ConfigurationStatus)<-[ho:HasOpportunities]-(Opportunity)")
.Where<CompanyNode>(Company => Company.Id == companyId)
.And().Where<ConfigurationStatusNode>(ConfigurationStatus => ConfigurationStatus.Status == configStatus)
.And().Where<OpportunityNode>(Opportunity => Opportunity.ConfigurationId == configurationId);
var x = results.Return<Node<CustomerNode>>("Customer").Results;
It appears that my "Match" property might not be set up correctly? I can't get any results to come from the Cypher query at all. This one is different from my other queries.....this is the only one that does a BackV in Gremlin.
Any help would be appreciated!
Thanks!
Patrick
I am designing a project on ASP.NET MVC 3.
I am using this query in my controller:
int batchSize = (int)db.ProductFormulation
.Where(r => r.ProductID == p)
.Min(r => r.Quantity);
Where p is input by the user.
When i run my project and user enter a value of p which does not exist in my table then an error occurrs.
How can i stop this error, e.g a message box should be created that states there does not exist record for value you entered, and my project should run continuously.
Please suggest me what should i do for it. Thanks in advance.
You're getting an error because Min is operating over a sequence with no elements.
You're basically looking for MinOrDefault(), which doesn't exist in the LINQ framework.
This answer has a good implementation of how to achieve it.
Alternatively, if you don't want to do the aggregate operation server-side, you could materialize the sequence first then perform the min:
int batchSize = 0;
var results = db.ProductFormulation.Where(r => r.ProductID == p).ToList();
if (results.Count > 0)
batchSize = results.Min(x => x.Quantity);
Obviously if you've got a lot of records, the above isn't really suitable, and you're better off with the aforementioned extension method.
I have an entry ID, which popularity has to be increased by one.
A simple solution looks like this:
Tag.find(id).increment!(:popularity)
However it doesn't seem to be very efficient, because I select the entire entry (*) from the database (even though I don't need it at all) and then do the second query to update it.
Is there a more efficient way to do this? I think, one update statement (without "select") should be enough, but how do I write this?
Tag.increment_counter :popularity, id
Not only does this skip the select, but it increments atomically.
Maybe something like :
Tag.update_all("popularity = popularity + 1", {:id => id})
Or
Tag.where(:id => id).update_all("popularity = popularity + 1")
?