What is the best way to deal with nullable string columns in LinqToSql? - string

Assume you have a table with a nullable varchar column. When you try to filter the table, you would use (pFilter is parameter):
var filter = pFilter;
var dataContext = new DBDataContext();
var result = dataContext.MyTable.Where(x=>x.MyColumn == filter).ToList();
Now, what if there is a keyword that means "All Nulls". The code would look like:
var filter = pFilter != "[Nulls]" ? pFilter : null;
var dataContext = new DBDataContext();
var result = dataContext.MyTable.Where(x=>x.MyColumn == filter).ToList();
But this doesn't work. Apparently, a String with value of null is... not null?
However, what do work is this code:
var filter = pFilter != "[Nulls]" ? pFilter : null;
var dataContext = new DBDataContext();
var result = dataContext.MyTable.Where(x=>x.MyColumn == filter || (filter == null && x.MyColumn == null)).ToList();
The workaround did not convinced me, that's why my question is: What is the best way to deal with nullable string columns in LinqToSql?

Use String.Equals that will make LINQ handle null appropriately on the generated SQL query
var result = dataContext.MyTable
.Where(x => String.Equals(x.MyColumn, filter))
.ToList();
Edit:
If you use == LINQ will generate the query for the general case WHERE [column] = #parameter but on SQL NULL does not match NULL, the proper way to test for NULL is [column] IS NULL.
With String.Equals LINQ has enough information to translate the method to the appropiate sentence in each case, what means:
if you pass a non-null string it will be
WHERE ([column] IS NOT NULL) AND ([column] = #parameter)
and if it is null
WHERE [column] IS NULL

Related

Spark pass string condition and return column type

I have a function, myFilter that I want to do this:
If the mode parameter is 'daily', then filter the data frame by date using the parameters 'dt' and dB'. This works.
If the mode parameter is 'custom', then filter by a valid condition passed as a String to the function like this:
val filcond = "col(\"custId\")===\"1\""
myFilter(mode, filcond)
Since the function returns a Column type, I think I need to convert the string to Column. But I have yet to find a way to do that. Any ideas how to do this?
def myFilter(mode: String, filcond : String): Column = {
val filterCondition: Column =
if (mode == "daily") {
$"update_date" >= date_sub(to_date(date_format(lit(dt), "yyyy-MM-dd")), dB) and $"update_date" <= to_date(date_format(lit(dt), "yyyy-MM-dd"))
} else if (mode == "custom") {
//col("custId")==="1" //I want it to return this condition, but pass it in as a param to this function
filcond // this does not work since it is a String
}
return filterCondition
}
val myDf = ss.read
.parquet("/data")
.select (
$"Id",
$"Url",
$"Type",
$"custId",
to_date($"updateTimestamp","yyyy-MM-dd").as("update_date")
)
.filter( myFilter(mode,filcond) )

linq to entities: compare string variable with NVarchar field

This code results in timeout exception
String City_Code = null;
var result = (from r in myContext.TableA
where ( City_Code == r.City_Code )
select r ).ToList();
while this code will return quickly
String City_Code = null;
var result = (from r in myContext.TableA
where ( r.City_Code == City_Code )
select r ).ToList();
The difference is in the order of operands of the equality. The field City_Code of table TableA is of type nvarchar(20).
What is a possible reason for that???
update: The generated tsql for the 2 queries are identical except for that part: the first one has the condition " #p__linq__2 = [Extent1].[City_Code])" while the second has " [Extent1].[City_Code] = #p__linq__2)"
The value of #p_linq_2 when I notice the time difference is NULL. If I run the queries in ssms with NULL in place of #p_linq_2 they respond both very quickly

Null value comparision in slick

I encounter a problem of nullable column comparison.
If some columns are Option[T], I wonder how slick translate like === operation on these columns to sql.
There are two possibilities: null value( which is None in scala ) and non-null value.
In the case of null value, sql should use is instead of =.
However slick doesn't handle it correctly in the following case.
Here is the code (with H2 database):
object Test1 extends Controller {
case class User(id: Option[Int], first: String, last: String)
class Users(tag: Tag) extends Table[User](tag, "users") {
def id = column[Int]("id",O.Nullable)
def first = column[String]("first")
def last = column[String]("last")
def * = (id.?, first, last) <> (User.tupled, User.unapply)
}
val users = TableQuery[Users]
def find_u(u:User) = DB.db.withSession{ implicit session =>
users.filter( x=> x.id === u.id && x.first === u.first && x.last === u.last ).firstOption
}
def t1 = Action {
DB.db.withSession { implicit session =>
DB.createIfNotExists(users)
val u1 = User(None,"123","abc")
val u2 = User(Some(1232),"123","abc")
users += u1
val r1 = find_u(u1)
println(r1)
val r2 = find_u(u2)
println(r2)
}
Ok("good")
}
}
I print out the sql. It is following result for the first find_u.
[debug] s.s.j.J.statement - Preparing statement: select x2."id", x2."first", x2."last" from "users" x2 where (
(x2."id" = null) and (x2."first" = '123')) and (x2."last" = 'abc')
Notice that (x2."id" = null) is incorrect here. It should be (x2."id" is null).
Update:
Is it possible to only compare non-null fields in an automatic fashion? Ignore those null columns.
E.g. in the case of User(None,"123","abc"), only do where (x2."first" = '123')) and (x2."last" = 'abc')
Slick uses three-valued-logic. This shows when nullable columns are involved. In that regard it does not adhere to Scala semantics, but uses SQL semantics. So (x2."id" = null) is indeed correct under these design decisions. To to a strict NULL check use x.id.isEmpty. For strict comparison do
(if(u.id.isEmpty) x.id.isEmpty else (x.id === u.id))
Update:
To compare only when the user id is non-null use
(u.id.isEmpty || (x.id === u.id)) && ...

