Problem with decimal precision in SQL using Linq to SQL - c#

I have a simple query:
var results = from k in db.tree_nodes
join s in db.stocks
on k.tree_nodes_id equals s.tree_nodes_id
into tmpTable
from rowtmp in tmpTable.DefaultIfEmpty()
select new
{
stock = (rowtmp.amount == null) ?
((k.code == null) ? (decimal?)null : (decimal?)0)
:
rowtmp.amount - rowtmp.amount_in_use,
};
This is the generated SQL code:
SELECT
(CASE
WHEN ([t1].[amount]) IS NULL THEN
(CASE
WHEN [t0].[code] IS NULL THEN CONVERT(Decimal(33,4),NULL)
ELSE CONVERT(Decimal(33,4),0)
END)
ELSE CONVERT(Decimal(33,4),[t1].[amount] - [t1].[amount_in_use])
END) AS [stock]
FROM [dbo].[tree_nodes] AS [t0]
LEFT OUTER JOIN [dbo].[stocks] AS [t1] ON [t0].[tree_nodes_id] = [t1].[tree_nodes_id]
The problem is, the generator created Decimal(33,4) when converting the results. So I'm getting "123.4560" in results instead of "123.456" All of my fields in this query are decimal(14,3). I don't mind the 33 part but I need to change the ,4 to ,3. How can I do this?

You could round the decimal values to 3 decimals?
var results = from k in db.tree_nodes
join s in db.stocks
on k.tree_nodes_id equals s.tree_nodes_id
into tmpTable
from rowtmp in tmpTable.DefaultIfEmpty()
select new
{
stock = (rowtmp.amount == null) ?
((k.code == null) ? (decimal?)null : (decimal?)0)
:
decimal.Round(rowtmp.amount,3) - decimal.Round(rowtmp.amount_in_use == null ? 0 : rowtmp.amount_in_use,3),
};
Dunno of any way to prevent linq-to-sql from type conversion.

Related

Need to convert SQL query to Entity Framework Core query

This SQL Server query is working very well, but when I convert it to an Entity Framework query, it did not return rows, instead it shows an error:
Could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync()
How to solve this error?
This is my SQL query:
SELECT
Sy.SystemUserName, Te.Teamname , ISNULL(bt.BreakId, 0) Bre,
ISNULL(DATEADD(ms, SUM(DATEDIFF(ms, '00:00:00.000', Bt.DurationTime)), '00:00:00.000'), 0) AS WorkingHours,
CASE
WHEN DATEADD(ms, SUM(DATEDIFF(ms, '00:00:00.000', Bt.DurationTime)), '00:00:00.000')
> '1900-01-01 08:00:00.000' THEN 'FP'
WHEN DATEADD(ms, SUM(DATEDIFF(ms, '00:00:00.000', Bt.DurationTime)), '00:00:00.000')
> '1900-01-01 04:00:00.000' THEN 'HP'
WHEN DATEADD(ms, SUM(DATEDIFF(ms, '00:00:00.000', Bt.DurationTime)), '00:00:00.000')
> '1900-01-01 02:00:00.000' THEN 'P'
ELSE 'AB'
END AS Attendance
FROM
SystemDetails Sy
LEFT JOIN
TeamList Te ON Sy.TeamId = Te.Id
LEFT JOIN
BreakTransaction Bt ON (COALESCE(Sy.Id, '') = COALESCE(Bt.SystemId, ''))
AND Bt.BreakId = 1
WHERE
Sy.IsDeleted = 0
AND Sy.TeamId != 0
AND Sy.CompanyGUID = '17'
GROUP BY
Sy.SystemUserName, Te.Teamname, Bt.BreakId
EF query:
=> await (from Sy in this.data.SystemDetails
join Te in this.data.TeamList on Sy.TeamId equals Te.Id
join Bt in this.data.BreakTransaction on Sy.Id equals Bt.SystemId
where Sy.CompanyGUID == UserId && Bt.BreakId == 1
group Bt by new
{
Sy.SystemUserName,
Te.Teamname,
Bt.DurationTime
}
into summary
select new BreakMasterListing
{
StaffName = summary.Key.SystemUserName,
TeamName = summary.Key.Teamname,
BreakHours = summary.Sum(x => x.DurationTime.Hours).ToString()
//Attendance = Bt.DurationTime.Hours >= 8 ? "FP" :
Bt.DurationTime.Hours >= 4 ? "HP" : Bt.DurationTime.Hours >= 2 ? "P" : "AB",
// BreakHours = (this.data.BreakTransaction.Where(s => s.SystemId == 1
&& s.BreakId == 1).Select(b =>
b.DurationTime.Hours)).DefaultIfEmpty(0).Sum().ToString()
})
.ToListAsync();
Need to fix this problem. any one assist me

