Count Group Ordinal in LINQ to Dataset - ado.net

I have an old FoxPro program that does a SQL query which includes the following:
SELECT Region,
Year AS yr_qtr,
SUM(Stock) AS inventory
**...
COUNT(Rent) AS rent_ct
FROM
**...
GROUP BY Region, Year
ORDER BY Region, Year
INTO CURSOR tmpCrsr
The query is against a .DBF table file, and includes data from an Excel file. I've used both to populate an enumeration of user-defined objects in my C# program. (Not sure .AsEnumerable is needed or not.) I then attempt to use LINQ to Dataset to query the list of user objects and create the same result set:
var rslt1 = from rec in recs_list //.AsEnumerable()
group rec by new {rec.Region, rec.Year} into grp
select new
{
RegName = grp.Key.Region,
yr_qtr = grp.Key.Year,
inventory = grp.Sum(s => s.Stock),
// ...
rent_count = grp.Count(r => r.Rent != null)
};
This gives me the warning that "The result of the expression is always 'true' since a value of type 'decimal' is never equal to 'null' of type 'decimal'" for the Count() of the Rent column.
This makes sense, but then how do I do a count exclusive of the rows that have a value of .NULL. for that column in the FoxPro table (or NULL in any SQL database table, for that matter)? I can't do a null test of a decimal value.

If rent is based off of a column which is not a nullable value, then checking for null makes no sense which I believe the compiler accurately shows. Change the line to
rent_count = grp.Count(r => r.Rent != 0)
instead.
For if the code is actuall nullable such as:
Decimal? rent;
That would make checking rent against null valid. If that is the case then the line would be:
rent_count = grp.Count(r => (r.Rent ?? 0) != 0)
where null coalesding operator ?? can be used. Which states if r.rent is null, use the value 0 (or any value you want technically) for r.Rent. in the next process.

Related

How to catch null while summarizing records in EF6

next problem with Linq/EF6 queries.
I simply like to build a sum of some decimal fields:
var offsetHours1 = (from os in db.TimesheetOffsets
where (os.EmployeeId == employeeId && os.OffsetDate <= DateTime.Today)
select new
{
offset = os.OffsetHours
}).Sum(h=>h.offset);
So far it works, if I have records to sum but if the query returns null or no records, I get a System.InvalidOperationException
Is there an elegant way to summarize records in one step, so if there are no records, 0 is returned?
Thanks, Carsten
There's a quirk with the Sum extension method. As OffsetHours is a decimal, the overload of Sum you'll be using is Sum(..., decimal) which has this behaviour. To avoid it you can cast the value to a decimal? (nullable). With this you'll be using a different Sum that returns a nullable decimal and is OK with empty lists;
You can for example do this;
var offsetHours1 = (from os in db.TimesheetOffsets
where (os.EmployeeId == employeeId && os.OffsetDate <= DateTime.Today)
select os.OffsetHours)
.Sum(h => (decimal?)h);
Edit:
Removed unnecessary creation of anonymous type.

How to update multiple rows by keeping some column values the same and updating others?

I am trying to bulk update some rows in postgres. Now not all of the rows need to update the same column values. For example, row 1 needs to update column 1 and 3 whereas row 2 needs to update column 2 and 4. so row 1 column 2 and 4 should not change and row 2 column 1 and 3 should not change.
I have tried using CASEs to conditionally SET the correct column values but it doesn't work with multiple rows. It DOES work if I only try to update 1 row at a time.
update topic as tmp set
"from" = (CASE WHEN tmp2."from2"::text = 'OLD_VALUE' THEN tmp."from"::int2 ELSE tmp2."from2"::int2 end),
"text_search" = (CASE WHEN tmp2."text_search2"::text = 'OLD_VALUE' THEN tmp."text_search"::text ELSE tmp2."text_search2"::text end),
"weight" = (CASE WHEN tmp2."weight2"::text = 'OLD_VALUE' THEN tmp."weight"::numeric ELSE tmp2."weight2"::numeric end)
from (values
(1051,1,'Electronic Devices',3),
(1052,'OLD_VALUE','OLD_VALUE',100)
) as tmp2("id2","from2","text_search2","weight2")
where tmp2."id2" = tmp."id"
This is the error message i get
SQL Error [22P02]: ERROR: invalid input syntax for type integer: "OLD_VALUE"
When I try with only 1 FROM value
from (values (1051,1,'Electronic Devices',3))
or
from (values (1052,'OLD_VALUE','OLD_VALUE',100))
it works correctly.
It even works correctly if the same columns need to be updated eg.
from (values
(1051,1,'Electronic Devices',3),
(1052,2,'Topic 2',100)
)
Why is it not working correctly when I need to update different columns for each row?
When you provide the list of values as values (1051,1,'Electronic Devices',3),(1052,'OLD_VALUE','OLD_VALUE',100)), the first set of values is interpreted as the "template" of data types, and in this case (1051,1,'Electronic Devices',3), it's int, int, text, int. Then, any subsequent values provided, will be expected to have the same data type signature. When it parses (1052,'OLD_VALUE','OLD_VALUE',100), it sees int,text,text,int, which doesn't match the data type signature it expects, so it reports an error.
When you omit the first value and provide only (1052,'OLD_VALUE','OLD_VALUE',100), then it identifies int,text,text,int as the "template" data type signature, and it proceeds without complaint.

Update with ISNULL and operation

