using LINQ query to get max of a varchar column - sql-server

I have a Code column of type varchar in database, which includes numbers only. I use the method below to get the next code, and it works fine. But I prefer all the calculations and type conversions to be done on SQL side, to have less overhead. Using the code below, I retrieve all data, which is not needed. Any ideas is really appreciated.
public string GetNextCode(int type)
{
var codeList = (from o in ObjectQuery
where o.Type == Type
select o.Code).ToList<string>();
List<int> list = codeList.Select(int.Parse).ToList();
int nextDL = list.Max() + 1;
return nextDL.ToString();
}

You could try something like the below, it orders the result set descending by the Code and then grabs the first result. Please note I haven't tested this but I think it should work.
var codeList = (from o in ObjectQuery
where o.Type == Type
select o.Code).OrderByDescending(
x => x.Code).First()
Instead of OrderByDescending().First() you could also use OrderBy().Last()

This is a solution I came up with:
var max = (from o in ObjectQuery
let num = ObjectQuery.Where(i => i.Type == Type).Select(i => i.Code).Cast<int>().Max()
select num).FirstOrDefault() + 1;
return max.ToString();

Related

Update table column using table in another SQL Server database while using a spatial function

I would like to update a table column using a spatial function with another database table. This is what I've come up with....
UPDATE FirstDatabase.dbo.track_logs
SET county = t2.CountyName
FROM OtherDatabase.dbo.tblCounty AS t2
WHERE t2.cty_geog.STIntersects(
GEOGRAPHY::STPointFromText('Point(' + FirstDatabase.dbo.track_logs.lng + ' ' +
FirstDatabase.dbo.track_logs.lat + ')', 26915)
)
but I get this error...
An expression of non-boolean type specified in a context where a condition is expected, near ')'.
Almost there. This is one of those silly things in TSQL. You need a = 1.
That's the answer to your question, but I would also be tempted to use POINT(lat, lon, SRID) to address your comment.
UPDATE FirstDatabase.dbo.track_logs
SET county = t2.CountyName
FROM OtherDatabase.dbo.tblCounty AS t2
WHERE t2.cty_geog.STIntersects(geography::Point(FirstDatabase.dbo.track_logs.lat,
FirstDatabase.dbo.track_logs.lng,
26915)) = 1
In a language like C#, you could write something like:
bool val = true;
if (val)
// do stuff
But in TSQL, you have to write the equivalent to:
bool val = true;
if (val == true)
// do stuff
This isn't specific to SQL Spatial, of course, you'd also have to specify WHERE bitColumnName = 1 or, as your example illustrates, WHERE bitReturningFunction(args) = 1.

Invalid Object Name, Temporary Table Entity Framework

