ORA-00907: Distinct, join and group by in LINQ C# - c#

I'm getting the error code ORA-00907, when executing the linq query below. It seems to be Oracle specific. The problem seems to be the "group by" subquery.
Lets say I have these two tables: USER and ADDRESS, with columns:
USER{userid, addressid},
ADDRESS{addressid, streetname}
Table ADDRESS contains several rows with the same addressid, so I guess I would like to group the ADDRESS-table (DISTINCT) on the addressid so I only get one match with addressid in USER-table, it should also be a LEFT JOIN, so if there is no match I still get the USER-record.
I have tried several different approaches, My code (example):
List<MyObject> result =
(
from u in context.USER.Where(i => i.userid > 100)
join a in (from address in context.ADDRESS group address by address.addressid)
on u.addressid equals a.FirstOrDefault().addressid into joinedaddress
from lfjoinedaddress in joinedaddress.DefaultIfEmpty()
join email in context.EMAIL on u.userid equals email.userid into jemail
from lfjemail in jemail.DefaultIfEmpty()
select new MyObject()
{
UserId = u.userid,
StreetName = lfjoinedaddress.streetname,
UserEmail = lfjemail.emailaddress
}
).ToList();
Someone know how to achieve this, by rewriting the query so it works against Oracle.
UPDATE:
This is the generated sql-query, except the "email":
SELECT
1 AS "C1",
"Extent1"."USERID" AS "USERID",
"Extent1"."ADDRESSID" AS "ADDRESSID"
FROM (SELECT
"USER"."USERID" AS "USERID",
"USER"."ADDRESSID" AS "ADDRESSIF",
FROM "EXT"."USER" "USER") "Extent1"
LEFT OUTER JOIN (SELECT "Distinct1"."ADDRESSID" AS "ADDRESSID1", "Limit1"."ADDRESSID" AS "ADDRESSID2", , "Limit1"."STREETNAME" AS "STREETNAME1"
FROM (SELECT DISTINCT
"Extent2"."ADDRESSID" AS "ADDRESSID"
FROM (SELECT
"ADDRESS"."ADDRESSID" AS "ADDRESSID",
"ADDRESS"."STREETNAME" AS "STREETNAME",
FROM "EXT"."ADDRESS" "ADDRESS") "Extent2" ) "Distinct1"
OUTER APPLY (SELECT "Extent3"."ADDRESSID" AS "ADDRESSID", "Extent3"."STREETNAME" AS "STREETNAME"
FROM (SELECT
"ADDRESS"."ADDRESSID" AS "ADDRESSID",
"ADDRESS"."STREETNAME" AS "STREETNAME",
FROM "EXT"."ADDRESS" "ADDRESS") "Extent3"
WHERE ("Distinct1"."ADDRESSID" = "Extent3"."ADDRESSID") AND (ROWNUM <= (1) ) ) "Limit1"
OUTER APPLY (SELECT "Extent4"."ADDRESSID" AS "ADDRESSID", , "Extent4"."STREETNAME" AS "STREETNAME"
FROM (SELECT
"ADDRESS"."ADDRESSID" AS "ADDRESSID",
"ADDRESS"."STREETNAME" AS "STREETNAME",
FROM "EXT"."ADDRESS" "ADDRESS") "Extent4"
WHERE ("Distinct1"."ADDRESSID" = "Extent4"."ADDRESSID") AND (ROWNUM <= (1) ) ) "Limit2" ) "Apply2" ON ("Extent1"."ADDRESSID" = "Apply2"."ADDRESSID2") OR (("Extent1"."ADDRESSID" IS NULL) AND ("Apply2"."ADDRESSID3" IS NULL))))

DISTINCT is applied to tuples not an individual value within a tuple. If STREETNAME is always the same per ADDRESSID in table ADDRESS, then you want DISTINCT tuples of (ADDRESSID, STREETNAME). Which you could simply do with selecting the distinct columns of context.ADDRESS as your subquery and omit the .FirstOrDefault().
join a in
(
from address in context.ADDRESS
select new
{
address.addressid,
address.streetname
}
).Distinct()
on u.addressid equals a.addressid into joinedaddress
from lfjoinedaddress in joinedaddress.DefaultIfEmpty()
If STREETNAME is not always the same per ADDRESSID, then you don't want DISTINCT at all.

Related

How can I turn SQL query that joins two columns and groups by count of one column and a column of each joined table into LINQ?

