SQL Linq Question - c#

I have the following working TSQL query in ms SQL 2008
SELECT
Date,
COUNT(click) AS clicks,
COUNT(sale) AS sales,
count(lead) as leads
FROM
(
SELECT ClickDate as date ,ID AS click ,CAST(NULL AS int) AS sale , CAST(null as int) as lead
FROM clicks
UNION ALL
SELECT Date,null, ID ,NULL
FROM sales
UNION ALL
SELECT Date,null, NULL ,ID
FROM leads
) t
GROUP BY Date
How would i convert this to LINQ to SQL?
I wrote this LINQ but it doesn't work.
public class mydata
{
public DateTime date { get; set; }
public int? click { get; set; }
public int? sale { get; set; }
public int? lead { get; set; }
}
var clicks = from c in Clicks
select new mydata
{
date = c.ClickDate, click = c.ID, sale = null, lead = null
};
var sales = from s in Sales
select new mydata
{
date = s.Date, click = null, sale = s.ID, lead = null
};
var leads = from l in Leads
select new mydata
{
date = l.Date, click = null, sale = null, lead = l.ID
};
var v = clicks.Concat(sales).Concat(leads);
var res = from x in v
group x by x.date into xg
select new
{
date = xg.Key, clicks = xg.Count(z => z.click != null)
};
}
How do i correct this LINQ query?
Update:
i Modified the LINQ query based on David B recommendation.
i'm still getting the following error:
"All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists."

David's correct about the first two issues. For your final problem (3), Count() does not work like it does in SQL. It expects a predicate that returns a bool. Your using it with ints (i.e. z.click, z.sales, etc.)

The problem is that the anonymous types in the projections are not identical... ID is int in one and Nullable<int> in another.
Instead of using an anonymous type in your projections, use this:
public class ConcatTarget
{
public DateTime TheDate {get;set;}
public int? ID {get;set;}
public string sale {get;set;}
public string lead {get;set;}
}
Even though no instances are actually constructed, LinqToSql uses the class's shape to translate the query.
As for Count, perhaps you meant .Count(x => x.Prop != null) ?
Ok, apparently you've hit upon a buggy translation behavior as described here.
What's happening is the sql translator sees the null assignments, and throws them away. This causes an incorrect number of sql columns to be selected between the sets.
Here's a possible workaround:
int? myNull = null;
var clicks =
from c in Clicks
select new mydata
{
date = c.ClickDate,
click = c.ID,
sale = c.ID + myNull,
lead = myNull + c.ID //note - expressions must be unique
};
The basic idea is to create unique expressions the query translator can't throw away. This is harder than it sounds (above is my ninth attempt).
Here's the other two tables:
var sales = from s in Sales
select new mydata
{
date = s.Date,
click = s.ID + myNull,
sale = s.ID,
lead = myNull + s.ID
};
var leads = from l in Leads
select new mydata
{
date = l.Date,
click = l.ID + myNull,
sale = myNull + l.ID,
lead = l.ID
};
If you had more than 2 columns-to-be-nulled, you could resort to subtraction, division, multiplication, etc.

Related

Query- LINQ, 2 tables, Union