I´m trying to optimize some process in my application but I´m stuck with this problem. My application is working so the entity mapping is correct. Simplifying what I´m trying to do is this:
using (var offCtx = new CheckinOfflineEntities())
{
using (var trans = offCtx.Database.BeginTransaction(IsolationLevel.Snapshot))
{
DateTime purgePivot = DateTime.Now.AddDays(-2);
count = offCtx.Database.ExecuteSqlCommand(#"select L.* into #NewLegs from InventoryLeg L where L.STDUTC >= {0}", purgePivot);
long d = offCtx.Database.SqlQuery<long>("select count(*) from #NewLegs").FirstOrDefault();
}
}
I´m selecting some data I want to delete from one table, storing it in a temporary table so that I can use this temporary table in other queries to exclude related data.
The problem is, when I try to use the temporary table I´m receiving the exception SqlException: "Invalid object name '#NewLegs'."
Thank you for your time.
You can merge the query like this.
And count returns int, not long.
COUNT always returns an int data type value. - MSDN
var query = string.Format("{0} {1}",
#"select L.* into #NewLegs from InventoryLeg L where L.STDUTC >= #STDUTC",
#"select count(*) from #NewLegs")
var d = offCtx.Database.SqlQuery<int>(query, new SqlParameter("STDUTC", purgePivot))
.FirstOrDefault();
I realy don´t know why but removing the parameter and adding it in the query solved the problem.
The code below works fine:
using (var offCtx = new CheckinOfflineEntities())
{
using (var trans = offCtx.Database.BeginTransaction(IsolationLevel.Snapshot))
{
DateTime purgePivot = DateTime.Now.AddDays(-2);
count = offCtx.Database.ExecuteSqlCommand(#"select L.* into #NewLegs from InventoryLeg L where L.STDUTC >= " + purgePivot.toString("yyyy-mm-dd HH:mm:ss");
long d = offCtx.Database.SqlQuery<long>("select count(*) from #NewLegs").FirstOrDefault();
}
}

LINQ to SQL anomaly

I'm trying to learn LINQ to SQL. I've run into something I just don't get. Here's the LINQ program (vb.net):
Imports System.IOModule Module1
Sub Main()
Dim crs = New DataClasses1DataContext()
Dim sw As New StringWriter()
crs.Log = sw
Dim reports = From report In crs.CRS_Report_Masters
Group report By report_id = report.Report_ID Into grouped = Group
Select New With {
.reportId = report_id,
.two = grouped.Sum(
Function(row) row.active_report * row.Report_ID)
}
For Each report In reports
Console.WriteLine("{0} {1}", report.reportId, report.two)
Next
MsgBox(sw.GetStringBuilder().ToString())
End Sub
End Module
Here's the SQL it produces:
SELECT SUM([t1].[value]) AS [two], [t1].[Report_ID] AS [reportId] FROM (
SELECT (-(CONVERT(Float,[t0].[active_report]))) *
(CONVERT(Float,CONVERT(Float,[t0].[Report_ID]))) AS [value], [t0].[Report_ID]
FROM [dbo].[CRS_Report_Master] AS [t0]
) AS [t1] GROUP BY [t1].[Report_ID]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.1
What I don't get is why there is a minus sign in the SQL before the math in the parentheses. I didn't specify that in the LINQ query.
If I recall correctly, VB.NET treats -1 as Boolean True, while SQL treats +1 as Boolean True. Therefore the minus sign is necessary for VB.NET to properly interpret the active_report field.
So, now I see what's going on. The LINQ to SQL definition of the column corresponding to a SQL bit is a .net Boolean. Fair enough. When doing math using it, however, the compiler negates the value as described by ekolis. I did not expect that -- in fact I think it's an error, since I do not get the results I expect. e.g. if active_report=1 and report_id=123, I expect to get "123" but I'm getting "-123". I modified the query like this:
Dim reports = (From report In crs.CRS_Report_Masters
Group report By report_id = report.Report_ID Into grouped = Group
Select New With {
.reportId = report_id,
.two = grouped.Sum(
Function(row) If(row.active_report, 1, 0) * CInt(row.Report_ID))
})
which changed the generated SQL to:
SELECT SUM([t1].[value]) AS [two], [t1].[Report_ID] AS [reportId] FROM (
SELECT (
(CASE
WHEN (COALESCE([t0].[active_report],#p0)) = 1 THEN #p1
ELSE #p2
END)) * (CONVERT(Int,[t0].[Report_ID])) AS [value], [t0].[Report_ID]
FROM [dbo].[CRS_Report_Master] AS [t0]
) AS [t1] GROUP BY [t1].[Report_ID]
-- #p0: Input Int (Size = -1; Prec = 0; Scale = 0) [0]
-- #p1: Input Int (Size = -1; Prec = 0; Scale = 0) [1]
-- #p2: Input Int (Size = -1; Prec = 0; Scale = 0) [0]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.1
Makes sense I suppose, though it's a roundabout way get the desired result. In T-SQL, a simple SELECT [BOOLEAN]*[INTEGER] will achieve the same result, I believe. Lesson learned: Don't trust LINQ to always do the right thing. Check its work!

SQL to LINQ Expression

I have specific SQL expression :
{
select * from courceMCPD.dbo.Contact c
where c.cID in ( select cId from courceMCPD.dbo.Friend f where f.cId=5)
}
i would like to get LINQ expression that gets the same result.
thank you in advance.
That sounds like it's equivalent to something like:
var friendIds = from friend in db.Friends
where friend.ContactId == 5
select friend.ContactId;
var query = from contact in db.Contacts
where friendIds.Contains(contact.Id)
select contact;
(There are lots of different ways of representing the query, but that's the simplest one I could think of.)
It's pretty odd to perform a join on a particular field and also mandate that that field has to have a particular value though... there's not very much difference between that and:
var query = db.Contacts.Where(c => c.Id == 5);
... the only difference is whether there are any friend entries for that particular contact.
EDIT: Smudge gave another option for the query in a comment, so I'm promoting it into this answer...
var query = db.Contacts.Where(c => c.Friends.Any(f => f.cId == 5))
This assumes you've got an appropriate Friends relationship defined in the Contacts entity.
Using labda expressions:
var query = dc.Contact
.Where(c => dc.Friend.Select(f => f.cId).Contains(i.cID))
.Where(c => c.cId == 5);
User "query" syntax:
var query = from c in dc.Contact
where (from f in dc.Friend select f.cID).Contains(c.cId)
where c.cId == 5
select c;
You haven't specified VB/C# so I'm going for VB =P
Dim results As IEnumerable(Of yourEntities.Contact) =
(From c In yourContextInstance.Contacts Where (From f In yourContextInstance.Friends
Where f.cId = 5 Select f.cId).Contains(c.cID))
Clearly, Jon's answer works, this query (I believe) just resembles your T-SQL Closer.

Converting complex sql stored proc into linq

I'm using Linq to Sql and have a stored proc that won't generate a class. The stored proc draws data from multiple tables into a flat file resultset.
The amount of data returned must be as small as possible, the number of round trips to the Sql Server need to be limited, and the amount of server-side processing must be limited as this is for an ASP.NET MVC project.
So, I'm trying to write a Linq to Sql Query however am struggling to both replicate and limit the data returned.
Here's the stored proc that I'm trying to convert:
SELECT AdShops.shop_id as ID, Users.image_url_75x75, AdShops.Advertised,
Shops.shop_name, Shops.title, Shops.num_favorers as hearts, Users.transaction_sold_count as sold,
(select sum(L4.num_favorers) from Listings as L4 where L4.shop_id = L.shop_id) as listings_hearts,
(select sum(L4.views) from Listings as L4 where L4.shop_id = L.shop_id) as listings_views,
L.title AS listing_title, L.price as price, L.listing_id AS listing_id, L.tags, L.materials, L.currency_code,
L.url_170x135 as listing_image_url_170x135, L.url AS listing_url, l.views as listing_views, l.num_favorers as listing_hearts
FROM AdShops INNER JOIN
Shops ON AdShops.shop_id = Shops.shop_id INNER JOIN
Users ON Shops.user_id = Users.user_id INNER JOIN
Listings AS L ON Shops.shop_id = L.shop_id
WHERE (Shops.is_vacation = 0 AND
L.listing_id IN
(
SELECT listing_id
FROM (SELECT l2.user_id , l2.listing_id, RowNumber = ROW_NUMBER() OVER (PARTITION BY l2.user_id ORDER BY NEWID())
FROM Listings l2
INNER JOIN (
SELECT user_id
FROM Listings
GROUP BY
user_id
HAVING COUNT(*) >= 3
) cnt ON cnt.user_id = l2.user_id
) l2
WHERE l2.RowNumber <= 3 and L2.user_id = L.user_id
)
)
ORDER BY Shops.shop_name
Now, so far I can return a flat file but am not able to limit the number of listings. Here's where I'm stuck:
Dim query As IEnumerable = From x In db.AdShops
Join y In (From y1 In db.Shops
Where y1.Shop_name Like _Search + "*" AndAlso y1.Is_vacation = False
Order By y1.Shop_name
Select y1) On y.Shop_id Equals x.shop_id
Join z In db.Users On x.user_id Equals z.User_id
Join l In db.Listings On l.Shop_id Equals y.Shop_id
Select New With {
.shop_id = y.Shop_id,
.user_id = z.user_id,
.listing_id = l.Listing_id
} Take 24 ' Fields ommitted for briefity...
I assume to select a random set of 3 listings per shop, I'd need to use a lambda expression however am not sure how to do this. Also, need to add in somewhere consolidated totals for listing fieelds against individual shops...
Anyone have any thoughts?
UPDATE:
Here's the current solution that I'm looking at:
Result class wrapper:
Public Class NewShops
Public Property Shop_id As Integer
Public Property listing_id As Integer
Public Property tl_listing_hearts As Integer?
Public Property tl_listing_views As Integer?
Public Property listing_creation As Date
End Class
Linq + code:
Using db As New Ads.DB(Ads.DB.Conn)
Dim query As IEnumerable(Of IGrouping(Of Integer, NewShops)) =
(From x In db.AdShops
Join y In (From y1 In db.Shops
Where (y1.Shop_name Like _Search + "*" AndAlso y1.Is_vacation = False)
Select y1
Skip ((_Paging.CurrentPage - 1) * _Paging.ItemsPerPage)
Take (_Paging.ItemsPerPage))
On y.Shop_id Equals x.shop_id
Join z In db.Users On x.user_id Equals z.User_id
Join l In db.Listings On l.Shop_id Equals y.Shop_id
Join lt In (From l2 In db.Listings _
Group By id = l2.Shop_id Into Hearts = Sum(l2.Num_favorers), Views = Sum(l2.Views), Count() _
Select New NewShops With {.tl_listing_views = Views,
.tl_listing_hearts = Hearts,
.Shop_id = id})
On lt.Shop_id Equals y.Shop_id
Select New NewShops With {.Shop_id = y.Shop_id,
.tl_listing_views = lt.tl_listing_views,
.tl_listing_hearts = lt.tl_listing_hearts,
.listing_creation = l.Creation,
.listing_id = l.Listing_id
}).GroupBy(Function(s) s.Shop_id).OrderByDescending(Function(s) s(0).tl_listing_views)
Dim Shops as New Dictionary(Of String, List(Of NewShops))
For Each item As IEnumerable(Of NewShops) In query
Shops.Add(item(0).shop_name, (From i As NewShops In item
Order By i.listing_creation Descending
Select i Take 3).ToList)
Next
End Using
Anyone have any other suggestions?
From the looks of that SQL and code, I'd not be turning it into LINQ queries. It'll just obfuscate the logic and probably take you days to get it correct.
If SQLMetal doesn't generate it properly, have you considered using the ExecuteQuery method of the DataContext to return a list of the items you're after?
Assuming that your sproc you're trying to convert is called sp_complicated, and takes in one parameter, something like the following should do the trick
Protected Class TheResults
Public Property ID as Integer
Public Property image_url_75x75 as String
'... and so on and so forth for all the returned columns. Be careful with nulls
End Class
'then, when you want to use it
Using db As New Ads.DB(Ads.DB.Conn)
dim results = db.ExecuteQuery(Of TheResults)("exec sp_complicated {0}", _Search)
End Using
Before you freak out, that's not susceptible to SQL Injection. L2SQL uses proper SQLParameters, as long as you use the squigglies and don't just concatenate the strings yourself.

Resources