LINQ - select statement in the selected column

i am intend to convert the following query into linQ
SELECT TOP 100 S.TxID,
ToEmail,
[Subject],
ProcessedDate,
[Status] = (CASE WHEN EXISTS (SELECT TxID FROM TxBounceTracking
WHERE TxID = S.TxID)
THEN 'Bounced'
WHEN EXISTS (SELECT TxID FROM TxOpenTracking
WHERE TxID = S.TxID)
THEN 'Opened'
ELSE 'Sent' END)
FROM TxSubmissions S
WHERE S.UserID = #UserID
AND ProcessedDate BETWEEN #StartDate AND #EndDate
ORDER BY ProcessedDate DESC
The following code is the linq that i converted.
v = (from a in dc.TxSubmissions
where a.ProcessedDate >= datefrom && a.ProcessedDate <= dateto && a.UserID == userId
let bounce = (from up in dc.TxBounceTrackings where up.TxID == a.TxID select up)
let track = (from up in dc.TxOpenTrackings where up.TxID == a.TxID select up)
select new { a.TxID, a.ToEmail, a.Subject,
Status = bounce.Count() > 0 ? "Bounced" : track.Count() > 0 ? "Opened" : "Sent",
a.ProcessedDate });
However this linq is too slow because the bounce and track table, how should i change the linq query to select one row only to match the SQL query above >>
SELECT TxID FROM TxOpenTracking WHERE TxID = S.TxID
in my selected column, so it can execute faster.
Note that the record contained one million records, thats why it lag
As you don't care about readability because you will end up generating the query via EF you can try to join with those two tables. (it looks that TxID is a FK or a PK/FK)
More about JOIN vs SUB-QUERY here: Join vs. sub-query
Basically your SQL looks like this:
SELECT TOP 100 S.TxID, ToEmail, [Subject], ProcessedDate,
[Status] = (CASE WHEN BT.TxID IS NOT NULL
THEN 'Bounced'
WHEN OP.TxID IS NOT NULL
THEN 'Opened'
ELSE 'Sent' END)
FROM TxSubmissions S
LEFT JOIN TxBounceTracking BT ON S.TxID = BT.TxID
LEFT JOIN TxOpenTracking OP ON S.TxID = OP.TxID
WHERE S.UserID = #UserID
AND ProcessedDate BETWEEN #StartDate AND #EndDate
ORDER BY ProcessedDate DESC
And then, you can try to convert it to LINQ something like:
v = (from subs in dc.TxSubmissions.Where(sub => sub.ProcessedDate >= datefrom && sub.ProcessedDate <= dateto && sub.UserID == userId)
from bts in dc.TxBounceTrackings.Where(bt => bt.TxID == subs.TxID).DefaultIfEmpty()
from ots in dc.TxOpenTrackings.Where(ot => ot.TxID == subs.TxID).DefaultIfEmpty()
select new { });
More about left join in linq here: LEFT JOIN in LINQ to entities?
Also if you remove default if empty you'll get a inner join.
Also you need to take a look at generated SQL in both cases.

Equivalent of outer join with non-equals predicate in Linq

