Linq outer join with grouping in subquery - linq-to-sql

I need to convert the following correlated subquery in Linq to Sql. I am able to achieve the left outer join part. However, grouping in a subquery is where i am getting incorrect results.
SELECT
ae.Id,ae.Title
,(select COUNT(*) from [dbo].[AssociationEventRSVP] where RSVPStatus='Y'
group by AssociationEventId, RSVPStatus having RSVPStatus='Y'
and AssociationEventId=ar.AssociationEventId) as CountYes
,(select COUNT(*) from [dbo].[AssociationEventRSVP]
group by AssociationEventId, RSVPStatus having RSVPStatus='N'
and AssociationEventId=ar.AssociationEventId) as CountNo
FROM [dbo].[AssociationEvents] as ae
left outer join AssociationEventRSVP as ar
on ae.Id=ar.AssociationEventId
Thanks in advance for your help.
Tushar M.

I'd first refactor your query to this:
SELECT
ae.Id,
ae.Title,
(select COUNT(*) FROM [dbo].[AssociationEventRSVP] WHERE RSVPStatus='Y' AND AssociationEventId=ae.Id) AS CountYes,
(select COUNT(*) FROM [dbo].[AssociationEventRSVP] WHERE RSVPStatus='N' AND AssociationEventId=ae.Id) AS CountNo
FROM [dbo].[AssociationEvents] as ae
And here's a simple (not necessarily efficient) LINQ to SQL conversion:
var results = from ae in context.AssociationEvents
select new
{
ae.Id,
ae.Title,
CountYes = context.AssociationEventRSVP.Where(aer => aer.RSVPStatus == "Y" && aer.AssociationEventId == ae.Id).Count(),
CountNo = context.AssociationEventRSVP.Where(aer => aer.RSVPStatus == "N" && aer.AssociationEventId == ae.Id).Count()
};

Related

Group by a pair

My Query
SELECT si1.itemno,si2.itemno,count(*)
FROM
(SELECT * FROM `saleitems` WHERE orgno = 9) as si1
INNER JOIN
(SELECT * FROM saleitems WHERE orgno =9) as si2
ON si1.invoiceno = si2.invoiceno AND si1.itemno != si2.itemno WHERE 1 GROUP BY si1.itemno,si2.itemno
Output:click to see output
But i expect 'group by (si1.itemno,si2.itemno) pair'. Like 3,4 and 4,3 should be grouped together in a single row.
The DBMS doesn't know that si1.itemno and si2.itemno are interchangeable, so as far as it's concerned si1.itemno=3,si2.itemno=4 is different from si1.itemno=4,si2.itemno=3.
The easiest way to eliminate those duplicates is to change the condition of your self-join so that it only finds the pairs with the IDs in order: just change the si1.itemno != si2.itemno condition to si1.itemno < si2.itemno.
Note that I referred to it as a "self-join", because although you currently have sub-queries, you don't need to, because you can just move the WHERE clauses to the final WHERE:
SELECT
si1.itemno,
si2.itemno,
count(*)
FROM
saleitems as si1
INNER JOIN
saleitems as si2
ON si1.invoiceno = si2.invoiceno
AND si1.itemno < si2.itemno
WHERE
si1.orgno = 9
AND si2.orgno = 9
GROUP BY
si1.itemno,
si2.itemno

SQL error as a result of rewriting a query using subquery into a query using join

The original query:
SELECT o.offering_number,
o.english_description,
o.french_description,
fop.price_amount,
fop.price_type_code,
fop.price_status_code,
fop.offering_id,
(SELECT fop1.price_amount from facility_offering_price fop1
WHERE fop.offering_id = fop1.Offering_Id
AND fop1.price_type_code = 5
AND fop1.price_status_code = 3
) as 'priceAmount'
from facility_offering_price fop
join offering o on fop.offering_id = o.offering_id
WHERE fop.price_start_date = '15-10-28'
AND fop.price_status_code IN (1,2)
/*AND (price_status_code IS NULL)*/
AND fop.price_type_code = 5
/*AND (o.offering_number IS NULL)*/
ORDER BY o.offering_number ASC, fop.price_sequence_number ASC;
It produces a result of one entry.
The result query:
SELECT o.offering_number,
o.english_description,
o.french_description,
fop.price_amount,
fop2.price_amount,
fop.price_type_code,
fop.offering_id,
fop2.offering_id
from facility_offering_price fop
join offering o on fop.offering_id = o.offering_id
inner join
(select
fop1.offering_id,
fop1.price_amount
from facility_offering_price fop1
WHERE fop1.price_type_code = 5
AND fop1.price_status_code = 3
) fop2 on fop.offering_id = fop2.offering_id
WHERE fop.price_start_date = '15-10-28'
AND fop.price_status_code IN (1,2)
/*AND (price_status_code IS NULL)*/
AND fop.price_type_code = 5
/*AND (o.offering_number IS NULL)*/
ORDER BY o.offering_number ASC, fop.price_sequence_number ASC;
It's result set is empty. However, an entry is found if I ask for fop1.price_status_code = 1.
Unable to wrap my head around this one I would appreciate your help.
Try using LEFT JOIN instead. The conversion from SELECT a, subquery AS val FROM ... to a join is more accurately reflected that way. The original query would return rows with NULL val when the subquery has no results; your version ends up omitting such rows completely.