original query looks like this :
UPDATE reponse_question_finale t1, reponse_question_finale t2 SET
t1.nb_question_repondu = (9-(ISNULL(t1.valeur_question_4)+ISNULL(t1.valeur_question_6)+ISNULL(t1.valeur_question_7)+ISNULL(t1.valeur_question_9))) WHERE t1.APPLICATION = t2.APPLICATION;
I know you cannot update 2 tables in a single query so i tried this :
UPDATE reponse_question_finale t1
SET nb_question_repondu = (9-(COALESCE(t1.valeur_question_4,'')::int+COALESCE(t1.valeur_question_6,'')::int+COALESCE(t1.valeur_question_7)::int+COALESCE(t1.valeur_question_9,'')::int))
WHERE t1.APPLICATION = t1.APPLICATION;
But this query gaves me an error : invalid input syntax for integer: ""
I saw that the Postgres equivalent to MySQL is COALESCE() so i think i'm on the good way here.
I also know you cannot add varchar to varchar so i tried to cast it to integer to do that. I'm not sure if i casted it correctly with parenthesis at the good place and regarding to error maybe i cannot cast to int with coalesce.
Last thing, i can certainly do a co-related sub-select to update my two tables but i'm a little lost at this point.
The output must be an integer matching the number of questions answered to a backup survey.
Any thoughts?
Thanks.
coalesce() returns the first non-null value from the list supplied. So, if the column value is null the expression COALESCE(t1.valeur_question_4,'') returns an empty string and that's why you get the error.
But it seems you want something completely different: you want check if the column is null (or empty) and then subtract a value if it is to count the number of non-null columns.
To return 1 if a value is not null or 0 if it isn't you can use:
(nullif(valeur_question_4, '') is null)::int
nullif returns null if the first value equals the second. The IS NULL condition returns a boolean (something that MySQL doesn't have) and that can be cast to an integer (where false will be cast to 0 and true to 1)
So the whole expression should be:
nb_question_repondu = 9 - (
(nullif(t1.valeur_question_4,'') is null)::int
+ (nullif(t1.valeur_question_6,'') is null)::int
+ (nullif(t1.valeur_question_7,'') is null)::int
+ (nullif(t1.valeur_question_9,'') is null)::int
)
Another option is to unpivot the columns and do a select on them in a sub-select:
update reponse_question_finale
set nb_question_repondu = (select count(*)
from (
values
(valeur_question_4),
(valeur_question_6),
(valeur_question_7),
(valeur_question_9)
) as t(q)
where nullif(trim(q),'') is not null);
Adding more columns to be considered is quite easy then, as you just need to add a single line to the values() clause

How do I check if a column is NULL using rust-postgres? [duplicate]

This question already has an answer here:
How to handle an optional value returned by a query using the postgres crate?
(1 answer)
Closed 5 years ago.
I am using the rust-postgres library and I want to do a SELECT and check if the first column of the first row is NULL or not.
This is how I get my data:
let result = connection.query(
r#"
SELECT structure::TEXT
FROM sentence
WHERE id = $1
"#,
&[&uuid]
);
let rows = result.expect("problem while getting sentence");
let row = rows
.iter()
.next() // there's only 1 result
.expect("0 results, expected one...");
The only simple way I found to figure it out is the following code:
match row.get_opt(0) {
Some(Ok(data)) => some data found,
Some(Err(_)) => the column is null,
None => out of bound column index
}
Unfortunately, it seems that Some(Err(_)) is the executed path for any kind of SQL/database error, and not only if the retrieved column is NULL.
Which condition should I use to check that the column is NULL ?
If all you need to know is whether the column is NULL, you could try changing your query to:
SELECT COUNT(1) FROM sentence WHERE id = $1 AND structure IS NOT NULL
with or without the NOT.
If you want to make the logic simpler so any error is an actual error, I'd consider changing the select value to something like:
COALESCE( structure::TEXT, ''::TEXT ) AS "structure"
so it should never be NULL. That should work as long as an empty string isn't a valid non-NULL value for that column.
Otherwise, I may have misunderstood your problem.

Add value of subquery in Entity Framework

I have a really strange situation with the subquery.
Value of subquery is calculated but assigned value to property is different
var ozForAllViews = from oz in dbContext.oz
where
oz.TId == 6050
select
new ozForAllView
{
ozId = oz.ozId,
Ilosc = oz.Ilosc - (from pz in dbContext.pz
where
pz.Aktywny &&
pz.ozId == oz.ozId
select pz).Sum(z=> (decimal?) z.Ilosc) ?? 0
};
My property Ilosc is not calculated properly, value in DB equals 5.
But returned value is always 0.
Why is that oz.Ilosc is not subtracted from subquery?
I mean that 5 - 0 should equals 5.
You should surround the expression that calculates the subtracted amount by parentheses:
from oz in dbContext.oz
where oz.TId == 6050
select
new ozForAllView
{
ozId = oz.ozId,
Ilosc = oz.Ilosc - ((from pz in dbContext.pz
where
pz.Aktywny &&
pz.ozId == oz.ozId
select pz).Sum(z=> (decimal?) z.Ilosc) ?? 0)
};
This is because the query is translated into SQL. In SQL, if one part of an expression is null, the whole expression is null. Your query is translated such that oz.Ilosc - (from ... (decimal?) z.Ilosc) is evaluated as one expression. That part becomes null when the subtracted amount is null and hence it will be returned as 0.
It's confusing because in plain C# code, the behavior would be different. There the ?? 0 part would be applied to the subtracted amount only.