I am looking for the Linq-equivalent of the following SQL:
SELECT t0.Fk_CompanyId, t0.CheckedUtc, t0.IsBlocking
FROM MyTable t0
LEFT OUTER JOIN MyTable t1
ON t0.Fk_CompanyId = t1.Fk_CompanyId AND t0.CheckedUtc < t1.CheckedUtc
WHERE t1.Fk_CompanyId IS NULL
AND t0.CheckedUtc IS NOT NULL
The closest I've got with Linq is:
from t0 in MyTable
join t1 in MyTable on t0.Fk_CompanyId equals t1.Fk_CompanyId into t1tmp
from t1 in t1tmp.DefaultIfEmpty()
where t1.Fk_CompanyId == null && t0.CheckedUtc != null && t0.CheckedUtc < t1.CheckedUtc
select new { cid = t0.Fk_CompanyId, cuct = t0.CheckedUtc, isbl = t0.IsBlocking }
... which produces the following SQL (reformatted slightly):
SELECT t0.Fk_CompanyId, t0.CheckedUtc, t0.IsBlocking
FROM MyTable AS t0
LEFT OUTER JOIN MyTable AS t1
ON t0.Fk_CompanyId = t1.Fk_CompanyId
WHERE (t1.Fk_CompanyId IS NULL)
AND (t0.CheckedUtc IS NOT NULL)
AND (t0.CheckedUtc < t1.CheckedUtc)
These are not exactly equivalent. (The t0.CheckedUtc < t1.CheckedUtc is pushed to the WHERE.) When I outer-left-join on t0.CheckedUtc < t1.CheckedUtc, this produces NULL values on the right-hand-side of the join. When I filter with WHERE instead, this removes all the NULL values that I am interested in.
Perspective: I am trying to find the rows in MyTable with the most recent CheckedUtc, if there are any non-NULL ones, grouped by Fk_CompanyId. And I want one row for each Fk_CompanyId. There are several "possible duplicates" that only deal with finding the most recent CheckedUtcs (but not their respective rows), or assume that CheckedUtc is NOT NULL.
So: How do I perform the equivalent of a join on a non-equals predicate in Linq?
Try this
from t0 in MyTable
From t1 in MyTable( x=>x.Fk_CompanyId=t0.Fk_CompanyId && x.CheckedUtc > t0.CheckedUtc ).DefaultIfEmpty()
where t1.Fk_CompanyId == null && t0.CheckedUtc != null
select new { cid = t0.Fk_CompanyId, cuct = t0.CheckedUtc, isbl = t0.IsBlocking }

Issue in linq to entity framework