ServiceStack.OrmLite with a DateTime.Month Predicate

While using ServiceStack.OrmLite 3.9.70.0, and following some of the examples from the ServiceStack.OrmLite wiki.
I am trying to select rows where the LastActivity date month = 1.
I keep getting the error:
{"variable 'pp' of type 'Author' referenced from scope '', but it is not defined"}
LastActivity is a nullable DateTime, defind like:
public DateTime ? LastActivity { get; set;}
I have tried:
db.Select<Author>(q => q.LastActivity.Value.Month == 1);
AND
var visitor = db.CreateExpression<Author>();
db.Select<Author>(visitor.Where(q => q.LastActivity.Value.Month == 1));
AND
SqlExpressionVisitor<Author> ev = OrmLiteConfig.DialectProvider.ExpressionVisitor<Author>();
db.Select<Author>(ev.Where(q => q.LastActivity.Value.Month == 1));
AND
var predicate = ServiceStack.OrmLite.PredicateBuilder.True<Author>();
predicate = predicate.And(q => q.LastActivity.Value.Month == 1);
db.Select<Author>(predicate);
I am trying to avoid using a sql string in the select because I like the compile time checking of the field names and types.
do a less than and more than on the date field IE
LastActivity >= variableThatHoldsStartDateOfMonth && LastActivity <= VariableThatHoldsLastDayOfMOnth.
This will give you results for the whole month

Converting a LEFT OUTER JOIN to Entity Framework

Here is a SQL Query I want to convert to EF4.3
command = database.GetSqlStringCommand(#"
select
H.AUTHENTICATION_ID,
USERNAME,
PERMISSIONS,
ORGANIZATION_IDENTIFIER,
O.ORGANIZATION_ID
from
AUTHENTICATION H
left join [AUTHORIZATION] T on H.AUTHENTICATION_ID=T.AUTHENTICATION_ID
join ORGANIZATION O on O.ORGANIZATION_ID = T.ORGANIZATION_ID
order by H.AUTHENTICATION_ID");
Here is the best LINQ I could come up with:
var query = from h in context.Authentications
join t in context.Authorizations on h.AuthenticationId equals t.Authentications.AuthenticationId
join o in context.Organizations on t.Organizations.OrganizationId equals o.OrganizationId
orderby
h.AuthenticationId
select new
{ AUTHENTICATION_ID = (Int16?)h.AuthenticationId,
h.Username,
t.Permissions,
o.OrganizationIdentifier,
OrganizationID = (Int16?)o.OrganizationId
};
I know i need to merge my first join (between Authorizations & Authentications) into, lets say x and apply DefaultIfEmpty but can't make out the syntax.
EDIT: Image for clarification:
Any help will be highly appreciated. Regards.
The basic syntax for a "left join" in Linq is like this:
from x in table1
join y in table2 on x.id equals y.id into jointable
from z in jointable.DefaultIfEmpty()
select new
{
x.Field1,
x.Field2,
x.Field3,
Field4 = z == null ? 0 : z.Field4
};
In your case, I'm a little confused because the entity relations you seem to be using in your Linq don't match the ones implied by your SQL; are the relationships here zero-or-one, zero-or-many, one-to-one, etc? Specifically, you're doing this:
from h in context.Authentications
join t in context.Authorizations on h.AuthenticationId equals t.Authentications.AuthenticationId
but your SQL implies that "Authentication" is the parent here with zero-or-more "Authorization" children, not the other way around, which would be more like:
from h in context.Authentications
from t in h.Authorizations.DefaultIfEmpty()
If you can give us a better idea of the data model and what data you expect to get out of it we can more easily explain how that query would look in Linq. Assuming that your relationships match what is implied by the SQL, you should be able to get what you want using the following Linq queries:
var query = from h in context.Authentications
from t in h.Authorizations.DefaultIfEmpty()
select new
{
h.AuthenticationId,
h.Username,
Permissions = t == null ? null : t.Permissions,
Organizations = t == null ? new EntitySet<Organization>() : t.Organizations
};
var query2 = from x in query
from o in x.organizations.DefaultIfEmpty()
select new
{
AUTHENTICATION_ID = (short?)x.AuthenticationId,
x.Username,
x.Permissions,
OrganizationIdentifier = o == null ? null : o.OrganizationIdentifier,
OrganizationID = o == null ? (short?)null : o.OrganizationID
};
Given the foreign keys that exist in the question diagram, how about something like this?
var query = from a in context.Authentications
select new
{
a.AuthenticationID,
a.Username,
a.Authorisations.Permissions ?? false,
a.Authorisations.Organisations.OrganisationIdentifier ?? 0
a.Authorisations.Organisations.OrganisationID ?? 0
};
I went ahead and moved the entire query to a Stored Procedure on the database. This solves the problem by avoiding LINQ and ObjectBuilder in the first place.

Resources