In my database, each URI has associated tags (Tag table) and each pageview (PageView table) is associated with someone viewing a particular page. I want to return a list of URIs that have the same tags as a given URI, by count of each URI that shares those tag(s). My SQL query looks like this:
select count(URI) as 'Count', p.URI, t.Name
from tracking.PageView as p
inner join Tracking.Tag as t on p.ID = t.PageViewID
where t.name in
(select t.Name
from tracking.PageView as p
inner join Tracking.Tag as t on p.ID = t.PageViewID
where p.URI = 'URI WE WANT TAGS OF'
)
and p.uri like '%/articles/%'
group by p.URI , t.name
order by Count desc
My apologies if the description is too vague for the query or if the query itself is rough. It was just the first one that worked. I've tried to separate the subquery into a variable and select values in that subquery, but it's been some time since I've used LINQ and I'm spinning wheels at this point.
The following is pretty much an exact translation of your current SQL query, which should get you started.
from p in tracking.PageView
join t in Tracking.Tag on p.ID equals t.PageViewID
where p.uri.Contains("/articles/")
&& (
from p2 in tracking.PageView
join t2 in Tracking.Tag on p2.ID equals t2.PageViewID
where p2.URI == "URI WE WANT TAGS OF"
select t2.name
).Contains(t.name)
group new { p, t } by new { p.URI, t.name } into g
orderby g.Count() descending
select new {
Count = g.Count(),
g.Key.URI,
g.Key.Name
}

SQL: Count rows which are not present in table