These are my two tables:
publicParking(publicParkingID, address, latitude, longtitude, status,
PricePerHour, maxSpaces, occupiedSpaces, freeSpaces, isOrdered)
and
parkingLot(parkingLotID, address, latitude, longtitude, status,
PricePerHour, maxSpaces, occupiedSpaces, freeSpaces, isOrdered)
All of the columns are the same except the ID.
I need to write query in LINQ that will return a table ordered by price with all the available parkings (publicParking / parkingLot) - with status==true.
The table should look like this:
ID address latitude longitude status
Should I do a union, or should I change the table so the first column will call just ID? (instead of publicParkingID and parkingLotID)
I've tried this code but it does not work
var union =
(from lot in parkingLots
where lot.status == true
select lot).Union( from pub in publicParkings
where pub.status==true
select pub);
It give this error :
I am working with LINQPad5 and with code editor of tutorialsteacher. Are there are other options?
To use Union both result sequences must contain the same types. In your example the outer query contains parkingLot and the inner publicParking.
It could be solved using anonymous types:
var union =
(from lot in parkingLots
where lot.status == true
orderby lot.PricePerHour // don't forget ordering
select new {
ID = lot.parkingLotID,
lot.address, lot.latitude, lot.longitude, lot.status})
.Union(from pub in publicParkings
where pub.status==true
orderby pub.PricePerHour // don't forget ordering
select new {
ID = pub.publicParkingID,
pub.address, pub.latitude, pub.longitude, pub.status});
But maybe better for further data handling would be a custom class:
public class ParkingData
{
public int ID {get; set;}
public string Address {get; set;}
public double Latitude {get; set;}
public string Longitude {get; set;}
public bool Status {get; set;}
}
and query like that:
var union =
(from lot in parkingLots
where lot.status == true
orderby lot.PricePerHour // don't forget ordering
select new ParkingData {
ID = lot.parkingLotID,
Address = lot.address,
Latitude = lot.latitude,
Longitude = lot.longitude,
Status = lot.status})
.Union(from pub in publicParkings
where pub.status==true
orderby pub.PricePerHour // don't forget ordering
select new {
select new ParkingData {
ID = pub.publicParkingID,
Address = pub.address,
Latitude = pub.latitude,
Longitude = pub.longitude,
Status = pub.status});

left non equi join linq

I have a list of objects in table tblB which is retrieved from db in following format.
public class playdata
{
public string consumerid { get; set; }
public string play_time { get; set; }
public string genre{ get; set; }
.
.
.
public int mycounter{ get; set; }
}
I have a table tblA which has a column colA which has int from 0 to 1000.
I wish to create a linq query similar to sql as below..
Select x.i as numindex, y.consumerid,y.play_time,y.genre
From
(Select colA as i from tblA) x
left join
(
Select consumerid, play_time,genre,mycounter from tblB
)y on y.mycounter > x.i
I tried the following unsuccessfully..
I came to find that Enumerable.Range(0, 1001) generated a numeric series so no need to get that table data..
List<playdata> plays = .....
var q= (from s in Enumerable.Range(0, 1001)
join p in plays on s < p.mycounter into t
from rt in t.DefaultIfEmpty() select new{
numindex=s,
consumerid=p.consumerid,
play_time =p.play_time,
genre=p.genre
}).ToList();
I see two errors in second line..
p is not in scope of left side of equals .. I have also tried..p.mycounter>s with same result.
The other error is in into where it shows.. expected contextual keyword equals
The errors go away if I change the second line to..
join p in plays on s equals p.mycounter into t
All help is sincerely appreciated.
Thanks
You have to use the alternative way of representing left outer join in LINQ:
var q =
(from i in Enumerable.Range(0, 1001)
from p in plays.Where(x => x.mycounter > i).DefaultIfEmpty()
select new
{
numindex = i,
consumerid = p?.consumerid,
play_time = p?.play_time,
genre = p?.genre
}).ToList();
Note that in LINQ to Objects you have to account for right side of the left outer join returning null when there is no matching element, otherwise you'll get NullReferenceException.

LINQ Left and Right Join Query

This is my sql query, how can I generate this query into LINQ:
SELECT
TimeTable.StartTime,
sum(Booking.Quantity) as Total
FROM PromotionSlot
RIGHT JOIN TimeTable
ON PromotionSlot.StartHour = TimeTable.StartTime
LEFT JOIN Booking
ON PromotionSlot.ID = Booking.PromotionSlotID
GROUP BY TimeTable.StartTime
Result:
|StartTime |Total|
---------------------
9 NULL
10 NULL
11 2
12 2
13 NULL
14 NULL
15 NULL
16 NULL
17 NULL
18 NULL
19 NULL
20 NULL
21 NULL
This is what I attempted, I'm not sure that the structure of the linq is correct with my SQL query. But I faced error about The cast to value type 'Int32' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type. Please guide me, thank you all in advance!
var bookingdata =
(from ps in dc.PromotionSlots
join t in dc.TimeTables on ps.StartHour equals t.StartTime into Ts
join bo in dc.Bookings on ps.ID equals bo.PromotionSlotID into Bs
from time in Ts.DefaultIfEmpty()
from book in Bs.DefaultIfEmpty()
group new {time,book} by time.StartTime into NewGroup
select new dataView
{
StartTime = NewGroup.Key,
numBookings = NewGroup.Select(a => a.book!=null? a.book.Quantity: 0).Sum()
}).ToList();
Here is my dataView model
public class dataView
{
public int? StartTime { get; set; }
public int? numBookings { get; set; }
}
UPDATED:
changed StartTime in my dataView model to int? and this is the result by using console.log()
Format: #:console.log("* " + "#item.StartTime" + ":" + "#item.numBookings");
* :0
* 10:0
* 11:2
* 12:2
I found out the reason why the above console.log() will appear this result. I tried to change my SQL Query RIGHT JOIN TimeTable to LEFT JOIN TimeTable. The return result totally the same like the output from my LINQ.
I think the problem you're hitting is that in ad-hoc query results, any column value can be null (eg., the Total column in your results example has mixed values, int and null). But C# is not so lenient, an a null value cannot be stuffed into a integer value.
I think that in your dataView type, the numBookings property is probably set to int, and an int type in C# is not nullable.
I believe if you change it to a nullable int, like:
public int? numBookings { get; set; }
then that may fix your error. If not that, then the following might work:
numBookings = NewGroup.Select(a => a.book!=null ? (a.book.Quantity ?? 0): 0).Sum() ?? 0
But I am not sure about this last one.
UPDATE
What if you update your LINQ query to use LEFT JOIN for both joins, like so:
var bookingdata = (
from t in dc.TimeTables
join ps in dc.PromotionSlots on t.StartTime equals ps.StartHour into Ts
from time in Ts.DefaultIfEmpty()
join bo in dc.Bookings on time.ID equals bo.PromotionSlotID into Bs
from book in Bs.DefaultIfEmpty()
group new {time, book} by time.StartTime into NewGroup
select new dataView
{
StartTime = NewGroup.Key,
numBookings = NewGroup.Select(a => a.book!=null? a.book.Quantity: 0).Sum()
}
).ToList();
Does this make the results more consistent with the SQL query?

Entity Framework / LINQ Round and Count Latitude/Longitude

I'm working on a maps application and need to be able to pare down a giant dataset of location values into more reasonable values. So what I'd like to do is group the positions by rounded values like the following SQL:
select ROUND(Latitude, 0), ROUND(Longitude, 0), count(*) from tweet
group by ROUND(Latitude, 0), ROUND(Longitude, 0)
order by count(*) desc
I can never get grouping to work the way I need it to though with EF and/or LINQ (mainly because I can't seem to understand how it works). Ideally I would prefer to have it right in the EF query so that it becomes part of the SQL statement and I don't have to bring the giant dataset into a List before performing some LINQ operation on it...but if it has to be done that way then so be it.
I can do it this way, but I don't want to:
var items = context.ExecuteStoreQuery<DataPoint>("select ROUND(Latitude, 0) as lat, ROUND(Longitude, 0)as lon, count(*) as weight from location " +
"group by ROUND(Latitude, 0), ROUND(Longitude, 0) " +
"order by count(*) desc", null).ToList();
Edit:
Final solution (with a bit of back and forth) accepted below ended up looking like this:
private class DataPoint
{
public double lat { get; set; }
public double lon { get; set; }
public int weight { get; set; }
}
var mydata = (from t in locations
group t by new {
Latitude = EntityFunctions.Truncate(t.Latitude, accuracy).Value,
Longitude = EntityFunctions.Truncate(t.Longitude, accuracy).Value }
into g
orderby g.Count()
select new DataPoint{ lat = g.Key.Latitude, lon = g.Key.Longitude, weight = g.Count() }).ToList();
Don't forget to add this using statement:
using System.Data.Objects;
from t in context.Tweets
group t by new { t.Latitude, t.Longitude } into g
order by g.Count() desc
select new { g.Key.Latitude, g.Key.Longitude, Count = g.Count() }

Lambda expressions to obtain data through navigation properties, LINQToEntities

I'm having a problem getting some data since I'm just starting to use LINQToEntities, Entity Framework and Lambda Expressions.
Let me explain my case:
I have a database with 4 tables as shown here:
when I generate the model from database in Visual Studio (2010) the result is this:
I searched for some info and turns out that since the table t_user_role only has the ids from its two parent tables, it gets abstracted in the model, and you have to use the navigation properties instead.
I had some problem getting the role info for a user on a given system (as the next function does)
public t_role GetRoleForUser(string userId, string sysId)
{
entities = new secfinEntities(); //context from the model
t_role userRole = entities.t_role.Where(r => r.t_user.Any(u => u.uid == userId) & r.sys_id == sysId).First();
return userRole;
}
Now I have to implement a simple search function that will look users that contain the provided string and return the user's id and name (uid, user_name) and their role's info (role_id, role_name) on a given system (the system info i have beforehand), so basically I wanna turn this next SQL Query into Lambda Expressions (keeping in mind that in the model the table t_user_role has been abstracted)
SELECT U.uid, U.user_name, R.role_id, R.role_name
FROM t_user U
INNER JOIN t_user_role UR ON U.uid = UR.uid
INNER JOIN t_role R ON UR.role_id = R.role_id
WHERE R.sys_id = #p0 -- first parameter
AND U.user_name LIKE '%' + #p1 + '%' -- second parameter
Also, I would like to store the results of that in a List of a type I have defined as follows:
public class UserRole
{
public string UserId { get; set; }
public string UserName { get; set; }
public string RoleId { get; set; }
public string RoleName { get; set; }
public UserRole(string uid, string uname, string rid, string rname)
{
UserId = uid;
UserName = uname;
RoleId = rid;
RoleName = rname;
}
}
So after explaining what I have done and what I'm trying to do first question is: how can that be done?, second: can the same be accomplished through the verbose form instead of Lambda expressions? if yes, how?
Thanks a lot in advance for your time.
This T-SQL:
SELECT U.uid, U.user_name, R.role_id, R.role_name
FROM t_user U
INNER JOIN t_user_role UR ON U.uid = UR.uid
INNER JOIN t_role R ON UR.role_id = R.role_id
WHERE R.sys_id = #p0 -- first parameter
AND U.user_name LIKE '%' + #p1 + '%' -- second parameter
given your model translates to this verbose syntax (including the requirement to use your new model):
var results = (from u in entities.t_user
from r in u.t_role
where r.sys_id == sysIdVariable && u.user_name.Contains(userNameVariable)
select new UserRole(u.uid, u.user_name, r.role_id, r.role_name))
I know you didn't ask for it, but a lambda version might look like:
var results = entities.t_user.Join(entities.t_role,
x => x.t_role_id,
x => x.role_id,
(u, r) => new UserRole(u.uid,
u.user_name,
r.role_id,
r.role_name))

Categories