The best way to Complex query in linq - c#

The best way to change below in Linq Lamda
Category will be unique across Location to its base location. And I should load the category of immediate location first. If there is no category with that name then should load its immediate parent. Also I have 3 level in location.
Id Name Location
==========================
1 Breakfast India
2 Breakfast TamilNadu
3 Breakfast Chennai
4 Dinner India
5 Dinner TamilNadu
6 Lunch Chennai
7 Combo TamilNadu
(India -1 level, TamilNadu -2 level, Chennai -2 level)
And I need result as
3 Breakfast Chennai
5 Dinner TamilNadu
6 Lunch Chennai
7 Combo TamilNadu
My SQL Query is :
Declare #locationId UNIQUEIDENTIFIER = '7504657E-FEE4-E411-9505-BC305BB6199F'
BEGIN
DECLARE #Locations TABLE(Id UNIQUEIDENTIFIER,LocationLevel int)
Set NoCount On
INSERT INTO #Locations
SELECT AccountId,0 FROM Locations WHERE id = #locationId AND AccountId is not null
UNION
SELECT FranchiseId,1 FROM Locations WHERE id = #locationId AND FranchiseId is not null
UNION
SELECT #locationId,2
Set NoCount Off
SELECT C.*
FROM Categories c
Join #Locations l on c.LocationId = l.Id
WHERE c.IsDeleted = 0
AND (c.LocationId = #locationId OR (c.IsPublished = 1 And c.Status = 1))
AND l.LocationLevel =
(
Select Max(l1.LocationLevel)
From Categories c1
JOIN #Locations l1 on c1.LocationId = l1.Id
WHERE c1.Name=c.Name AND c1.IsDeleted = 0
)
Order by C.Name
END
My current query was :
var categoryes = await CategoryRepository.GetCategoryForContext(ParentLocationIdList.Keys);
Here ParentLocationIdList contains location id and level(1|2|3) for selected location
var result = categoryes.Join(ParentLocationIdList, c => c.LocationId, l => l.Key, (c, l) => new
{
Category = c,
Level = l.Value,
Name = c.Name
});
categoryes = result.GroupBy(g => g.Name).Select(s => s.First(d => d.Level == s.Max(m => m.Level)).Category).ToList();
return categoryes;

Related

When returning the count from SQL, how to also include the value in grouped item for which the count doesn't exist?

Been working on this SQL dilemma for a while now. The part of the table looks like the following.
It's a many-to-many table relationship where one claim can have many notes. So, one example would be the following:
------------------------------------------
| ClaimID | NoteID | Note |
------------------------------------------
| 2387 | 1 | Test 1 |
| 2387 | 2 | Test 2 |
| 2387 | 3 | Test 3 |
| 2532 | 4 | Something 1 |
| 2539 | 5 | abcd |
| 2539 | 6 | jklm |
------------------------------------------
You get the idea.
So, when I run the query I want the result in such a way that it should show me the number of note counts from 1 to 10. If the count exist, then it should show me the count, otherwise 0. An example of what it would look like in the real-world scenario is the following.
[{
"numOfNotes":1,
"count":5916
},{
"numOfNotes":2,
"count":1846
},{
"numOfNotes":3,
"count":639
},{
"numOfNotes":4,
"count":226
},{
"numOfNotes":5,
"count":94
},{
"numOfNotes":6,
"count":50
},{
"numOfNotes":7,
"count":10
},{
"numOfNotes":8,
"count":2
},{
"numOfNotes":9,
"count":2
},{
"numOfNotes":11,
"count":2
}]
That's the query return from the database that I retrieved using C# and linq. Here's the code for that.
if (type == "e" || type == "p")
{
//sub query to retrieve notes
var subquery = from f in db.DBFileInfo
join c in db.Claims on f.FileID equals c.FileID into cl
from gp1 in cl.DefaultIfEmpty()
join n in db.Notes on gp1.ClaimID equals n.ClaimID into nt
from gp2 in nt.DefaultIfEmpty()
where f.ReportDate.Month == month && f.ReportDate.Year == year
group gp2 by gp2.ClaimID into g
select new
{
Key = g.Key,
Count = g.Count()
};
//query to grop by notes count. Notes count is consider contact per claim
var count = (from c in db.Claims
join s in subquery on c.ClaimID equals s.Key
where c.RecordType == type &&
(c.Username != "RxService")
&& (c.HIC3 != "J3A" && c.HIC3 != "J3C" && c.HIC3 != "H7N")
group s by s.Count into g
orderby g.Key
select new
{
NumOfNotes = g.Key,
count = g.Count()
}).Take(10);
}
If you notice in the result, there are numOfNotes from 1 - 11 but 10 is missing. That's because there aren't any claimID that has 10 notes. So, in this case, I still want SQL to return "numOfNotes": 10, "count": 0. And if you notice, I only asked for 10 results (Take(10)), because there can be more than 10 such notes per claim which we are not interested.
And in some cases, there aren't more than 5 notes per claimID for the given time period. In one instance, the result from SQL only goes up to 6. But I still want the result upto 10 whether it exists or not. Is it possible?
In case if you're interested: Here's my SQL statement
SELECT
count(C.ClaimID) as count, N.NotesPerClaim
FROM
ClaimsTable C
INNER JOIN
(SELECT
claimid, count(note) as NotesPerClaim
FROM
NotesTable
GROUP BY
ClaimID) as N ON N.ClaimID = C.ClaimID
WHERE
RecordType = 'e' AND
(Username <> 'RxService') AND
(HIC3 <> 'J3A' AND HIC3 <> 'J3C' AND HIC3 <> 'H7N')
GROUP BY
N.NotesPerClaim
ORDER BY
N.NotesPerClaim;
You seem to want a numbers table. Here is one method:
WITH nums as (
SELECT 1 as n
UNION ALL
SELECT n + 1
FROM nums
WHERE n < 10
),
t as (
SELECT count(C.ClaimID) as NumClaims, N.NotesPerClaim
FROM ClaimsTable C JOIN
(SELECT claimid, count(note) as NotesPerClaim
FROM NotesTable
GROUP BY ClaimID
) N
ON N.ClaimID = C.ClaimID
WHERE c.RecordType = 'e' AND
c.Username <> 'RxService' AND
c.HIC3 NOT IN ('J3A', 'J3C', 'H7N')
GROUP BY N.NotesPerClaim
)
SELECT nums.n as NotesPerClaim, t.NumClaims
FROM nums LEFT JOIN
t
ON nums.n = t.NotesPerClaim
ORDER BY NotesPerClaim;
Use LEFT JOIN instead of INNER JOIN
Also is better use HIC3 NOT IN ('J3A', 'J3C', 'H7N')
SELECT count(C.ClaimID) AS count
, N.NotesPerClaim
FROM ClaimsTable C
LEFT JOIN (
SELECT claimid
, count(note) AS NotesPerClaim
FROM NotesTable
GROUP BY ClaimID
) AS N ON N.ClaimID = C.ClaimID
WHERE RecordType = 'e'
AND (Username <> 'RxService')
AND HIC3 NOT IN ('J3A', 'J3C', 'H7N')
GROUP BY N.NotesPerClaim
ORDER BY N.NotesPerClaim;
LEFT JOIN on the notes table, and check for ISNULL condition. In the case of NULL from notes, return 0.
Here is a SQL Fiddle demo of the concept.
Your code should be:
`SELECT count(C.ClaimID) as count, ISNULL(N.NotesPerClaim,0) as NotesPerClaim
FROM ClaimsTable C
LEFT OUTER JOIN
(SELECT claimid, count(note) as NotesPerClaim from NotesTable
GROUP BY ClaimID) as N
ON N.ClaimID = C.ClaimID
WHERE RecordType = 'e' AND
(Username <> 'RxService') AND (HIC3 NOT IN ('J3A','J3C','H7N'))
GROUP BY ISNULL(N.NotesPerClaim,0)
ORDER BY N.NotesPerClaim;`

Not a simple Max

I have the following data:
Id | Value | OtherStuff
---------------------------
6 | 6 | 1
---------------------------
5 | 4 | 2
---------------------------
5 | 2 | 3
The desired result:
Id | Value | OtherStuff
---------------------------
6 | 6 | 1
---------------------------
5 | 4 | 2
That is I need the Max Value for each of the Id's.
I'm a bit stumped of how to do this without breaking it into multiple queries, can it be done, and if so how?
Update: I think I oversimplified the issue:
var query = from st in StockStakes
join o in Organisations on j.OrganisationId equals o.OrganisationId into oGroup
from o in oGroup.DefaultIfEmpty()
where st.Stock.Status == "A"
select new
{
Id = st.Id,
Value = st.Value,
CustomerId = o.OrganisationId
};
The data sample from above still stands... now how do i structure the query to give me the Max Value alongside each Id?
var query = from x in data
group x by x.Id into x
select x.OrderByDescending(y => y.Value).FirstOrDefault()
Based on you updated query, similar approach to the first query, but since you have multiple tables you need to group all the tables into an anonymous object and then select only the columns you want
var query = from st in StockStakes
join o in Organisations on j.OrganisationId equals o.OrganisationId into oGroup
from o in oGroup.DefaultIfEmpty()
where st.Stock.Status == "A"
group new { st, o } by st.Id into g
let largestValue = g.OrderByDescending(x => x.Value).FirstOrDefault()
select new
{
Id = g.Key,
Value = largestValue.st.Value,
CustomerId = largestValue.o.OrganisationId
};
I'm not really sure about what you mean, but maybe you can try with this query.
select Id, max(Value)
from your_table
group by Id;
This gives you the max "Value column" value for each "Id column" value.
-- EDIT --
LINQ version:
var q = from t in dc.YourTable
group t by t.Id
into g
select new
{
Id = g.Id,
Value = (from t2 in g select t2.Value).Max()
};
Code not tested. I'm on the bus now... :-) Give it a try!

LINQ left join ordered with empty right-items at the end

My understanding of LINQ and Entity Framework is minimal, and I'm learning as I go along...
I am trying to write a query that takes the information from a view called GroupView and does a left-join on a table called GroupSequence... the information is then to be used by an <asp:Repeater>.
The resultant set should have all the items from GroupView with the joined items at the start (in sequence defined by the GroupSequence table) and with non-joined items at the end (in sequence defined by the Id of the GroupView items).
I.e...
[GroupView] | [GroupSequence]
[Id] [Name] [Calc] | [Id] [GroupId] [UserId] [Sequence]
1 Group 1 23 | 1 1 1 3
2 Group 2 34 | 2 2 1 2
3 Group 3 45 | 3 3 1 1
4 Group 4 56
5 Group 5 67
With the expected outcome of...
[Id] [Name] [Calc]
3 Group 3 45
2 Group 2 34
1 Group 1 23
4 Group 4 56
5 Group 5 67
If I do the following, despite using the DefaultIfEmpty, all I get is the 3 groups linked to the sequence. But the page displays, even though it's only 3 rows...
from #group in context.GroupViews
join seq in context.GroupSequences on #group.Id equals seq.GroupId into groupSeq
from item in groupSeq.DefaultIfEmpty()
where item.UserId == 1
orderby item.Sequence
select new { Id = #group.Id, Name = #group.Name, Calc = #group.Calc };
If I do the following, the .DataBind on the repeater complains that...
The entity or complex type 'DevModel.GroupSequence' cannot be constructed in a LINQ to Entities query
from #group in context.GroupViews
join seq in context.GroupSequences on #group.Id equals seq.GroupId into groupSeq
from item in groupSeq.DefaultIfEmpty(new GroupSequence { Id = #group.Id, UserId = 1, Sequence = #group.Id + 1000 })
where item.UserId == 1
orderby item.Sequence
select new { Id = #group.Id, Name = #group.Name, Calc = #group.Calc };
Based on this question and accepted answer I have also tried using a DTO like this...
class GroupViewSequenceDTO
{
public int Id { get; set; }
public string Name { get; set; }
public int? Calc { get; set; }
}
from #group in context.GroupViews
join seq in context.GroupSequences on #group.Id equals seq.GroupId into groupSeq
from item in groupSeq.DefaultIfEmpty(new GroupSequence { Id = #group.Id, UserId = 1, Sequence = #group.Id + 1000 })
where item.UserId == 1
orderby item.Sequence
select new GroupViewSequenceDTO { Id = #group.Id, Name = #group.Name, Calc = #group.Calc };
But I still get the same error as before (cannot be constructed)
The question...
How can I write this query so that the repeater will show all 5 rows, with the first 3 in sequence order, and the last 2 added on? What am I doing wrong?
You need to move the filter into the left join condition since it will be false when item is null.
from #group in context.GroupViews
from seq in context.GroupSequences.Where(x => #group.Id == x.GroupId && x.UserId == 1).DefaultIfEmpty()
orderby seq.Sequence ?? int.MaxValue
select new GroupViewSequenceDTO
{
Id = #group.Id,
Name = #group.Name,
Calc = #group.Calc
};

Replacing Id with Actual values in SQL Server

I have the following table structure also I have mention my expected output please help me with query as I don't know much about SQL query
Query :
SELECT * FROM(
SELECT ESIDispensary,ESILocation,test,Category, COUNT(*) AS [Total Count]
FROM
(SELECT category,ESILOCATION,ESIDISPENSARY,TEST
FROM(SELECT id,CompanyId,FName,Code,category,ESILOCATION,ESIDISPENSARY
FROM dbo.[EmployeeDetail] e WHERE e.CompanyId = 1 AND Category in (1,2)) a
LEFT JOIN
(SELECT *
from
(SELECT EmployeeId, CustomeFieldName,FieldValue
FROM dbo.[CustomeFieldDetail] C
JOIN dbo.[EmployeeDetail] e ON e.id = c.employeeid AND e.CompanyId = c.companyid
WHERE e.CompanyId = 1 AND Category IN (1,2)) SRC
PIVOT
(MAX(FieldValue) FOR CustomeFieldName IN([TEST]))
piv)
b ON a.Id = b.EmployeeId
) AS a
GROUP BY ESIDispensary ,ESILocation,test,Category) x
Table generated using above query
ESIDispensary ESILocation test Category Count
12 11 NULL 1 NULL
12 13 30 1 1
14 13 29 2 2
Table 1 : ESI
Id CompanyId FieldName ComboValue
11 1 ESILOCATION mumbai
12 1 ESIDISPENSARY mumbai
13 1 ESILOCATION pune
14 1 ESIDISPENSARY pune
29 1 TEST HDFC
30 1 TEST ICICI
Table 2 : Category
id CategoryName
1 staff
2 manager
Problem is i want to replace IDs with respected values also can i change above query to get expected result
Expected Summary Output :
ESIDispensary ESILocation test staff manager
mumbai mumbai NULL 1 NULL
mumbai pune ICICI 1 1
pune pune HDFC NULL 2
Is this is what you want:
SELECT (SELECT ComboValue FROM ESI WHERE ID = ESIDispensary) AS ESIDispensary,
SELECT ComboValue FROM ESI WHERE ID = ESILocation) AS ESILocation,
SELECT ComboValue FROM ESI WHERE ID = test) AS test,
Category, [Total Count]
FROM
(
SELECT ESIDispensary,ESILocation,test,Category, COUNT(*) AS [Total Count]
FROM
(SELECT category,ESILOCATION,ESIDISPENSARY,TEST
FROM(SELECT id,CompanyId,FName,Code,category,ESILOCATION,ESIDISPENSARY
FROM dbo.[EmployeeDetail] e WHERE e.CompanyId = 1 AND Category in (1,2)) a
LEFT JOIN
(SELECT *
from
(SELECT EmployeeId, CustomeFieldName,FieldValue
FROM dbo.[CustomeFieldDetail] C
JOIN dbo.[EmployeeDetail] e ON e.id = c.employeeid AND e.CompanyId = c.companyid
WHERE e.CompanyId = 1 AND Category IN (1,2)) SRC
PIVOT
(MAX(FieldValue) FOR CustomeFieldName IN([TEST]))
piv)
b ON a.Id = b.EmployeeId
) AS a
GROUP BY ESIDispensary, ESILocation, test, Category) x

Query to return pairs of related rows, but only when both rows exist

I have the following tables:
*sistema_documentos*
[id], [caminho], [idDocType](FK -> sistema_DocType.id)
*sistema_Indexacao*
[id] ,[idDocumento](FK -> sistema_documentos.id) ,[idIndice](FK ->
sistema_Indexes) ,[valor]
*sistema_DocType*
[id], [tipoNome](FK -> sistema_DocType.id)
*sistema_DocType_Index*
[id],[idName],[mask],[idTipo](FK -> sistema_DocType.id),[tamanho]
From this query:
select distinct a.id, b.idIndice, b.valor from tgpwebged.dbo.sistema_Documentos as a
join tgpwebged.dbo.sistema_Indexacao as b on a.id = b.idDocumento
join tgpwebged.dbo.sistema_DocType as c on a.idDocType = c.id
join tgpwebged.dbo.sistema_DocType_Index as d on c.id = d.docTypeId
where d.docTypeId = 40
and (b.idIndice = 11 AND b.valor = '11111111' OR b.idIndice = 12 AND b.valor = '22222' )
I get the following result
id idIndice valor
13 11 11111111
13 12 22222
14 11 11111111
14 12 22222
16 12 22222
As you can see, I want all ids with idIndice 11 with value 11111111 and 12 with value 22222
Id 16 has id 12 with value 22222 authough it does not have id 11 with value 11111111 so I donĀ“t want it to be shown.
How can I update my query to obtain the result I want. Hope my question is clear. If it is not just ask and I edit my post. Thanks
I would suggest something like this:
WITH TempTable AS
(
select distinct a.id, b.idIndice, b.valor
from tgpwebged.dbo.sistema_Documentos as a
join tgpwebged.dbo.sistema_Indexacao as b on a.id = b.idDocumento
join tgpwebged.dbo.sistema_DocType as c on a.idDocType = c.id
join tgpwebged.dbo.sistema_DocType_Index as d on c.id = d.docTypeId
where d.docTypeId = 40
and (b.idIndice = 11 AND b.valor = '11111111' OR b.idIndice = 12 AND b.valor = '22222' )
)
SELECT *
FROM TempTable t1
WHERE (select count(*)
from TempTable t2
where t1.id = t2.id AND t1.valor != t2.valor) = 1
So... get all the results from your first query where there is at least one result from the table that matches on id, but does not match on valor. (This assumes you could have duplicate rows with the same valor, but you wouldn't want that.)
Try something like this. I took out the tables that didn't have direct bearing on the query, although I named them similarly, and I created a simple schema to replicate the problem. I hope this is clear, and that the connection back to your original query is likewise clear.
CREATE TABLE Documentos (ID INT, document varchar(12))
create table Indexacao (AID INT, indice int, valor varchar(12))
insert Documentos(id, document)
values (1, 'test1'),
(2, 'test2'),
(3, 'test3')
insert Indexacao (aid, indice, valor)
values (1, 11, '11111111'),
(1, 12, '22222'),
(2, 12, '22222')
The important part of the code is the INTERSECT - it returns only rows that are in both sets. In my experience this operator is usually more efficient than anything containing an OR. In the query below, we are getting only those Indexacao rows whose idDocumentos are in the INTERSECT of the two sets of conditions.
SELECT Ind.*
FROM Indexacao Ind
JOIN (
SELECT D.ID
FROM Documentos D
JOIN Indexacao I
ON D.ID = I.AID
WHERE I.Indice = 11 AND I.valor = '11111111'
INTERSECT
SELECT D.ID
FROM Documentos D
JOIN Indexacao I
ON D.ID = I.AID
WHERE I.Indice = 12 AND I.valor = '22222'
)Doc (ID)
ON Doc.ID = Ind.AID
This assumes that you don't have duplicate Indice, Valor rows for a single idDocumento - if you do, you will need to add a DISTINCT.

Categories

Resources