MySQL How to deal with Error: Subquery returns more than 1 row?

I'm trying to write a query where it selects all records from a table where certain complex/nested criteria are met. The logic in my query I think is correct, but the problem I'm running into is the final subquery (see example) is returning more than 1 row, which is what I would expect/need. So, the problem is, how do I deal with this? Does MySQL support some kind of looping or set criterion?
SELECT c.primary_key
FROM esjp_content c
WHERE template_id = (
SELECT DISTINCT esjp_content.template_id
FROM esjp_content
INNER JOIN esjp_hw_config ON esjp_content.template_id = esjp_hw_config.proc_id
INNER JOIN esjp_assets ON esjp_hw_config.primary_key = esjp_assets.hw_config_id
WHERE
esjp_content.summary_id > 0
AND
(esjp_assets.asset_label='C001498500' OR esjp_assets.asset_label='H0065' OR esjp_assets.asset_label='L0009')
)
AND
EXISTS (SELECT 1 FROM esjp_content c2 WHERE c2.summary_id = c.primary_key)
AND
c.primary_key != (
/* This subquery returns more than 1 result. */
SELECT esjp_signoffs.content_id
FROM esjp_signoffs
INNER JOIN esjp_assets ON esjp_signoffs.asset_id = esjp_assets.primary_key
WHERE
esjp_signoffs.user_id=1
AND
(esjp_assets.asset_label='C001498500' OR esjp_assets.asset_label='H0065' OR esjp_assets.asset_label='L0009')
);
For additional details on my tables, see this other StackOverflow post I made earlier today. (This is an entirely different question.)
If i understan correctly the
c.primary_key != (
/* This subquery returns more than 1 result. */
SELECT esjp_signoffs.content_id
FROM esjp_signoffs
INNER JOIN esjp_assets ON esjp_signoffs.asset_id = esjp_assets.primary_key
WHERE
esjp_signoffs.user_id=1
AND
(esjp_assets.asset_label='C001498500' OR esjp_assets.asset_label='H0065' OR esjp_assets.asset_label='L0009')
);
return more then a row .. you could use not in ..
c.primary_key not in (
or
inner join ( ..... ) t on c.primary_key != t.the_column_you_need

SQL Query producing wrong count result

I have the following SQL query
SELECT
DISTINCT
count("SiteTree_Live"."ID")
FROM
"SiteTree_Live"
LEFT JOIN "Page_Live" ON "Page_Live"."ID" = "SiteTree_Live"."ID"
LEFT JOIN "TourPage_Live" ON "TourPage_Live"."ID" = "SiteTree_Live"."ID"
LEFT JOIN "DepartureDate" ON "DepartureDate"."TourID" = "SiteTree_Live"."ID"
WHERE
("SiteTree_Live"."Locale" = 'en_AU')
AND ("SiteTree_Live"."ClassName" IN ('TourPage'))
AND ("DepartureDate"."DepartureDate" LIKE '2012-11%')
but it producing a wrong count as the query result. The total intented result this query is suppose to return should not be more than 245 but currently, its returning more than about "4569" results.
Thats is because of the JOIN on the "DepartureDate" table as the query returns the expected result when i remove the join from the "DepartureDate" table.
What modification do i need to make to my query to count the Macthes between "SiteTree_Live"."ID" and "DepartureDate"."TourID" whiles counting only the "SiteTree_Live"."ID" count excluding the Departure dates?
Any suggestions welcomed :)
THE ANSWER
SELECT
COUNT(DISTINCT SiteTree_Live.ID)
FROM
"SiteTree_Live" LEFT JOIN "Page_Live" ON "Page_Live"."ID" = "SiteTree_Live"."ID"
LEFT JOIN "TourPage_Live" ON "TourPage_Live"."ID" = "SiteTree_Live"."ID"
LEFT JOIN "DepartureDate" ON "DepartureDate"."TourID" = "SiteTree_Live"."ID"
WHERE
("SiteTree_Live"."Locale" = 'en_AU')
AND ("SiteTree_Live"."ClassName" IN ('TourPage'))
AND ("DepartureDate"."DepartureDate" LIKE '2013-03%')
Seems to give me the right result. Thanks for the tip #Michael Berkowski
Minor correction: if DepartureDate is a date-type, then the LIKE '2013-03% will force it to be coerced into a character type (this is a mysql feature) As a result, any indexes on DepartureDate will not be used, IIRC. Better use a plain range-query:
SELECT
COUNT(DISTINCT stl.ID)
FROM
SiteTree_Live stl
LEFT JOIN
DepartureDate dd ON dd.TourID = stl.ID
WHERE
stl.Locale = 'en_AU'
AND stl.ClassName = 'TourPage'
AND dd.DepartureDate >= '2013-03-01'
AND dd.DepartureDate < '2013-04-01'
;
Do this (You have a bunch of unneeded joins)
SELECT
COUNT(DISTINCT SiteTree_Live.ID)
FROM
`SiteTree_Live`
LEFT JOIN
`DepartureDate` ON `DepartureDate`.`TourID` = `SiteTree_Live`.`ID`
WHERE
`SiteTree_Live`.`Locale` = 'en_AU'
AND `SiteTree_Live`.`ClassName` = 'TourPage'
AND `DepartureDate`.`DepartureDate` LIKE '2013-03%'
You could also do a GROUP BY:
SELECT
COUNT(SiteTree_Live.ID)
FROM
`SiteTree_Live`
LEFT JOIN
`DepartureDate` ON `DepartureDate`.`TourID` = `SiteTree_Live`.`ID`
WHERE
`SiteTree_Live`.`Locale` = 'en_AU'
AND `SiteTree_Live`.`ClassName` = 'TourPage'
AND `DepartureDate`.`DepartureDate` LIKE '2013-03%'
GROUP BY
SiteTree_Live.ID

