简体   繁体   中英

How to get Max and Min IN/OUT time for each date in LINQ?

I am new to LINQ and have been trying to write a linq query that would be equivalent to the following SQL statement:

SELECT linking."RfidNumber",
    linking."VehicleID",
    acs."Date",
    Min(acs."acs_datetime" ) as INTIME,
    Max(acsout."acs_datetime" )  
FROM dbo."acs_transaction_new" acs
INNER JOIN dbo."acs_transaction_new" acsout 
    ON acs."Date" = acsout."Date"  AND acs."acs_operations" = 'in' AND
        acsout."acs_operations" = 'out'
INNER JOIN dbo."PassLinkings" linking ON acs."acs_rfid_no" = linking."RfidNumber" AND
    acs."acs_vehicle_id" = linking."VehicleID"   
WHERE acsout."acs_operations" = 'out' AND acs."acs_operations" = 'in'
GROUP BY linking."RfidNumber", linking."VehicleID",  acs."Date";

Basically I want MIN acs."acs_datetime" where acs."acs_operations"='in' and similarly MAX acs."acs_datetime" where acs."acs_operations"='out' for each date in the table for unique combination of RfidNumber and VehicleID

I am lost at Linq:

    var query1 = (from acs in db.acs_transaction
                             join acsout in db.acs_transaction on acs.acs_datetime equals acsout.acs_datetime     
                              join linking in db.PassLinkings on new { rfid = acs.acs_rfid_no, vehicleid = acs.acs_vehicle_id } equals new { rfid = linking.RfidNumber, vehicleid =  (linking.VehicleID == null ? -1 : (int)linking.VehicleID) }
                              where acs.acs_operations == "in" && acsout.acs_operations == "out"
                              select new { acs.acs_rfid_no,
 acs.VehicleID ,
acs."Date",
Min(acs."acs_datetime" ),// something like that
Max(acs."acs_datetime" )
}).ToList(); ;

From the Query that you have posted following is my understanding of your models:

public class Acs_Transaction_New_class
{
    public DateTime Date {get; set;}
    public string acs_operations {get; set;}
    public int acs_rfid_no {get; set;}
    public int acs_vehicle_id {get; set;}
    public DateTime acs_datetime {get; set;}
}

public class PassLinking_Class
{
    public int RfidNumber { get; set; }
    public int VehicleID { get; set; }
}

Fluent Syntax:

// Data Placeholders to compile linq query

   var result = Acs_Transaction_New
                   .Join(Acs_Transaction_New, acs => acs.Date, acsout => acsout.Date, (acs, acsout) => new { acs, acsout })
                   .Where(x => x.acs.acs_operations == "in" && x.acsout.acs_operations == "out")
                   .Join(PassLinkings, x => new { rfid = x.acs.acs_rfid_no, vehicleid = x.acs.acs_vehicle_id }, linking => new { rfid = linking.RfidNumber, vehicleid = linking.VehicleID}, (x, linking) => new { x.acs, x.acsout,linking })
                   .GroupBy(x => new { x.linking.RfidNumber, x.linking.VehicleID, x.acs.Date })
                   .Select(x => new
                   {
                       x.Key.RfidNumber,
                       x.Key.VehicleID,
                       x.Key.Date,
                       acs_min_datetime = x.Min(y => y.acs.acs_datetime),
                       acs_max_datetime =  x.Max(y => y.acsout.acs_datetime)
                   });

Important Points:

  1. Fluent Syntax is much more verbose than Query syntax, but provides a clear data cascading, therefore easier to perceive
  2. From the Sql query, I have translated, acs_operations == "in" / "out" as Where condition, instead of Join, ideally we don't need comparison with "in" and "out" twice

Query Syntax:

var result = from acs in Acs_Transaction_New
             join acsout in Acs_Transaction_New on acs.Date equals acsout.Date
             where acs.acs_operations == "in" && acsout.acs_operations == "out"
             join link in PassLinkings on new { rfid = acs.acs_rfid_no, vehicleid = acs.acs_vehicle_id } equals new { rfid = link.RfidNumber, vehicleid = link.VehicleID}
             group new {acs,acsout } by new { link.RfidNumber, link.VehicleID, acs.Date } into group1
             select new
             {
                 group1.Key.RfidNumber,
                 group1.Key.VehicleID,
                 group1.Key.Date,
                 acs_min_datetime = group1.Min(y => y.acs.acs_datetime),
                 acs_max_datetime = group1.Max(y => y.acsout.acs_datetime)
             };

Both versions of the Query are compiling, therefore syntactically correct, you just need to make modifications to suit your use case

Another way (in SQL and LINQ) would be to group the rows regardless of in/out operation, and then filter and min/max inside each group:

var ans = from acs in dbo.acs_transaction
          join linking in dbo.PassLinkings on new { RfidNumber = acs.acs_rfid_no, VehicleID = acs.acs_vehicle_id } equals new { linking.RfidNumber, linking.VehicleID }
          group acs by new { linking.RfidNumber, linking.VehicleID, acs.Date } into acsg
          select new {
              acsg.Key.RfidNumber,
              acsg.Key.VehicleID,
              acsg.Key.Date,
              InTime = acsg.Where(acs => acs.acs_operations == "in").Min(acs => acs.acs_datetime),
              OutTime = acsg.Where(acs => acs.acs_operations == "out").Max(acs => acs.acs_datetime)
          };

In my LINQ to SQL testing with a similar database query, this generated a single SQL query.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM