I want to translate query like this:
SELECT * FROM Product WHERE Product.ID in (SELECT Product.ID FROM other_table)
into LINQ. I read about using the contains method but the problem is that it generates a lot of parameters for each id passed in like this:
WHERE [Product].[ID] IN (#p0, #p1)
If I had for example one bilion parameters I want to pass into my query the server won't be able to execute such a long query. Is it possible to create LINQ query in such a way that the generated SQL will be close to the original?
Thanks,
Romek
If you are using large tables then IN statments are a bad idea, they are very slow. You should be doing joins.
Anyway, here is what you want;
using(dbDataContext db = new dbDataContext())
{
var result = from p in db.products
join o in db.other_table
on p.ID equals o.ID
select p;
}
You should be able to use join for this.
other_Table.Join(product, ot => ot.Id, pd => pd.Id, (pd, ot) => pd);
Related
Can you use Doctrine QueryBuilder to INNER JOIN a temporary table from a full SELECT statement that includes a GROUP BY?
The ultimate goal is to select the best version of a record. I have a viewVersion table that has multiple versions with the same viewId value but different timeMod. I want to find the version with the latest timeMod (and do a lot of other complex joins and filters on the query).
Initially people assume you can do a GROUP BY viewId and then ORDER BY timeMod, but ORDER BY has no effect on GROUP BY, and MySQL will return random results. There are a ton of answers out there (e.g. here) that explain the problem with using GROUP and offer a solution, but I am having trouble interpreting the Doctrine docs to find a way to implement the SQL with Doctrine QueryBuilder (if it's even possible). Why don't I just use DQL? I may have to, but I have a lot of dynamic filters and joins that are much easier to do with QueryBuilder, so I wanted to see if that's possible.
Sample MySQL to Reproduce in Doctrine QueryBuilder
SELECT vv.*
FROM view_version vv
#inner join only returns where the result sets overlap, i.e. one record
INNER JOIN (
SELECT MAX(timeMod) maxTimeMod, viewId
FROM view_version
GROUP BY viewId
) version ON version.viewId = vv.viewId AND vv.timeMod = version.maxTimeMod
#join other tables for filter, etc
INNER JOIN view v ON v.id = vv.viewId
INNER JOIN content_type c ON c.id = v.contentTypeId
WHERE vv.siteId=1
AND v.contentTypeId IN (2)
ORDER BY vv.title ASC;
Theoretical Solution via Query Builder (not working)
I am thinking that the JOIN needs to inject a DQL statement, e.g.
$em = $this->getDoctrine()->getManager();
$viewVersionRepo = $em->getRepository('GutensiteCmsBundle:View\ViewVersion');
$queryMax = $viewVersionRepo->createQueryBuilder()
->addSelect('MAX(timeMod) AS timeModMax')
->addSelect('viewId')
->groupBy('viewId');
$queryBuilder = $viewVersionRepo->createQueryBuilder('vv')
// I tried putting the query in a parenthesis, to no avail
->join('('.$queryMax->getDQL().')', 'version', 'WITH', 'vv.viewId = version.viewId AND vv.timeMod = version.timeModMax')
// Join other Entities
->join('e.view', 'view')
->addSelect('view')
->join('view.contentType', 'contentType')
->addSelect('contentType')
// Perform random filters
->andWhere('vv.siteId = :siteId')->setParameter('siteId', 1)
->andWhere('view.contentTypeId IN(:contentTypeId)')->setParameter('contentTypeId', $contentTypeIds)
->addOrderBy('e.title', 'ASC');
$query = $queryBuilder->getQuery();
$results = $query->getResult();
My code (which may not match the above example perfectly) outputs:
SELECT e, view, contentType
FROM Gutensite\CmsBundle\Entity\View\ViewVersion e
INNER JOIN (
SELECT MAX(v.timeMod) AS timeModMax, v.viewId
FROM Gutensite\CmsBundle\Entity\View\ViewVersion v
GROUP BY v.viewId
) version WITH vv.viewId = version.viewId AND vv.timeMod = version.timeModMax
INNER JOIN e.view view
INNER JOIN view.contentType contentType
WHERE e.siteId = :siteId
AND view.contentTypeId IN (:contentTypeId)
ORDER BY e.title ASC
This Answer seems to indicate that it's possible in other contexts like IN statements, but when I try the above method in the JOIN, I get the error:
[Semantical Error] line 0, col 90 near '(SELECT MAX(v.timeMod)': Error: Class '(' is not defined.
A big thanks to #AdrienCarniero for his alternative query structure for sorting the highest version with a simple JOIN where the entity's timeMod is less than the joined table timeMod.
Alternative Query
SELECT view_version.*
FROM view_version
#inner join to get the best version
LEFT JOIN view_version AS best_version ON best_version.viewId = view_version.viewId AND best_version.timeMod > view_version.timeMod
#join other tables for filter, etc
INNER JOIN view ON view.id = view_version.viewId
INNER JOIN content_type ON content_type.id = view.contentTypeId
WHERE view_version.siteId=1
# LIMIT Best Version
AND best_version.timeMod IS NULL
AND view.contentTypeId IN (2)
ORDER BY view_version.title ASC;
Using Doctrine QueryBuilder
$em = $this->getDoctrine()->getManager();
$viewVersionRepo = $em->getRepository('GutensiteCmsBundle:View\ViewVersion');
$queryBuilder = $viewVersionRepo->createQueryBuilder('vv')
// Join Best Version
->leftJoin('GutensiteCmsBundle:View\ViewVersion', 'bestVersion', 'WITH', 'bestVersion.viewId = e.viewId AND bestVersion.timeMod > e.timeMod')
// Join other Entities
->join('e.view', 'view')
->addSelect('view')
->join('view.contentType', 'contentType')
->addSelect('contentType')
// Perform random filters
->andWhere('vv.siteId = :siteId')->setParameter('siteId', 1)
// LIMIT Joined Best Version
->andWhere('bestVersion.timeMod IS NULL')
->andWhere('view.contentTypeId IN(:contentTypeId)')->setParameter('contentTypeId', $contentTypeIds)
->addOrderBy('e.title', 'ASC');
$query = $queryBuilder->getQuery();
$results = $query->getResult();
In terms of performance, it really depends on the dataset. See this discussion for details.
TIP: The table should include indexes on both these values (viewId and timeMod) to speed up results. I don't know if it would also benefit from a single index on both fields.
A native SQL query using the original JOIN method may be better in some cases, but compiling the query over an extended range of code that dynamically creates it, and getting the mappings correct is a pain. So this is at least an alternative solution that I hope helps others.
I am in the process of building a dashboard and need to extract some data from a somewhat complex schema.
I have a select statement (see below) that I am using to extract information, but need to conduct some filtering on part of the select statement, and I'm struggling.
select distinct j.id 'Job_Id'
, js.outcome 'Outcome'
,(select string from property where parent_sheet_id = ps.id and name= 'Build_Type') as 'Build_Type'
from job j, job_step js, property_sheet ps, property p
where j.id = js.job_id
and ps.entity_id=js.id
and ps.id=p.parent_sheet_id
and ps.entity_type='step'
and p.name = 'Id'
group by j.id
order by j.id desc;
I am sure that there is a better way of doing this query, and I would appreciate any other suggestions, but I am mostly attempting to place a filter on the nested select statement which has an alias of "Build_Type", but when I try it appears not to work. I've read some blogs that this is not possible, so I am a little stuck.
Any help would be much appreciated.
Thanks.
select
ps.id,
Build_Type.string
from
property_sheet as ps
left join
property as Build_Type
on ps.id = Build_Type.parent_sheet_id and Build_Type.name = 'Build_Type'
where Build_Type....
I would like to do a subquery and then inner join the result of that to produce a query. I want to do this as I have tested an inner join query and it seems to be far more performant on MySql when compared to a straight IN subquery.
Below is a very basic example of the type of sql I am trying to reproduce.
Tables
ITEM
ItemId
Name
ITEMRELATIONS
ItemId
RelationId
Example Sql I would Like to create
Give me the COUNT of RELATIONs for ITEMs having a name of 'bob':
select ir.itemId, count(ir.relationId)
from ItemRelations ir
inner join (select itemId from Items where name = 'bob') sq
on ir.itemId = sq.itemId
group by ir.itemId
The base Nhibernate QueryOver
var bobItems = QueryOver.Of<Item>(() => itemAlias)
.Where(() => itemAlias.Name == "bob")
.Select(Projections.Id());
var bobRelationCount = session.QueryOver<ItemRelation>(() => itemRelationAlias)
.Inner.Join(/* Somehow join the detached criteria here on the itemId */)
.SelectList(
list =>
list.SelectGroup(() => itemRelationAlias.ItemId)
.WithAlias(() => itemRelationCountAlias.ItemId)
.SelectCount(() => itemRelationAlias.ItemRelationId)
.WithAlias(() => itemRelationCountAlias.Count))
.TransformUsing(Transformers.AliasToBean<ItemRelationCount>())
.List<ItemRelationCount>();
I know it may be possible to refactor this into a single query, however the above is merely as simple example. I cannot change the detached QueryOver, as it is handed to my bit of code and is used in other parts of the system.
Does anyone know if it is possible to do an inner join on a detached criteria?
MySql 5.6.5 has addressed the performance issue related to the query structure.
See here: http://bugs.mysql.com/bug.php?id=42259
No need for me to change the output format of my NHibernate queries anymore. :)
It's been a while since I've written raw SQL, I was hoping someone could help me out in optimizing this SQL query so that it works across, both, MySQL and PostgreSQL.
I would also have to implement this via CodeIgniter (2.x) using ActiveRecord, any help/advice?
SELECT *
FROM notaries, contact_notaries
WHERE notaries.id = contact_notaries.notary_id
AND WHERE ( contact_notaries.city LIKE %$criteria%
OR contact_notaries.state LIKE %$criteria
OR contact_notaries.address LIKE %$criteria%)
Thanks!
Each query can have just one WHERE clause (you don't need the second)
It's much better to put join condition into JOIN rather then WHERE.
Are you sure you really need all the columns from 2 tables (*)?
So I'd refactor it to
SELECT [field_list]
FROM notaries
INNER JOIN contact_notaries ON (notaries.id = contact_notaries.notary_id)
WHERE ( contact_notaries.city LIKE '%$criteria%'
OR contact_notaries.state LIKE '%$criteria'
OR contact_notaries.address LIKE '%$criteria%')
Using a1ex07's query:
SELECT [field_list]
FROM notaries
INNER JOIN contact_notaries ON (notaries.id = contact_notaries.notary_id)
WHERE ( contact_notaries.city LIKE '%$criteria%'
OR contact_notaries.state LIKE '%$criteria'
OR contact_notaries.address LIKE '%$criteria%')
Active record:
$this->db->select(); // Leave empty to select all fields
$this->db->join('contact_notaries', 'notaries.id = contact_notaries.notary_id', 'inner');
$this->db->like('contact_notaries.city', 'criteria');
$this->db->like('contact_notaries.state', 'criteria');
$this->db->like('contact_notaries.address', 'match');
$results = $this->db->get('notaries');
To specify a list of fields you can do $this->db->select('field_1, field_2, ...');.
http://codeigniter.com/user_guide/database/active_record.html
I wonder if someone can help me. I want to replicate the following SQL query using LINQ in VB.Net.I'm a little unclear on how to do subqueries / aggregates.
Thanks
SELECT *
FROM Server S
INNER JOIN ServerHDD H
ON S.Server_ID = H.Server_ID
INNER JOIN (SELECT MAX(ServerHDD_ID) AS ServerHDD_ID
FROM ServerHDD
GROUP BY Server_ID, Letter) Filter
ON H.ServerHDD_ID = Filter.ServerHDD_ID
ORDER BY S.Hostname, H.Letter
Got this as below in C# => need VB.Net Conversion please.
from S in SERVER
join H in SERVERHDD on S.Server_ID equals H.Server_ID
join FILTER in
(from s in SERVERHDD group s
by new {s.Server_ID, s.Letter}
into groupedServerHDD select new
{
SERVERHDD_ID = groupedServer.Sum(gS=>gS.ServerHDD_ID)
}
)
on H.ServerHDD_ID equals FILTER.SERVERHDD_ID
orderby S.Hostname, H.Letter
select S
This is my most favorite page regarding this topic. I love LINQ to SQL (and wish they intended to continue support for it over Entity Framework...) http://msdn.microsoft.com/en-us/vbasic/bb688085.aspx. On this page you will find all the answers to your querying needs. It is hard to format the query here without something to test it against!
Your inner joins go away with the simple join syntax of LtS. You can either say .Max() on your inner select or Max(pseudo functoid here) like this:
From p2 In g _
Where p2.UnitPrice = g.Max(Function(p3) p3.UnitPrice) _
Select p2
There are a couple of LINQ learning tools out there that are pretty cool.
This one is my favourite... LinqPad. But you might also want to check out Linqer.
You should be able to paste your code into one of them apps and it will show you how its converted. Hope they come in handy :)