SQL to Linq translation with inner join and 2 left joins (with sub-query)

I have the following query which works fine in SSMS. Im using LinqPad (C#) but really puzzling to succeed with the left outer join in LinqToSql:
SELECT DISTINCT
A.LocID,
V1.PrfValue AS pID,
V2.PrfValue AS sID,
D.DivisionManager,
A.IsApproved,
A.DateCreated
FROM
dbo.Locations AS A
INNER JOIN
dbo.Divisions AS D
ON A.DivisionID = D.DivisionID
LEFT OUTER JOIN
dbo.ValuesInLocations AS V1
ON A.LocID = V1.LocID
AND
V1.PrfID IN (SELECT
PrfID
FROM
dbo.PrfTag
WHERE
(LevelTypeID = 1))
LEFT OUTER JOIN
dbo.ValuesInLocations AS V2
ON A.LocID = V2.LocID
AND
V2.PrfID IN (SELECT
PrfID
FROM
dbo.PrfTag
WHERE
(LevelTypeID = 2))
As you can see, this isn't the most elegant query to begin work, and I agree that the subquery in both left joins could be improved. However, could you please help me with this translation??
Following are 2 possible translations of your query. I uses 3 separate queries in the first translation to make it more readable. I hope you find them useful.
var query1 =
from prfTag in DataContext.PrfTag
where prfTag.LevelTypeID = 1
select PrfID;
var query1 =
from prfTag in DataContext.PrfTag
where prfTag.LevelTypeID = 2
select PrfID;
var query = (
from A in DataContext.Locations
join D in DataContext.Divisions
on A.DivisionID equals D.DivisionID
join V1 in DataContext.ValueInLocations
on A.LocID equals V1.LocID
into VGroup1
from V1 in VGroup1.DefaultIfEmpty()
join V2 in DataContext.ValueInLocations
on A.LocID equals V2.LocID
into VGroup2
from V2 in VGroup2.DefaultIfEmpty()
where (V1 == null || (V1 != null && query1.Contains(V1.PrfID)))
&& (V2 == null || (V2 != null && query2.Contains(V2.PrfID)))
select new
{
A.LocID,
pID = V1 != null ? V1.PrfValue : "",
sID = V2 != null ? V2.PrfValue : "",
D.DivisionManager,
A.IsApproved,
A.DateCreated
}).Distinct();
Here is the second possible translation:
var query = (
from A in DataContext.Locations
join D in DataContext.Divisions
on A.DivisionID equals D.DivisionID
join V1 in DataContext.ValueInLocations
on A.LocID equals V1.LocID
into VGroup1
from V1 in VGroup1.DefaultIfEmpty()
join prfTag1 in DataContext.PrfTag
on V1.PrfID equals prfTag1.PrfID
join V2 in DataContext.ValueInLocations
on A.LocID equals V2.LocID
into VGroup2
from V2 in VGroup2.DefaultIfEmpty()
join prfTag2 in DataContext.PrfTag
on V2.PrfID equals prfTag2.PrfID
select new
{
A.LocID,
pID = V1 != null ? V1.PrfValue : "",
sID = V2 != null ? V2.PrfValue : "",
D.DivisionManager,
A.IsApproved,
A.DateCreated
}).Distinct();
There may be some tricky way to do this in LINQ, but LINQ JOINs are painful for anything other than an inner join. For maintainability, I would discourage the use of linq for this query. I know this doesn't exactly answer your question, but I don't think you're going to get an answer you like that's any better than that query.