Not a simple Max - c#

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!

Related

MS SQL Querying Tables Where Data may or may not exist

Have not come across an exact use-case for what I'm trying to achieve on Stackoverflow so will explain it in the hopes someone can assist.
I have two tables, one contains a master activity list and the other contains who completed those activities.
Table A is the Activity Table. This is a distinct list of things that can be done.
ID | Activity
---------------------------
1 | Change Oil
2 | Change Airfilter
3 | Change Brake Fluid
Table B is the Activity Log table. This tracks where people have done one of the above Activities. ActivityID links to ID on Table A.
ID | ActivityID | CompletedBy
---------------------------------------
1 | 1 | john#auto.com
2 | 1 | sally#auto.com
3 | 3 | john#auto.com
What I am trying to produce is a list of all activities, but then only for a distinct person. I have tried multiple ways for this, but can only get the query to show where values exist in both tables.
My preferred output would be the following, where in the query i have asked to show me the full Activity list and also to show where John has completed anything. If there is no record in Table B for this activity, to show a blank value.
ID | Activity | CompletedBy
-------------------------------------------
1 | Change Oil | john#auto.com
2 | Change Airfilter |
3 | Change Brake Fluid | john#auto.com
Here is my current SQL Query in my attempt to work this one out, which right now the results only return ID 1 and 3 from that example above, where john actually has a record in Table B.
Select a.ID, a.Activity, b.CompletedBy
FROM ActivityList a
LEFT OUTER JOIN ActivityLog b
ON a.ID = b.ActivityID
Where CompletedBy = 'john#auto.com'
GROUP BY a.ID, a.Activity, b.CompletedBy
Any help or pointers in the right direction would be greatly appreciated.
If you move the CompletedBy restriction into the join conditions you would not need the group by.
Select a.ID, a.Activity, b.CompletedBy
FROM ActivityList a
LEFT OUTER JOIN ActivityLog b
ON a.ID = b.ActivityID and b.CompletedBy = 'john#auto.com'
Change your where clause to Where CompletedBy = 'john#auto.com' or CompletedBy is null so it matches on a text match or the case where the name does not exist at all. You can also use isnull( to convert the null value to a empty string if that is the behavior you are wanting.
Select a.ID, a.Activity, isnull(b.CompletedBy, '') as CompletedBy
FROM ActivityList a
LEFT OUTER JOIN ActivityLog b
ON a.ID = b.ActivityID
Where CompletedBy = 'john#auto.com' or b.CompletedBy is null
GROUP BY a.ID, a.Activity, b.CompletedBy
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataTable table_A = new DataTable();
table_A.Columns.Add("ID", typeof(int));
table_A.Columns.Add("Activity", typeof(string));
table_A.Rows.Add(new object[] { 1, "Change Oil"});
table_A.Rows.Add(new object[] { 2, "Change Airfilter"});
table_A.Rows.Add(new object[] { 3, "Change Brake Fluid"});
DataTable table_B = new DataTable();
table_B.Columns.Add("ID", typeof(int));
table_B.Columns.Add("ActivityID", typeof(string));
table_B.Columns.Add("CompletedBy", typeof(string));
table_B.Rows.Add(new object[] { 1, 1, "john#auto.com"});
table_B.Rows.Add(new object[] { 2, 1, "sally#auto.com"});
table_B.Rows.Add(new object[] { 3, 3, "john#auto.com"});
var groups = (from a in table_A.AsEnumerable()
join b in table_B.AsEnumerable() on a.Field<int>("ID") equals b.Field<int>("ID")
select new { a = a, b = b})
.GroupBy(x => x.b.Field<string>("CompletedBy"))
.ToList();
}
}
}
----------
Select a.ID, a.Activity, b.CompletedBy
FROM ActivityList a
Right OUTER JOIN ActivityLog b
ON a.ID = b.ActivityID
Where CompletedBy = 'john#auto.com'
GROUP BY a.ID, a.Activity, b.CompletedBy
Your query is correct just change the potion of condition.
Select a.ID, a.Activity, b.CompletedBy
FROM ActivityList a
LEFT JOIN ActivityLog b
ON a.ID = b.ActivityID AND b.CompletedBy = 'john#auto.com'
GROUP BY a.ID, a.Activity, b.CompletedBy

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;`

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
};

Check and compare column values in sql server table

I have this table (Prefrences_Table)
--------------------------
|student | Preferences |
--------------------------
Stud A | Stud B
Stud A | Stud C
Stud B | Stud E
Stud B | Stud A
Stud C | Stud F
Stud F | Stud B
--------------------------
If "Stud A" has added "Stud B" in his Preferences list, i would like to check if "stud B" has also added "stud A" in his preference, so i can add both of them in one group.
How can this be done using SQL or C#?
A self-join should work just fine here. The additional predicate returns only the first instance of the match to avoid duplicates.
select t.student, t1.student
from
Prefrences_Table t
inner join Prefrences_Table t1
on t.student = t1.preferences
and t.preferences = t1.student
and t.student < t1.student
this might give you anwer to your question, field mutual will be one if both students added the other in preferences, zero otherwise
SELECT T1.student, T2.Preferences,
(SELECT COUNT(*) FROM Prefrences_Table T2 WHERE T2.Preferences = T1.student AND T2.student = T1.Preferences) AS mutual
FROM Prefrences_Table T1
Another alternative would be the following:
SELECT * FROM
(
SELECT PM.student, PM.Preferences,
(SELECT COUNT(student) FROM Prefrences_Table AS PI WHERE PI.Preferences = PM.student
AND PI.student = PM.Preferences) AS CheckCross
FROM Prefrences_Table AS PM
) AS PD
WHERE PD.CheckCross > 0
You have some SQL answers, here is one in c#/linq.
var list = new List<Prefrences_Table>();
var results = (from t in list
join t1 in list on t.student equals t1.preferences
where
t.student == t1.preferences &&
t.preferences == t1.student &&
string.CompareOrdinal(t.student, t1.student) < 0
select new {t.student, t1.student}
);

how to get only those which have maximum value in a specified column in asp.net c#

I have a table with "customer_id, date, installment_no, amount" columns. I want to get the information of last installment of each customer_id till today. here installment_no is int type and when a new installment is deposited, the installment_no is increased by 1 in new entry. My table look like:
CS1001 | 12-06-2013 | 1 | 2500
CS1002 | 19-06-2013 | 1 | 1600
CS1001 | 14-07-2013 | 2 | 2500
I want to get a sqlcommand statement for do so.
Group all records by customer_id, then order all customer's records by installment_no, and select only record with max installment_no from each group:
from c in customers
group c by c.customer_id into g
select g.OrderByDescending(x => x.installment_no).First()
Same with pure SQL if you don't use Linq
SELECT c.* FROM Customers c
INNER JOIN (
SELECT customer_id, MAX(installment_no) max_installment
FROM Customers
GROUP BY customer_id
) cmax
ON c.customer_id = cmax.customer_id
AND c.installment_no = cmax.max_installment
var result = list.GroupBy(x=>x.customer_id)
.Select(g=>g.OrderByDescending(y=>y.installment_no).First())
.ToList();

Categories

Resources