GROUP BY, ORDER BY and taking first in LINQ - c#

Suppose I have a table with two columns:
TABLE A
-------
ProjectID NUMBER
STATUS VARCHAR2(6) // either 'CLOSED' or 'NEW'
There could be maximum two entries for a ProjectID with the two possible values of STATUS and the combination (ProjectID, STATUS) is unique.
I need to select only those ProjectID's that have status 'NEW'. Also, if for a projectID, there are two entries with different statuses (NEW and CLOSED), I don't want it in the output.
I tried using group by, then ordering the resultset descending (so as to get 'NEW' row for a project ID first) and then taking the first row in LINQ, similar to this:
var query = (from a in context.A.Where(o => o.STATUS == 'NEW')
group a by a.ProjectID into groups
select groups.OrderByDescending(o => o.ProjectID)
.ThenBy(o => o.STATUS)
.FirstOrDefault());
Butt it's resulting into an "APPLY" clouse in the query which is resulting into an error. Apparantly, Oracle 10g doesn't support it.
Any help is appreciated.

Something like this, perhaps?
SQL> with test (projectid, status) as
2 (select 1, 'new' from dual union -- should be returned
3 select 2, 'new' from dual union
4 select 2, 'closed' from dual union
5 select 3, 'closed' from dual union
6 select 4, 'new' from dual -- should be returned
7 )
8 select projectid
9 from test
10 group by projectid
11 having min(status) = max(status)
12 and min(status) = 'new';
PROJECTID
----------
1
4
SQL>

Proper tu use having count(distinct STATUS=1) :
create table tableA( ProjectID int, STATUS varchar2(10) );
insert all
into tableA values(1 ,'NEW')
into tableA values(1 ,'CHANGED')
into tableA values(2 ,'NEW')
into tableA values(3 ,'CHANGED')
select * from dual;
/
select * from
(
select ProjectID, max(STATUS) STATUS
from tableA
group by ProjectID
having count(distinct STATUS)=1
)
where STATUS = 'NEW';

I believe I have accomplished what you want, using a subquery in LINQ.
var query = (from a in context.A
where (from b in context.A
where b.ProjectID == a.ProjectID
select new { a.ProjectID, a.STATUS }).Distinct().Count() == 0
&& a.STATUS == "NEW"
select a.ProjectID).ToList();
Essentially, the outer query just makes sure that each a record has a NEW status, and the inner query makes sure that there are no two distinct records with the given ProjectID, because if there are, one is CLOSED. I avoided using a GROUP BY since you said your database does not support LINQ's way of doing it.
I hope I understood your problem correctly, and I hope this helps!

Related

How can I return data from my table grouped but still in order of title?