I got two tables called: EmployeeTable & TaskAssignmentTable.
They look like this :
TaskAssignmentTable shows tasks assigned to employees. In order to assign new tasks to employees i want to have count of tasks assigned to different people and then assign task to people who have least tasks assigned.
Problem: using normal count() on TaskAssignmentTable results in this table:
But what i want is some sort of join between tables which shows count of rows which are present in first table and absent in 2nd table with count equal to 0 like this one:
So what would be the SQL query to join tables and do such thing? (Optional: Since I'm using C# Linq-2-SQL i would be grateful if someone can write LINQ syntax for this).
You need a LEFT OUTER JOIN based upon your statement that you want rows that are present in the first table but not the second:
SELECT EmployeeID, Name, Count(TaskID) as CNT
FROM EmployeeTable e
LEFT JOIN TaskAssignmentTable t
ON e.employeeID = t.FKEmployeeID
GROUP BY EmployeeID, Name
Try
SELECT EmployeeID, Name, Count(TaskID) as CNT
FROM EmployeeTable emp
LEFT JOIN TaskAssignmentTable task on emp.employeeID = task.FKEmployeeID
GROUP BY EmployeeID, Name
For that you have to use Left Outer Join.
SELECT EmployeeID, Name, Count(TaskID) as CNT
FROM EmployeeTable emp
LEFT OUTER JOIN TaskAssignmentTable task on emp.employeeID = task.FKEmployeeID
GROUP BY EmployeeID, Name
And LINQ Version of this query look like this
var employees = from emp in dbContext.Employees
join task in dbContext.TaskAssignmentTable
on emp.employeeID equals task.FKEmployeeID
into tEmpWithTask
from tEmp in tEmpWithTask.DefaultIfEmpty()
group tEmp by new { emp.EmployeeID, emp.Name } into grp
select new {
grp.Key.EmployeeID,
grp.Key.Name,
grp.Count(t=>t.TaskID != null)
};
You need to OUTER JOIN the two table (in your case a LEFT JOIN):
SELECT EmployeeID, Name, Count(TaskID) as CNT
FROM EmployeeTable emp
LEFT JOIN TaskAssignmentTable task on emp.employeeID = task.FKEmployeeID
GROUP BY EmployeeID, Name

Why this error occurs: Subquery returned more than 1 value

My Sql Query
SELECT BOOKING_TIME,
Contact_No,
(FName+LName)AS NAME ,
E_MAIL,
(SELECT ZM.ZONE_NAME
FROM Zone_Master ZM
INNER JOIN BOOKINGS ON ZM.Zone_ID = BOOKINGS.Zone_ID)AS ZONE_NAME,
City,
Addr_1,
Addr_2,
PIN,
(SELECT PROJECTS.PROJECT_NAME
FROM PROJECTS
INNER JOIN BOOKINGS ON PROJECTS.PROJECT_ID=BOOKINGS.PROJECT_ID)AS PROJECT_NAME
FROM BOOKINGS
You're getting that error because of your subqueries:
(SELECT ZM.ZONE_NAME
FROM Zone_Master ZM
INNER JOIN BOOKINGS ON ZM.Zone_ID = BOOKINGS.Zone_ID) AS ZONE_NAME
And:
(SELECT PROJECTS.PROJECT_NAME
FROM PROJECTS
INNER JOIN BOOKINGS ON PROJECTS.PROJECT_ID = BOOKINGS.PROJECT_ID) AS PROJECT_NAME
You're getting multiple records back and trying to store them in a single field.
Here is your query:
SELECT BOOKING_TIME, Contact_No,(FName+LName)AS NAME, E_MAIL,
(SELECT ZM.ZONE_NAME
FROM Zone_Master ZM INNER JOIN
BOOKINGS
ON ZM.Zone_ID = BOOKINGS.Zone_ID
) AS ZONE_NAME,
City, Addr_1, Addr_2, PIN,
(SELECT PROJECTS.PROJECT_NAME
FROM PROJECTS INNER JOIN
BOOKINGS
ON PROJECTS.PROJECT_ID=BOOKINGS.PROJECT_ID
) AS PROJECT_NAME
FROM BOOKINGS;
Either subquery could be returning more than one row. In a subselect in the select clause, you can only return one value. I think there is an easy fix. You probably want correlated subuqeries, so just remove the BOOKINGS table from each subquery:
SELECT BOOKING_TIME, Contact_No, (FName+LName)AS NAME, E_MAIL,
(SELECT ZM.ZONE_NAME
FROM Zone_Master ZM
WHERE ZM.Zone_ID = BOOKINGS.Zone_ID
) AS ZONE_NAME,
City,Addr_1,Addr_2,PIN,
(SELECT PROJECTS.PROJECT_NAME
FROM PROJECTS
WHERE PROJECTS.PROJECT_ID = BOOKINGS.PROJECT_ID
)AS PROJECT_NAME
FROM BOOKINGS;
These are now "correlated subqueries". In this case, they should each return at most one row.
Another way to express this query is using join syntax:
SELECT BOOKING_TIME, Contact_No, (FName+LName)AS NAME, E_MAIL,
ZM.ZONE_NAME,
City, Addr_1, Addr_2, PIN,
p.PROJECT_NAME
FROM BOOKINGS b LEFT OUTER JOIN
Zone_Master zm
on ZM.Zone_ID = BOOKINGS.Zone_ID LEFT OUTER JOIN
PROJECTS p
on p.PROJECT_ID = b.PROJECT_ID

linq to sql optimized a group with multiple joins

I need help generating a more efficient LINQ query:
Table: Positions
-PositionID
-Name
Table: Person
-PersonID
-Name, etc...
Table: PersonPosition
-PersonID
-PositionID
I need a result set that groups the people assigned to each position:
PositionID Person
1 John
Bob
Frank
2 Bill
Tom
Frank, etc...
My first thought was this LINQ query:
from perspos in PersonPositions
join pers in Persons on perspos.PersonID equals pers.PersonID
group pers by perspos.PositionID into groups
select new {groups.Key, groups}
Which works great, but produces the following SQL:
SELECT [t0].[PositionID] AS [Key]
FROM [PersonPosition] AS [t0]
INNER JOIN [Person] AS [t1] ON [t0].[PersonID] = [t1].[PersonID]
GROUP BY [t0].[PositionID]
GO
-- Region Parameters
DECLARE #x1 Int = 3
-- EndRegion
SELECT [t1].[PersonID], [t1].[UserID], [t1].[Firstname], [t1].[Lastname], [t1].[Email], [t1].[Phone], [t1].[Mobile], [t1].[Comment], [t1].[Permissions]
FROM [PersonPosition] AS [t0]
INNER JOIN [Person] AS [t1] ON [t0].[PersonID] = [t1].[PersonID]
WHERE #x1 = [t0].[PositionID]
GO
-- Region Parameters
DECLARE #x1 Int = 4
-- EndRegion
SELECT [t1].[PersonID], [t1].[UserID], [t1].[Firstname], [t1].[Lastname], [t1].[Email], [t1].[Phone], [t1].[Mobile], [t1].[Comment], [t1].[Permissions]
FROM [PersonPosition] AS [t0]
INNER JOIN [Person] AS [t1] ON [t0].[PersonID] = [t1].[PersonID]
WHERE #x1 = [t0].[PositionID]
GO
-- Region Parameters
DECLARE #x1 Int = 5
-- EndRegion
SELECT [t1].[PersonID], [t1].[UserID], [t1].[Firstname], [t1].[Lastname], [t1].[Email], [t1].[Phone], [t1].[Mobile], [t1].[Comment], [t1].[Permissions]
FROM [PersonPosition] AS [t0]
INNER JOIN [Person] AS [t1] ON [t0].[PersonID] = [t1].[PersonID]
WHERE #x1 = [t0].[PositionID]
GO
on and on...
Is there a better LINQ query that translates to a more efficient SQL statement?
You should already have the relationship defined in your database, and also on your dbml.
Avoid doing joins when you don't have to; they are really tedious. Let LINQ-to-SQL do this for you. Something like this should work:
var data = context.PersonPositions
.Select(pos => new { pos.PositionID, pos.Person });
return data.GroupBy(pos => pos.PositionID);
or
return context.Positions.Select(pos =>
new { pos, pos.PersonPositions.Select(pp => pp.Person).ToList() }).ToList();
I'm fairly sure you have to just join the tables and select the result, then call .AsEnumerable(), and group after that:
(from perspos in PersonPositions
join pers in Persons
on perspos.PersonID equals pers.PersonID
select new { perspos.PositionID, Person = pers })
.AsEnumerable().GroupBy(p => p.PositionID, p => p.Person);

Queries generated by group by vs group join

I have the following group by linq statement
from c in Categories
join p in Products on c equals p.Category into ps
select new { Category = new {c.CategoryID, c.CategoryName}, Products = ps };
However this generates the following left outer join query and returns all categories even if there are no products associated.
SELECT [t0].[CategoryID], [t0].[CategoryName], [t1].[ProductID], [t1].[ProductName], [t1].[SupplierID], [t1].[CategoryID] AS [CategoryID2], [t1].[QuantityPerUnit], [t1].[UnitPrice], [t1].[UnitsInStock], [t1].[UnitsOnOrder], [t1].[ReorderLevel], [t1].[Discontinued], (
SELECT COUNT(*)
FROM [Products] AS [t2]
WHERE [t0].[CategoryID] = [t2].[CategoryID]
) AS [value]
FROM [Categories] AS [t0]
LEFT OUTER JOIN [Products] AS [t1] ON [t0].[CategoryID] = [t1].[CategoryID]
ORDER BY [t0].[CategoryID], [t1].[ProductID]
What I really want is to return only those categories that have associated products. But if I re-write the linq query like so:
from c in Categories
join p in Products on c equals p.Category
group p by new {c.CategoryID, c.CategoryName} into ps
select new { Category = ps.Key, Products = ps };
This gives me the desired result but a query is generated for each category:
SELECT [t0].[CategoryID], [t0].[CategoryName]
FROM [Categories] AS [t0]
INNER JOIN [Products] AS [t1] ON [t0].[CategoryID] = [t1].[CategoryID]
GROUP BY [t0].[CategoryID], [t0].[CategoryName]
GO
-- Region Parameters
DECLARE #x1 Int SET #x1 = 1
DECLARE #x2 NVarChar(9) SET #x2 = 'Beverages'
-- EndRegion
SELECT [t1].[ProductID], [t1].[ProductName], [t1].[SupplierID], [t1].[CategoryID], [t1].[QuantityPerUnit], [t1].[UnitPrice], [t1].[UnitsInStock], [t1].[UnitsOnOrder], [t1].[ReorderLevel], [t1].[Discontinued]
FROM [Categories] AS [t0]
INNER JOIN [Products] AS [t1] ON [t0].[CategoryID] = [t1].[CategoryID]
WHERE (#x1 = [t0].[CategoryID]) AND (#x2 = [t0].[CategoryName])
GO
-- Region Parameters
DECLARE #x1 Int SET #x1 = 2
DECLARE #x2 NVarChar(10) SET #x2 = 'Condiments'
-- EndRegion
SELECT [t1].[ProductID], [t1].[ProductName], [t1].[SupplierID], [t1].[CategoryID], [t1].[QuantityPerUnit], [t1].[UnitPrice], [t1].[UnitsInStock], [t1].[UnitsOnOrder], [t1].[ReorderLevel], [t1].[Discontinued]
FROM [Categories] AS [t0]
INNER JOIN [Products] AS [t1] ON [t0].[CategoryID] = [t1].[CategoryID]
WHERE (#x1 = [t0].[CategoryID]) AND (#x2 = [t0].[CategoryName])
GO
...
Is there a way to do the equivalent of a inner join and group by and still only produce a single query like the group join?
var queryYouWant =
from c in Categories
join p in Products on c equals p.Category
select new {Category = c, Product = p};
var result =
from x in queryYouWant.AsEnumerable()
group x.Product by x.Category into g
select new { Category = g.Key, Products = g };
Is there a way to do the equivalent of a inner join and group by and still only produce a single query like the group join?
No. When you say GroupBy followed by non-aggregated access of the group elements, that's a repeated query with the group key as a filter.
What is the purpose of that join?
Your original query is identical to this:
from c in Categories
select new { Category = new { c.CategoryID, c.CategoryName }, c.Products }
Am I somehow missing something obvious???
If you want only categories with products, then do this:
from c in Categories
where c.Products.Any()
select new { Category = new { c.CategoryID, c.CategoryName }, c.Products }
Or, if you want to flatten the results:
from p in Products
select new { p, p.Category.CategoryID, p.Category.CategoryName }
The latter will translate into an inner or outer join - depending on whether that relationship is nullable. You can force the equivalent of an inner join as follows:
from p in Products
where p.Category != null
select new { p, p.Category.CategoryID, p.Category.CategoryName }

Categories

Resources