select u.user, g.group, u2g.something
from users, groups, u2g
where users.u = u2g.u and groups.g = u2g.g
that returns data like this:
user, group, something
----------------------
1 , 3, a
1 , 5, b
2 , 3, c
3 , 3, d
4 , 5, e
now I would like to limit this query in such a way that it only shows users which are both in groups 3 and 5 - so it would only return {1,3, a} , {1,5, b} for my example data.
edit: I added another column to the data because there may be an incorrect solution using a group by.
edit2: sorry, I was misled by documentation. MySQL 4.0 does not support subqueries :(
edit3: This SQL will be generated programatically for any number of groups (well, up to 20 in current specification) so I would like to avoid solutions that give me too much additional coding to do. If a solution will not be found, I will just modify the resulting .Net 1.1 DataTable, but I would like to avoid that if possible.
edit4: any new idea? Perhaps one without subqueries that includes IN (3,5)?
Using double join with groups-table will give you the correct result:
select u.user, u2g.something
from users
INNER JOIN u2g ON users.u = u2g.u
INNER JOIN groups g1 ON u2g.g = g1.g AND g1.group = 3
INNER JOIN groups g2 ON u2g.g = g2.g AND g2.group = 5
/* try this for two rows, one for each group */
INNER JOIN groups ON u2g.g = groups.g
However, this doesn't exactly match your request, that you want two rows, one for each group. This will only give you one row, you might be able to join it once more with groups therefor rendering two rows.
Another example (if you're selecting using the same groupID that you map against ):
SELECT u.uID, gm.something
FROM cdcms_users u
inner join cdcms_group_memberships gm1 on gm1.uID = u.uID AND gm1.gID = 32
inner join cdcms_group_memberships gm2 on gm2.uID = u.uID AND gm2.gID = 33
select u.user, g.group, u2g.something
from users u, groups g, u2g
where u.user = u2g.user and g.group = u2g.group
where exists
(select 1
from u2g u2g2
where u2g2.user=u.user and u2g2.group in(3,5))
Something along these lines?
select u.[user], g.group
from u
inner join ug on ug.userid = u.id
inner join g on g.id = ug.groupid
inner join
(
select ug.userid
from ug
where ug.groupid in (1,2)
group by ug.userid
having count(*) = 2
) sub on sub.userid = u.id
-Edoode
Quite hideous non-general solution that results in two rows in Oracle:
select users.u, groups.g
from users , groups, u2g, groups g2, u2g u2g2
where users.u = u2g.u
and users.u = u2g2.u
and groups.g = u2g.g
and g2.g = u2g2.g
and (groups.g in (3,5) and g2.g in (3,5) and groups.g <> g2.g)
;
Why is groups used in the query? Its only accessed field (g) exists in u2g. I imagine you probably want to bring back a boatload of stuff from there as well.
In order to get the sort of resultset you describe without the use of subqueries you end up with a real mess: a quadratic explosion of query text!
You'll need something of the following form:
select users.u, groups.g, u2g0.something
from users u, groups g, u2g u2g0, u2g u2g1
where groups.g = 3
and users.u = u2g0.u
and u2g0.g = 3
and users.u = u2g1.u
and u2g1.g = 5
union all
select users.u, groups.g, u2g1.something
from users u, groups g, u2g u2g0, u2g u2g1
where groups.g = 5
and users.u = u2g0.u
and u2g0.g = 3
and users.u = u2g1.u
and u2g1.g = 5
Since this is a programmatically generated query, I'll use a web-page-scripting-like notation here to describe the query construction. I will also make the rash and unwarranted simplifying assumption that the group identifiers are not a potential SQL-injection attack vector. :-)
<% for(int i = 0; i < requiredGroups.Length; i++) { %>
<% if(i > 0) { %>
union all
<% } %>
select users.u, groups.g, u2g<%=i%>.something
from users u, groups g
<% for(int j = 0; j < requiredGroups.Length; j++) { %>
, u2g u2g<%=j%>
<% } %>
where groups.g = <%=requiredGroups[i]%>
<% for(int j = 0; j < requiredGroups.Length; j++) { %>
and users.u = u2g<%=j%>.u
and u2g<%=j>.g = <%=requiredGroups[j]%>
<% } %>
<% } %>
Related
I have a pretty simple sql query:
select * from comments c
inner join users u on u.id = c.user_id
where user_id = 1 OR (c.user_id IN (select user_id_one from friends f where user_id_two = 1))
I am having a lot of trouble getting this translated into TypeORM, specifically this part:
c.user_id IN (select user_id_one from friends f where user_id_two = 1)
No where is it clear on how to use the IN operator along with a inner select statement.
You can add a Where statement and inside that add something like this:
query.where((queryBuilder: SelectQueryBuilder<YOUR_ENTITY>) => {
queryBuilder.where('alias.id in' +
queryBuilder.subQuery()
.select('a.id')
.from(YOUR_ENTITY, 'a')
.getQuery(),
)
})
// ...
I want to do something like :
SELECT "recipes"."id"
FROM "recipes"
INNER JOIN "groups" ON "groups"."recipe_id" = "recipes"."id"
INNER JOIN "steps" ON "steps"."group_id" = "groups"."id"
INNER JOIN "steps_ingredients_memberships" ON "steps_ingredients_memberships"."step_id" = "steps"."id"
INNER JOIN "ingredients" ON "ingredients"."id" = "steps_ingredients_memberships"."ingredient_id"
WHERE (ingredients.id IN (5, 6) AND ingredients.id IN (10, 11))
LIMIT 10
But this request return 0 rows...
I know this request can't work but i can't find a solution
I want to get "Recipes" that have ingredients 5 OR 6 AND 10 OR 11.
Think like :
5 = tomatoes
6 = big tomatoes
10 = potatoes
11 = big potatoes
I want "Recipes" with tomatoes OR big tomatoes AND potatoes OR big potatoes.
Solution adopted
Because ingredients can be random, I wrote a scope :
scope :ingredients_filtering, lambda { |ingredients|
return if ingredients.nil?
queries = []
ingredients.each do |ingredient|
related_ids = ingredient.related_ids.uniq.join(', ')
queries << "BOOL_OR(ingredients.id IN (#{related_ids}))"
end
group(:id).having(queries.join(' AND '))
}
Thanks you to all of you ! :)
To expand on #MrYoshiji's comment Here we can make this more "railsy" as follows:
ingredients_table = Ingredient.arel_table
ingredient_list1 = [5,6]
ingredient_list2 = [10,11]
condition = Arel::Nodes::NamedFunction.new(
'BOOL_OR',[ingredients_table[:id].in(ingredient_list1)]
).and(
Arel::Nodes::NamedFunction.new(
'BOOL_OR',[ingredients_table[:id].in(ingredient_list2)]
)
)
recipes = Recipe.joins(groups: {
steps: {
steps_ingredients_memberships: :ingredients
}
})
.group('recipes.id')
.having(condition)
This will produce SQL similar to the linked answer: (Provided by #ThorstenKettner) e.g.
SELECT recipes.*
FROM recipes
JOIN groups ON groups.recipe_id = recipes.id
JOIN steps ON steps.group_id = groups.id
JOIN steps_ingredients_memberships ON steps_ingredients_memberships.step_id = steps.id
JOIN ingredients ON ingredients.id = steps_ingredients_memberships.ingredient_id
GROUP BY
recipes.id
HAVING
BOOL_OR(ingredients.id IN (5, 6)) AND
BOOL_OR(ingredients.id IN (10, 11))
However it offers the flexibility to change ingredient_list1 and ingredient_list2 in a far more efficient manner without concern for escaping and the like.
BTW to address your comment:
"I put the ruby-on-rails tag because if there is a solution in ruby-on-rails it's better but I don't think so ..."
arel can assemble any query you can imagine (it can assemble invalid/fragmented SQL too). If it is valid SQL ActiveRecord can run it so if you are using rails and have query questions definitely include the ruby-on-rails tag like you did.
Update (based on your posted solution) but using arel rather than string concatenation
scope :ingredients_filtering, lambda { |ingredients|
return unless ingredients
ingredients_table = Ingredient.arel_table
conditions = ingredients.map do |ingredient|
Arel::Nodes::NamedFunction.new(
'BOOL_OR',[ingredients_table[:id].in(ingredient.related_ids)]
)
end.reduce(&:and)
group(:id).having(conditions)
}
You want aggregation. Group by recipe and see if it has the desired ingredients.
SELECT r.id
FROM recipes r
JOIN groups g ON g.recipe_id = r.id
JOIN steps s ON s.group_id = g.id
JOIN steps_ingredients_memberships sim ON sim.step_id = s.id
JOIN ingredients i ON i.id = sim.ingredient_id
GROUP BY r.id
HAVING BOOL_OR(i.id IN (5, 6)) AND BOOL_OR(i.id IN (10, 11));
use GROUP BY and HAVING:
SELECT "recipes"."id"
FROM "recipes"
INNER JOIN "groups" ON "groups"."recipe_id" = "recipes"."id"
INNER JOIN "steps" ON "steps"."group_id" = "groups"."id"
INNER JOIN "steps_ingredients_memberships" ON "steps_ingredients_memberships"."step_id" = "steps"."id"
INNER JOIN "ingredients" ON "ingredients"."id" = "steps_ingredients_memberships"."ingredient_id"
GROUP BY "recipes"."id"
HAVING COUNT(CASE WHEN ingredients.id IN (5, 6) THEN 1 END) > 0
AND COUNT(CASE WHEN ingredients.id IN (10, 11) THEN 1 END) > 0
I have a nested WHERE IN clause in my SQL, how would this translate to LINQ, bonus points for using lambda expressions. New to all of this.
SELECT EndowmentID
FROM Criteria c
WHERE c.ID IN(
SELECT CriterionID
FROM Filters
WHERE ChoiceID IN(
SELECT ChoiceID
FROM Responses
WHERE ApplicationID = 1
)
)
This query can definitely be improved using joins...
SELECT EndowmentID
FROM
Criteria C
JOIN Filters F ON C.ID = F.CriterionID
JOIN Responses R ON F.ChoiceID = R.ChoiceID
WHERE R.ApplicationID = 1
Depending on the keys of your tables you might have to SELECT DISTINCT
From there you can write a simple LINQ query:
from c in Criteria
join f in Filters on c.ID equals f.CriterionID
join r in Responses on f.ChoiceID equals r.ChoiceID
where r.ApplicationID = 1
select c.EndowmentID
Again, you might have to Distinct() this.
var result =
Criteria.Where(c =>
Filters.Where(f =>
Responses.Where(r => r.ApplicationId == 1).Select(r => r.ChoiceId)
.Contains(f.ChoiceId)
).Select(f => f.CriterionId)
.Contains(c.Id)
).Select(c => EndowmentId);
I have a SQl query working fine, I need to convert it to LINQ and got some problems.
My working SQL query:
select d.UserID, d.Content, d.UpdateTime
from DiaryPosts as d
where d.UserID = 2
/* friends */
Union
select d.UserID, d.Content, d.UpdateTime
from DiaryPosts as d
join Friends as fr
on d.UserID = fr.FriendID
where fr.UserID = 2
/* following */
Union
select d.UserID, d.Content, d.UpdateTime
from DiaryPosts as d
join Followers as fl
on d.UserID = fl.UserID
where fl.FollowerID = 2
/* ordenando por UpdateTime desc */
order by 3 desc
What I tried based on my own previous question:
var friends = (from u in db.User
join f in db.Friends
on u.ID equals f.FriendID
where f.UserID == userset.ID
orderby u.Nickname
select new FriendsSet { ID = u.ID, Nickname = u.Nickname, Thumbnail = u.Thumbnail }).ToList();
var followers = (from u in db.User
join f in db.Followers
on u.ID equals f.FollowerID
where f.UserID == userset.ID
orderby u.Nickname
select new FriendsSet { ID = u.ID, Nickname = u.Nickname }).ToList();
var diaryPosts = (from d in db.DiaryPosts
join e in db.EstadosDeAlma
on d.EstadosDeAlmaID equals e.ID
join u in db.User
on d.UserID equals u.ID
where d.UserID == userset.ID
select new DiaryPostsSet {
PostID = d.ID,
EstadoDeAlmaID = e.ID,
EstadoDeAlma = e.Title,
Author = u.Nickname,
Thumbnail = u.Thumbnail,
UserID = u.ID,
IsDuplicated = d.IsDuplicated,
FriendID = d.FriendID,
FriendName = u.Nickname,
Time = d.UpdateTime,
MessagesCount = d.FriendMessages.Count(m => m.DiaryPostsID == d.ID)
}).Take(6).ToList();
var diaryPostsUnion = diaryPosts.Union(friends).Union(followers).OrderBy(d => d.UpdateTime);
The errors I get:
'System.Collections.Generic.List<MvcWebRole1.Models.DiaryPostsSet>' does not contain a definition for 'Union' and the best extension method overload 'System.Linq.ParallelEnumerable.Union<TSource>(System.Linq.ParallelQuery<TSource>, System.Collections.Generic.IEnumerable<TSource>)' has some invalid arguments
Instance argument: cannot convert from 'System.Collections.Generic.List<MvcWebRole1.Models.DiaryPostsSet>' to 'System.Linq.ParallelQuery<MvcWebRole1.Models.FriendsSet>'
You're trying to union a sequence of one type with a sequence of another type. That doesn't make sense - unions have to be over the same types, basically.
It's not really clear what you're trying to do here, but I suspect you don't want a union.
Oh, and if you want all of this to happen in the database, get rid of the ToList calls.
EDIT: Okay, it's hard to see exactly how your LINQ queries correspond to your SQL queries, but basically your "select" clauses should be something like:
from d in db.DiaryPosts
// stuff here
select new { UserID = d.ID, d.UpdateTime, d.Content }
Use the exact same anonymous type in each query - the same property names and types, in the same order - and you should be able to use the Union method.
I have a LEFT OUTER OUTER join in LINQ that is combining with the outer join condition and not providing the desired results. It is basically limiting my LEFT side result with this combination. Here is the LINQ and resulting SQL. What I'd like is for "AND ([t2].[EligEnd] = #p0" in the LINQ query to not bew part of the join condition but rather a subquery to filter results BEFORE the join.
Thanks in advance (samples pulled from LINQPad) -
Doug
(from l in Users
join mr in (from mri in vwMETRemotes where met.EligEnd == Convert.ToDateTime("2009-10-31") select mri) on l.Mahcpid equals mr.Mahcpid into lo
from g in lo.DefaultIfEmpty()
orderby l.LastName, l.FirstName
where l.LastName.StartsWith("smith") && l.DeletedDate == null
select g)
Here is the resulting SQL
-- Region Parameters
DECLARE #p0 DateTime = '2009-10-31 00:00:00.000'
DECLARE #p1 NVarChar(6) = 'smith%'
-- EndRegion
SELECT [t2].[test], [t2].[MAHCPID] AS [Mahcpid], [t2].[FirstName], [t2].[LastName], [t2].[Gender], [t2].[Address1], [t2].[Address2], [t2].[City], [t2].[State] AS [State], [t2].[ZipCode], [t2].[Email], [t2].[EligStart], [t2].[EligEnd], [t2].[Dependent], [t2].[DateOfBirth], [t2].[ID], [t2].[MiddleInit], [t2].[Age], [t2].[SSN] AS [Ssn], [t2].[County], [t2].[HomePhone], [t2].[EmpGroupID], [t2].[PopulationIdentifier]
FROM [dbo].[User] AS [t0]
LEFT OUTER JOIN (
SELECT 1 AS [test], [t1].[MAHCPID], [t1].[FirstName], [t1].[LastName], [t1].[Gender], [t1].[Address1], [t1].[Address2], [t1].[City], [t1].[State], [t1].[ZipCode], [t1].[Email], [t1].[EligStart], [t1].[EligEnd], [t1].[Dependent], [t1].[DateOfBirth], [t1].[ID], [t1].[MiddleInit], [t1].[Age], [t1].[SSN], [t1].[County], [t1].[HomePhone], [t1].[EmpGroupID], [t1].[PopulationIdentifier]
FROM [dbo].[vwMETRemote] AS [t1]
) AS [t2] ON ([t0].[MAHCPID] = [t2].[MAHCPID]) AND ([t2].[EligEnd] = #p0)
WHERE ([t0].[LastName] LIKE #p1) AND ([t0].[DeletedDate] IS NULL)
ORDER BY [t0].[LastName], [t0].[FirstName]
I'm not sure if it will change the result set with "AND ([t2].[EligEnd] = #p0" as part of the subquery rather than the join condition. One thing I like to do with complex queries might help you here. I like to break them into smaller queries before combining them. The deferred execution of LINQ lets us do multiple statements with one eventual call to the database. Something like this:
var elig = from mri in vwMETRemotes
where met.EligEnd == Convert.ToDateTime("2009-10-31")
select mri;
var users = from l in Users
where l.LastName.StartsWith("smith")
where l.DeletedDate == null
var result = from l in users
join mr in elig on l.Mahcpid equals mr.Mahcpid into lo
from g in lo.DefaultIfEmpty()
orderby l.LastName, l.FirstName
select g
Breaking it down like that can make it easier to debug, and perhaps it can tell LINQ better what you intend.
Code ended up looking like this. RecodePopulation and RecordRegistration are just methods to translate values from the query.
var elig = from mri in db.MetRemote
where mri.EligEnd == Convert.ToDateTime(ConfigurationManager.AppSettings["EligibilityDate"])
orderby mri.EligEnd
select mri;
var users = from l in db.Users
where l.LastName.StartsWith(filter)
where l.DeletedDate == null
select l;
var results = (from l in users
join m in elig on l.MahcpId equals m.MAHCPID into lo
from g in lo.DefaultIfEmpty()
orderby l.LastName, l.FirstName
select new UserManage()
{
Username = l.Username,
FirstName = l.FirstName,
LastName = l.LastName,
DateOfBirth = l.DOB,
Gender = l.Gender,
Status = RecodePopulation(g.Population, l.CreatedDate),
UserId = l.Id,
WellAwardsRegistered = RecodeRegistration(l.Id, 1)
}).Distinct().OrderBy(a => a.LastName).ThenBy(n => n.FirstName).Skip((currentPage - 1) * resultsPerPage).Take(resultsPerPage);