I've got a simple table in a SQL Server 2012 database and I'm querying the table using EntityFramework 6 in ASP.Net 4.51. The table has the following structure and data...
FileID GroupID Title DateAdded
------------------------------------------------------
1 1 Charlie rev 1 21/05/2016
2 2 Beta rev 1 22/05/2016
3 1 Charlie rev 2 23/05/2016
4 2 Beta rev 2 24/05/2016
5 3 Alpha rev 1 25/05/2016
Basically the table represents files and revisions of files uploaded by the user, so when they view the data I'm wanting to show the first file of a group, then all older revisions in descending date order below. Ordering by GroupID and DateAdded Descending, I can get the following...
FileID GroupID Title DateAdded
--------------------------------------------------------
3 1 Charlie rev 2 23/05/2016
1 1 Charlie rev 1 21/05/2016
4 2 Beta rev 2 24/06/2016
2 2 Beta rev 1 22/05/2016
5 3 Alpha rev 1 25/05/2016
While this is close to what I'm after, I'd rather have the titles in alphabetical order first, then all the revisions (by group) in descending date order.
I'm looking for this output:
FileID GroupID Title DateAdded
-----------------------------------------------------
5 3 Alpha rev 1 25/05/2016
4 2 Beta rev 2 24/05/2016
2 2 Beta rev 1 22/05/2016
3 1 Charlie rev 2 23/05/2016
1 1 Charlie rev 1 21/05/2016
I can achieve this with two tables, but I'm ideally looking for a solution using the table I currently have.
Can anyone help with a Linq statement that will produce this output?
In short, I think what i'm asking for is to sort the table by the first most recent Title of each group (by descending date order) then by the remaining items of each group by descending date order.
Thanks in advance for your help,
Edit: to satisfy posters who want 'shown effort', I know that the following Linq statement will work to produce the first result.
var result = context.MyTable.OrderBy(x => x.GroupID)
.ThenByDescending(x => x.DateAdded);
As for the second result... I wouldn't be posting if I knew how to achieve it. I'm not new to SQL but I am new to this particular problem. It isn't homework and I've spent a number of hours trying to figure it out. As stated, I already have this working using two tables but it should be achievable with one.
In short, I think what i'm asking for is to sort the table by the first most recent Title of each group (by descending date order) then by the remaining items of each group by descending date order.
There are several way you can accomplish this in LINQ to Objects. However LINQ to Entities supports limited number of constructs, so I would suggest you using a direct translation of the above explanation:
var result = context.MyTable
.OrderBy(t => context.MyTable
.Where(t1 => t1.GroupId == t.GroupId)
.OrderByDescending(t1 => t1.DateAdded)
.Select(t1 => t1.Title)
.FirstOrDefault())
.ThenByDescending(t => t.DateAdded);
Using plain old SQL this could be a starting point:
declare #t table (FileId int, GroupId int, Title varchar(50), DateAdded datetime)
insert into #t
select 1, 1, 'Charlie rev 1', '2016-05-21'
union select 2, 2, 'Beta rev 1', '2016-05-22'
union select 3, 1, 'Charlie rev 2', '2016-05-23'
union select 4, 2, 'Beta rev 2', '2016-05-24'
union select 5, 3, 'Alpha rev 1', '2016-05-25'
select t.*
from #t t
join (
select GroupId, min(Title) as mtitle from #T group by GroupId
) subt on t.GroupId = subt.GroupId
order by subt.mtitle, t.GroupId, t.DateAdded desc
But I can't off the top of my head write the equivalent Linq.
The plain old SQL sample works by picking one Title value (in this case the MINvalue) from a group and uses that as a representative for the whole group for sorting.
If your logic always compose Title as FileName + "rev" + revision.Version then you can just order values by that value instead of GroupId.
But you'll need to use AsEnumerable to switch to LINQ to Objects before OrderBy. This will cause that your query will execute without ordering and returns data to the client and then will order on client side.
Next query will return data as in the last table:
var result = context
.MyTable
.AsEnumerable()
.OrderBy(x => x.GetFileName())
.ThenByDescending(x => x.DateAdded);
However, this will not give the expected word if the input string only has one word, so a special case is needed. But if you have always use pattern above then this is enough for your reasons.
If you need to have IQueryable result then you can implement a stored procedure to retrieve substring from Title field:
var result = context
.MyTable
.AsEnumerable()
.OrderBy(x => x.Title.Substring(0, s.IndexOf(" "))
.ThenByDescending(x => x.DateAdded);
I gave it a try and here is a solution I stubled across. However, due to lack of time I didn't test it for mor than the provided test data. Feel free to comment.
DECLARE #t TABLE (
FileID int
,GroupID int
,Title nvarchar(50)
,DateAdded date
);
INSERT INTO #t VALUES(1, 1, 'Carlie rev 1', '2016-05-21'), (2, 2, 'Beta rev 1', '2016-05-22'),
(3, 1, 'Carlie rev 2', '2016-05-23'), (4, 2, 'Beta rev 2', '2016-05-24'),
(5, 3, 'Alpha rev 1', '2016-05-25');
WITH cte1 AS(
SELECT *, ROW_NUMBER() OVER (ORDER BY title ASC) AS rn0, ROW_NUMBER() OVER (ORDER BY DateAdded DESC) AS rn1
FROM #t
),
cte2 AS(
SELECT *, rn0*rn1 AS rn3
FROM cte1
)
SELECT FileID, GroupID, Title, DateAdded FROM cte2
ORDER BY rn3

How to retrieve all columns from table1 and matching columns from table2(Left outer join) using Linq