(from dtContract in dataEntity.contracts
where
(
dtContract.Supplier_ID == suppID &&
dtContract.Contract_Year != null
)
orderby dtContract.Contract_Year
select new
{
year = (int) dtContract.Contract_Year
}).ToList();
Above LINQ query is converted to following SQL query
SELECT
`Project1`.`Supplier_ID`, `Project1`.`C1`
FROM
`contracts` AS `Project1`
WHERE
(`Project1`.`Supplier_ID` = #p__linq__0)
AND (`Project1`.`Contract_Year` IS NOT NULL)
ORDER BY `Project1`.`Contract_Year` ASC;
Which is throwing exception
Unknown column C1
Can you please provide the reason behind this..?

linq-to-sql Concat() throwing a System.IndexOutofRangeException

I'm struggling with an exception using linq-to-sql Concat()
I've got 2 tables.
The first table, ParsedMessages, has the following fields
* ParsedMessageID (int)
* MessageTypeID (int)
* TextMessage (varchar(max))
The second table, ParsedMessageLinks, has the following fields
* ParsedMessageID (int)
* AnotherID (int)
* NumberOfOccurences (int)
This is what I need to achieve using a single linq query but I'm not sure if it's possible or not.
Through a join, retrieves ParsedMessage records that links to a certain AnotherID. In example SQL and linq code, the AnotherID will have the value 0 just for the purpose of having an example.
For each ParsedMessage record, I also need the NumberOfOccurences (field of table #2)
Retrieve only the top(100) ParsedMessage records for each MessageTypeID. So for example, if there is 275 records in ParsedMessages that links to AnotherID==0 where the first 150 records have MessageTypeID == 0 and the remaining 125 records having MessageTypeID == 1, I want my query to end up returning 200 records, the top(100) with MessageTypeID == 0 and the top(100) with MessageTypeID == 1
After a lot of search, I've found that the plain SQL equivalent of I what I want to do is this. I knew that this exists first end, but I tried to find something else without Union all at first and fail to do so (my SQL knowledge is not that good) :
SELECT TOP(100) PM.*,
PML.NumberOfOccurences FROM
ParsedMessages PM INNER JOIN
ParsedMessageLinks PML ON
PM.ParsedMessageID =
PML.ParsedMessageID WHERE
PML.AnotherID = 0 AND PM.MessageTypeID
= 0 ORDER BY PM.ParsedMessageID DESC UNION ALL
SELECT TOP(100) PM.*,
PML.NumberOfOccurences FROM
ParsedMessages PM INNER JOIN
ParsedMessageLinks PML ON
PM.ParsedMessageID =
PML.ParsedMessageID WHERE
PML.AnotherID = 0 AND PM.MessageTypeID
= 1 ORDER BY PM.ParsedMessageID DESC UNION ALL
SELECT TOP(100) PM.*,
PML.NumberOfOccurences FROM
ParsedMessages PM INNER JOIN
ParsedMessageLinks PML ON
PM.ParsedMessageID =
PML.ParsedMessageID WHERE
PML.AnotherID = 0 AND PM.MessageTypeID
= 2 ORDER BY PM.ParsedMessageID DESC
So basically, the only way to retrieve the data I need is to do 3 sql queries in a single pass where only the PM.MessageTypeID is different for each query.
Now I wanted to achieve this using linq-to-sql. After googling, I've found that I could use the Linq Concat() method to reproduce a SQL Union All.
Here are some links pointing to what I thought would work :
http://blog.benhall.me.uk/2007/08/linq-to-sql-difference-between-concat.html
EF. How to union tables, sort rows, and get top entities?
I end up having this exception :
System.IndexOutOfRangeException : "Index was outside the bounds of the array."
Here's the faulty code :
IQueryable<MyObject> concatquery;
int[] allMessageTypeIDs = new int[] { 0, 1, 2 };
for (int mt = 0; mt < allMessageTypeIDs.Length; mt++)
{
if (mt == 0)
{
concatquery = (from pm in db.ParsedMessages
join pml in db.ParsedMessageLinks on pm.ParsedMessageID equals pml.ParsedMessageID
where pml.AnotherID == 0 && pm.MessageTypeID == allMessageTypeIDs[mt]
orderby pm.ParsedMessageID descending
select new MyObject
{
NumberOfOccurences = pml.Occurrences,
ParsedMessage = pm
}).Take(100);
}
else
{
concatquery = concatquery.Concat(from pm in db.ParsedMessages
join pml in db.ParsedMessageLinks on pm.ParsedMessageID equals pml.ParsedMessageID
where pml.AnotherID == 0 && pm.MessageTypeID == allMessageTypeIDs[mt]
orderby pm.ParsedMessageID descending
select new MyObject
{
NumberOfOccurences = pml.Occurrences,
ParsedMessage = pm
}).Take(100);
}
}
var results = concatquery.ToArray();
I've declared the int array allMessageTypeIDs, for simplicity. But remember that the values it holds may differ, so that's why I've added the for loop. Maybe it's "illegal" to use a Concat() in a loop that way, but I could not find any relevant information on this exception.
The class MyObject basically hold a int (NumberOfOccurences) and a ParsedMessage database object, nothing else.
Any suggestions on what could be wrong with my code that causes the exception?
Thanks
Francis
Never use the variable you're looping with in your Linq queries. It just doesn't work. You want to assign a new temporary variable to use instead.
IQueryable<MyObject> concatquery;
int[] allMessageTypeIDs = new int[] { 0, 1, 2 };
for (int mt = 0; mt < allMessageTypeIDs.Length; mt++)
{
var myItem = allMessageTypeIDs[mt]; // <-- HERE!
if (mt == 0)
{
concatquery = (from pm in db.ParsedMessages
join pml in db.ParsedMessageLinks on pm.ParsedMessageID equals pml.ParsedMessageID
where pml.AnotherID == 0 && pm.MessageTypeID == myItem
orderby pm.ParsedMessageID descending
select new MyObject
{
NumberOfOccurences = pml.Occurrences,
ParsedMessage = pm
}).Take(100);
}
else
{
concatquery = concatquery.Concat(from pm in db.ParsedMessages
join pml in db.ParsedMessageLinks on pm.ParsedMessageID equals pml.ParsedMessageID
where pml.AnotherID == 0 && pm.MessageTypeID == myItem
orderby pm.ParsedMessageID descending
select new MyObject
{
NumberOfOccurences = pml.Occurrences,
ParsedMessage = pm
}).Take(100);
}
}
var results = concatquery.ToArray();

Categories

Resources