Two LINQ data contexts issue - linq-to-sql

I'm getting this error when using LINQ2SQL:
The query contains references to items defined on a different data context.
Here's the code:
var instances = (from i in context.List
join j in context.CatsList on i.ListID equals j.ListID
join c in context.Cats on j.CatID equals c.CatID
where c.SID == Current.SID
orderby i.Title
select i).Distinct();
The problem, as far as I can ascertain, is that the Current object is actually a LINQ2SQL object returned from a property executing a different LINQ statement.
So, therefore, LINQ2SQL doesn't like executing a query on the database where the query has to be built from one LINQ statement including another statement's result.
My problem with that is that (I'll try to summarise the issue here) the Current object is retrieved using the same context as the query above and ultimately the Current.SID should simply resolve to an int, so what is the compiler's problem with executing it?
In short, why is it not possible to execute a LINQ query using a previous query's returned object as an argument?

This is a solution to the issue, rather than a direct answer of your final question, but you can probably get by with:
var sid = Current.SID;
var instances = (from i in context.List
join j in context.CatsList on i.ListID equals j.ListID
join c in context.Cats on j.CatID equals c.CatID
where c.SID == sid
orderby i.Title
select i).Distinct();

Related

How to make sqlachemy see implicit lateral joins as in json_each or jsonb_each?

I'm trying to figure out the proper way of using json_each. I've seen some tricks like using column or text. So far I've found a quite clean way using table_valued, that works except for the cross join warning.
term = 'connection'
about_exp = func.json_each(EventHistory.event, '$.about').table_valued('value')
events = s.query(EventHistory).filter(about_exp.c.value == term)
EventHistory contains one json field that looks like this: {"about": ["antenna", "connection", "modem", "network"]}
The resulting query works as expected but I'm getting the following warning:
SAWarning: SELECT statement has a cartesian product between FROM element(s) "event_history" and FROM element "anon_1". Apply join condition(s) between each element to resolve.
For any one that would like to experiment here is a working example in from of unit tests: https://gist.github.com/PiotrCzapla/579f76bdf95a485eaaafed1492d9a70e
So far the only way I found not to emit the warning is to add join(about_exp, true())
from sqlalchemy import true
about_exp = func.json_each(EventHistory.event, '$.about').table_valued('value')
events = s.query(EventHistory).join(about_exp, true()).filter(
about_exp.c.value == about_val
)
But it needs additional import of true and additional join statement, if anyone has a better solution please let me know.
As of sqlalchemy version 1.4.33, you can use the joins_implicitly=True option for table_valued.
term = 'connection'
about_exp = func.json_each(EventHistory.event, '$.about').table_valued('value', joins_implicitly=True)
events = s.query(EventHistory).filter(about_exp.c.value == term)
joins_implicitly – when True, the table valued function may be used in the FROM clause without any explicit JOIN to other tables in the SQL query, and no “cartesian product” warning will be generated. May be useful for SQL functions such as func.json_each().
source

CI active record style sql queries

I am new in Code Igniter and like its active record feature now is there any useful steps or tips or any guidness how do i convert my pervoiusly written simple SQL Queries in CI style like this is my perviouly written simple query
SELECT *
FROM hs_albums
WHERE id NOT IN (SELECT album_id
FROM hs_delete_albums
WHERE user_id = 72
AND del_type = 1)
AND ( created = 72
OR club_id IN (SELECT cbs.id
FROM hs_clubs cbs
INNER JOIN hs_club_permissions cbp
ON cbs.id = cbp.club_id
WHERE cbp.user_id = 72
AND cbp.status = 2)
OR group_id IN (SELECT gps.id
FROM hs_groups gps
INNER JOIN hs_group_permissions grp
ON gps.id = grp.group_id
WHERE grp.user_id = 72
AND grp.status = 2)
OR comp_id IN (SELECT cmp.id
FROM hs_companies cmp
INNER JOIN hs_comp_permissions comp
ON cmp.id = comp.comp_id
WHERE comp.user_id = 72
AND comp.status = 2) )
The short answer is: You don't.
CodeIgniter's Active Record implementation is basically a layer on top of SQL that makes writing queries easier by:
Automatically escaping values
Automatically generating the appropriate query syntax for the database, so that the application can be more easily ported between databases (for instance, if you didn't use Active Record to write a query, and then wanted to move from MySQL to PostgreSQL, then you might well need to rewrite the query to make it work with PostgreSQL)
Providing a syntax for queries in PHP directly, thus avoiding the context switching between PHP and SQL.
However, it can't do everything SQL can do, and while I would always try to use ActiveRecord where possible, there comes a point where you're better off forgetting about using it and just using $this->db->query() to write your query directly. In this case, as mamdouh alramadan has said, CodeIgniter doesn't support subqueries so you can't replicate this query using ActiveRecord anyway.
The thing to remember is that ActiveRecord is a tool, not a requirement. If you're using CodeIgniter and aren't using an ORM instead, you should use it for the reasons mentioned above. However, once it starts getting in the way, you should consider whether it would be better practice to write your query manually instead.

Linq2sql Optimizing Left join to get items that exist in only in 1 container

I want to get items from one container that don't exist in another. One container is IEnumerable, and another is an entity in DB. For example
IEnumberable<int> ids = new List<int>();
ids.Add(1);
ids.Add(2);
ids.Add(3);
using (MyObjectContext ctx = new MyObjectContext())
{
var filtered_ids = ids.Except(from u in ctx.Users select u.id);
}
This approach works, but I realized that underlying sql is something like SELECT id FROM [Users]. That is not what I want. Changing it to
var filtered_ids = ids.Except(from u in ctx.Users
where ids.Contains(u.id)
select u.id);
improves underlying query and adds WHERE [id] IN (...) which seems a way better.
I have 2 questions:
Is it possible to improve performance any further for this query?
As far as I remember there is a limit on how many parameters can be in IN . Will my current query work if I exceed the limit (which is not very likely to happen, but it's better to be prepare) ?
That query should be fine, provided proper indexes/primary keys are in place.
The upper limit on sql parameters accepted by sql server is around 2100. If you exceed the limit, you will be met with a sql exception instead of results.

Union (or Concat, etc..) with Constant values and projection

I've discovered a very nasty gotcha with Linq-to-sql, and i'm not sure what the best solution is.
If you take a simple L2S Union statement, and include L2S code in one side, and constants in the other, then the constants do not get included in the SQL Union and are only projected into the output after the SQL, resulting in SQL errors about the number of columns not mathching for the union.
As an example:
(from d in dc.mytable where foo == "bar" select new {First = d.Foo, Second = d.Roo})
.Union(from e in dc.mytable where foo == "roo" select new {First= "", Second = e.Roo})
This will generate an error "All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.
This is particularly insidious (and maddening) because there are obviously the same number of expressions in the list, but when you look at the SQL, you will notice that it does not generate a column for "First" in the second half of the Union. This is because "First" is inserted into the projection AFTER the query.
Ok, the easy solution is to just convert each part into Enumerables or Lists or something and then do the union in memory rather than SQL, and that's fine if you're dealing with a small amount of data. However, if you're working with a large set of data, which you then plan to further filter (in sql) before returning it this is not ideal.
I guess what i'm looking for is a way to force L2S to include the column in the SQL. Is that possible?
UPDATE:
While not an exact duplicate, this error is similar to This Question and has similar solutions. So i'm closing, but not deleting this question because it may help someone else come to posible solutions from a different way.
Unfortunately, L2S is too smart of it's own good sometimes.
I've decided that the only real solution is to use a stored proc. Hope this helps.
This is a bug in the Linq2SQL provider.
In LinqPad you can clearly see the bug.
(from d in dc.mytable where foo == "bar" select new {First = d.Foo, Second = d.Roo})
.Union(from e in dc.mytable where foo == "roo" select new {First= "", Second = e.Roo})
Will server side produce something like this:
SELECT [t2].[Foo], [t2].[Roo]
FROM (
SELECT [t0].[Foo], #p0 AS [value]
FROM [dc].[Mytable] AS [t0]
UNION ALL
SELECT [t1].[Foo], [t1].[Roo]
FROM [dc].[Mytable] AS [t1]
) AS [t2]
This will be a problem because the union will name the second column "value" instead of "Roo", which will cause the outer query to fail.
If you, however, switch the order of the two tables
(from e in dc.mytable where foo == "roo" select new {First= "", Second = e.Roo})
.Union(from d in dc.mytable where foo == "bar" select new {First = d.Foo, Second = d.Roo})
So that the constant assignment within the generated T-SQL comes in the non-first table, then things may work because T-SQL ignores the column names of subsequent tables.
Note: The first table in a union decides both column name and type. So would be smart to get LinqPad anyway.

How does linq-to-sql generate sql for collection pseudoqueries?

My understanding is that the LinqToSql pseudolanguage describes a set using a syntax very similar to SQL and this will allow you to efficiently update a property on a collection of objects:
from b in BugsCollection where b.status = 'closed' set b.status = 'open'
This would update the underlying database using just one SQL statement.
Normally an ORM needs to retieve all of the rows as separate objects, update attributes on each of them and save them individually to the database (at least that's my understanding).
So, how does linq-to-sql avoid having to do this when other orms are not able to avoid it?
The syntax shown in your question is incorrect. LINQ is not intended to have side-effects; it is a query language. The proper way to accomplish what you're looking for is
var x = from b in dataContext.BugsCollection where b.status == "closed";
foreach (var y in x)
y.status = "open";
dataContext.SubmitChanges();
This would generate the single SQL statement that you're talking about. The reason it is able to accomplish this is because of deferred execution - the L2S engine doesn't actually talk to the database until it has to - in this case, because SubmitChanges() was called. L2S then sends the generated SQL statement to the database for execution.
Because LINQ to SQL uses Expression Trees to convert your Query Syntax to actual SQL...it then executes the SQL against the database (rather than pulling all of the data, executing against the in-memory data, and then writing the changes back to the database).
For example, the following Query Syntax:
var records = from r in Records
where r.Property == value
select r
Gets translated first to Lamda Syntax:
Records.Where(r => r.Property == value).Select();
And finally to SQL (via Expression Trees):
SELECT Property, Property2, Property3 FROM Record WHERE Property = #value
...granted, the example doesn't update anything...but the process would be the same for an update query as opposed to a simple select.