I have to retrieve all the columns from table1 and matching columns from table2. I have a stored procedure as :
alter Procedure [dbo].[usp_Property]
#UserId bigint =null
As
Begin
select P.PID, P.PropertyName, P.SBUArea, P.ListedOn,
P.Availability, P.Price, F.UserID, F.PID as FavProjId
from dbo.Property P left outer join dbo.Favorite F
on (F.PID=P.PID And F.UserID=#UserId)
I want to get Linq query for the same. So far I tried with something like
//User Id comes from session..
//var userId
var result=(from p in Properties
join f in Favorites
on p.PID equals f.PID into r
from r1 in r.DefaultIfEmpty()
where r1.UserID==userId
select new
{
p.PID,
p.PropertyName,
p.SBUArea, p.ListedOn,
r1.UserId
});
Can anyone please correct me. I want to use left outer join or any other alternate thing here.
If I beautify your SP's code, I get this:
DECLARE #UserId int
SET #UserId = 12435
SELECT
P.PID
,P.PropertyName
,P.SBUArea
,P.ListedOn
,P.Availability
,P.Price
,F.UserID
,F.PID AS FavProjId
FROM Property AS P
LEFT JOIN Favorite AS F
ON (F.PID=P.PID AND F.UserID = #UserId)
Now I wonder if you need that UserId in the WHERE clause of the SQL, or really in the join.
But anyway, here the LINQ-equivalent of exactly that SQL:
System.Int64 __UserId = 12435;
var query = (
from P in Repo.Property
from F in Repo.Favorite
.Where(fav=> fav.PID == P.PID && fav.UserID == __UserId)
.DefaultIfEmpty() // <== makes join left join
select new
{
PID = P.PID
,PropertyName = P.PropertyName
,SBUArea = P.SBUArea
,ListenOn = P.ListedOn
,Availabiity = P.Availability
,Price = P.Price
,UserId = F.UserID
,FavProjId = F.PID
}
);
var data = (query).ToList();
Use anonymous objects in your selection
var result = from t in table1
join x in table2
on t.id equals x.id
select new { id = t.id, col1 = t.col1, col2 = x.col2 }
If you will put the where clause after join you may get null reference exception because DefaultIfEmpty returns default value for non matching rows. You can filter the records before joining itself like this:-
var result=(from p in Properties
join f in Favorites.Where(x => x.UserID == userId)
on p.PID equals f.PID into r
from r1 in r.DefaultIfEmpty()
select new
{
p.PID,
p.PropertyName,
p.SBUArea,
p.ListedOn,
r1.UserId
});
Please note you need to access properties of Favorites using r1.
Update:
As far as I have understood you need all records from Property table and only matching rows from Favorite table. But you have a filter on your Favorite table so the ultimate data source will differ. Let me make my point clear by this example:-
Suppose you have following data in Property table:-
PID PropertyName Availability Price
1 aaa true 20
2 bbb false 10
3 ccc true 50
4 ddd false 80
5 eee true 55
6 fff false 70
and Favorite table like this:-
FID PID UserId
1 4 1001
2 2 1005
3 5 1007
And let's say you want all records for UserId 1005, then the result should contain all the property Id's from 1 till 6 even if UserId 1005 doesn't match for property Id's 4 & 2 right? So the query above is as per this understanding. Check this Fiddle with same example and output.

SQL query distinct issue

I have written the below query to list all the rows in table [HRSurvey]
which empid is only present in empsurveyselection. Both table have empid columns.
But i am not able to get the proper results because there
might be multiple empid for same surveyid in empsurveyselection table.
But this query works fine when there is only one empid for each surveyid in empsurveyselection table.
Could you please rewrite the query to list the all the rows in the table HRSurvey where the empid are there in empsurveyselection ?
SELECT hrs.*
FROM [HRSurvey] hrs
Left Join [HRSurveyEmployee] hse
ON hse.EmpID = hrs.EmpID
LEFT Join Surveys s
ON s.surveyid = hse.surveyid
WHERE hrs.empid IN (
SELECT empid FROM [HRSurveyEmployee] where surveyid = s.surveyid
) and sempid
in ( select DISTINCT empid FROM empsurveyselection WHERE deptid=9 and surveyid = s.surveyid)
You can write as:
SELECT hrs.col1, -- Worst Practice to use * in production code to pull data
hrs.col12 -- Use explicit column names instead
FROM [HRSurvey] hrs
INNER JOIN [HRSurveyEmployee] hse ON hse.EmpID = hrs.EmpID
INNER JOIN [Surveys] s ON s.surveyid = hse.surveyid
INNER JOIN [empsurveyselection]ess ON ess.deptid=9
AND ess.surveyid = s.surveyid AND hrs.sempid = ess.empid
Please try with the below query....
select a.*
from HRSurvey a
where empid exists (select 1
from empsurveyselection b
where a.Empid = b.Empid)
Thanks for the help! Actually issue was not with the query. Issue was with the data and i fixed it.

c# linq to entities using method based queries - trying to select where the object appears only once

i have got this table that relates the Table hardware with a table Process..
this table is called processHardware.
this table is discribed by:
IDProcessHardware
IDProcess
IDHardware
State
the field state can have 3 states (1-Insert, 2-Remove,3-Substitute)..
so i can i have this:
IDProcessoHardware IDProcesso IDHardware State
1 10 1 1
2 10 2 1
3 10 1 2
what this tell me is that the hardware with id 1 was insert on the process with the id 10
then the user insert the hardware with id 2 on the process with the id 10, and the it remove the hardware with the id 1 from the process with the id 10
by giving the id of the process i want to get the id of the hardware that were insert, this is, the id of the hardware that were remove..
so in this case the record that i will get is record number 2..because was insert, but was not removed..
after getting the ids from this table i need to relate the ids with the table hardware, this table is described by idhardware, serial number, description..
i was using linq method base..
and this was something that i did, but didnt go further after this..
var ProcessoHardware = from procHardware in db.ProcessoHardwares
where procHardware.Rem == 0 && procHardware.IDProcesso == IDProcesso
group procHardware by procHardware.IDHardware into g
select new { IDHardware = g.Key, count = g.Count() };
the query above didnt work for me...
so i want to get the records that appears only once on the table, and then relate the ids that were obtained from this query and get the info about those ids like, serial number, description(these fields are on a table called Hardware).
thanks in advance..
in sql i manage to do the query ..
SELECT *
FROM
(SELECT IDHardware ,COUNT(IDHardware) nu
FROM dbo.ProcessoHardware
WHERE IDProcesso=47
Group By IDHardware) T WHERE nu=1
how do i pass this to linq?
Firstly your SQL statement would be clearer if you used the having clause so it becomes
SELECT IDHardware, COUNT(IDHardware) nu
FROM dbo.ProcessoHardware
WHERE IDProcesso=47
GROUP BY IDHardware
HAVING COUNT(IDHardware) = 1
secondly, your SQL statement doesn't mention a field called Rem, but your LINQ states where procHardware.Rem == 0. I'm going to assume that you need to keep that filter. If so then all you need to do is add a where clause to count your group, g. Try the following
var ProcessoHardware = from procHardware in db.ProcessoHardwares
where procHardware.Rem == 0 && procHardware.IDProcesso == IDProcesso
group procHardware by procHardware.IDHardware into g
where g.Count() == 1
select new { IDHardware = g.Key, count = g.Count() };
although the literal transformation of your statement (without the Rem and hard coded ID of 47) to LINQ would be
var ProcessoHardware = from procHardware in db.ProcessoHardwares
where procHardware.IDProcesso == 47
group procHardware by procHardware.IDHardware into g
where g.Count() == 1
select new { IDHardware = g.Key, count = g.Count() };

LINQ to Entities find top records from ordered groupings

I have a problem that I know how to solve in SQL but not with Linq to Entities.
My data looks like this:
ID GROUP TIMESTAMP
-- ----- ---------
1 A 2011-06-20
2 A 2011-06-21
3 B 2011-06-21
4 B 2011-06-22
5 B 2011-06-23
6 C 2011-06-30
I want to retrieve all the Entity objects (not just the ID) such that I am only getting the most recent record from each group. (ie. the records with ids 2, 5, 6)
In SQL I would do something like this:
SELECT * FROM my_table a
WHERE a.timestamp =
(SELECT MAX(timestamp) FROM my_table b
WHERE a.group = b.group)
(For the sake of this question you can assume that timestamp is unique within each group).
I'd like to do this query against a WCF Data Service using Linq to Entities but I can't seem to have a nested query that references the outside query like this. Can anyone help?
Possibly not as clean and efficient as the hand written version but here's what I came up with
var q = from a in db.MyEntities
where a.Timestamp == (from b in db.MyEntities
where b.Group == a.Group
select b.Timestamp).Max()
select a;
which translates into this SQL
SELECT
[Project1].[Id] AS [Id],
[Project1].[Group] AS [Group],
[Project1].[Timestamp] AS [Timestamp]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Group] AS [Group],
[Extent1].[Timestamp] AS [Timestamp],
[SSQTAB1].[A1] AS [C1]
FROM [MyEntities] AS [Extent1]
OUTER APPLY
(SELECT
MAX([Extent2].[Timestamp]) AS [A1]
FROM [MyEntities] AS [Extent2]
WHERE [Extent2].[Group] = [Extent1].[Group]) AS [SSQTAB1]
) AS [Project1]
WHERE [Project1].[Timestamp] = [Project1].[C1]
Hi try to use linqer that will convert your sql statements to linq query.
Linqer
Best Regards
This should work:
var query = db.my_table
.GroupBy(p=>p.group)
.Select(p=>p.OrderByDescending(q=>q.timestamp).First());
Here you go.A simple way to do.
var result = (from x in my_table
group x by x.Group into g
select new
{
g.Key,
timestamp = g.Max(x => x.TimeStamp),
g //This will return everything in g
});

Categories