Linq.Where-to-SQL on a text field comparing to a list of values - sql

Customer.text is a field in an T-SQL DB (that I do not control and thus may not alter) of type "text".
I'd like to do something like this:
List<string> compare = new List<string>();
compare.Add("one");
compare.Add("two");
var q = from t in customer
where t.text.Contains( compare.First())
select t;
this will work.
But now I'd like to do something like: (!NOT WORKING!)
var q = from t in customer
where compare.Contains( t.text )
select t;
How can I achieve this? Is it even possible?
EDIT: The problem is obviously not exactly clear: A text column in SQL cannot be queried using "=" but only with LIKE. Thus the compare.Contains( t.text ) will result in an error, as it is converted into a query using "=".
What I did not tell - I thought it is irrelevant - is, that I use LINQ-to-ORM (LLBLGen in this case).
What I tried instead:
var q = from t in customer
where compare.Any( x => t.text.Contains(x) )
select t;
Now this did not work also. Currently I'm not at work, but the exception was something with a ConstantExpression not being convertable into a SetExpression.
I hope this gave some clarification.
EDIT2:
Joseph pointed this out to me: PredicateBuilder. It creates an Expression on a given ObjectType.
Now my problem is, that my type is an anonymous type out of multiple joins.
Is there an easy or elegant way to handle this?

Now I might be missing something, but your code looks like it should work. Did you include the namespaces at the top of the file?
using System.Linq;
using System.Linq.Expressions;
You could also rewrite it without the Linq2Sql syntax, like:
var q = customer.Where(c => compare.Contains(c.text));

You could build your query using LinqKit's free predicate builder class. Here is a blog post which describes its use and has a link to the download site.
http://thecodeslinger.wordpress.com/2008/10/28/linqkit-predicatebuildert-goodness/
Below is a code sample from the post
//First get a list of keywords that match the description entered.
string[] parts = txtInclude.Text.Split(new[] {‘ ‘});
string[] noparts = null;
if(txtButNot.Text.Trim().Length > 0)
noparts = txtExclude.Text.Trim().Split(new[] {‘ ‘});
var pred = PredicateBuilder.True<Pet>();
//here is where you would loop through your compare object
parts.ForEach(p => pred = pred.And(pl => pl.description.Contains(p)));
if(noparts != null)
noparts.ForEach(p => pred = pred.And(pl => !pl.description.Contains(p)));
var pets = from s in db.Pets.Where(pred)
select s;

You have to convert the text field to string
var query = from t in dataContext.table
where compare.Contains(t.textField.ToString())
select t;

Related

QueryDSL like operation SimplePath

Similarly to this question I would like to perform an SQL "like" operation using my own user defined type called "AccountNumber".
The QueryDSL Entity class the field which defines the column looks like this:
public final SimplePath<com.myorg.types.AccountNumber> accountNumber;
I have tried the following code to achieve a "like" operation in SQL but get an error when the types are compared before the query is run:
final Path path=QBusinessEvent.businessEvent.accountNumber;
final Expression<AccountNumber> constant = Expressions.constant(AccountNumber.valueOfWithWildcard(pRegion.toString()));
final BooleanExpression booleanOperation = Expressions.booleanOperation(Ops.STARTS_WITH, path, constant);
expressionBuilder.and(booleanOperation);
The error is:
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [7!%%] did not match expected type [com.myorg.types.AccountNumber (n/a)]
Has anyone ever been able to achieve this using QueryDSL/JPA combination?
Did you try using a String constant instead?
Path<?> path = QBusinessEvent.businessEvent.accountNumber;
Expression<String> constant = Expressions.constant(pRegion.toString());
Predicate predicate = Expressions.predicate(Ops.STARTS_WITH, path, constant);
In the end, I was given a tip by my colleague to do the following:
if (pRegion != null) {
expressionBuilder.and(Expressions.booleanTemplate("{0} like concat({1}, '%')", qBusinessEvent.accountNumber, pRegion));
}
This seems to do the trick!
It seems like there is bug/ambiguity. In my case, I need to search by couple fields with different types (String, Number), e.g. SQL looks like:
SELECT * FROM table AS t WHERE t.name = "%some%" OR t.id = "%some%";
My code looks like:
BooleanBuilder where = _getDefaultPredicateBuilder();
BooleanBuilder whereLike = new BooleanBuilder();
for(String likeField: _likeFields){
whereLike = whereLike.or(_pathBuilder.getString(likeField).contains(likeValue));
}
where.and(whereLike);
If first _likeFields is type of String - request works fine, otherwise it throws Exception.

How to build the correct LINQ query (generate OR instead of AND)

I need some help in building the correct query. I have an Employees table. I need to get a list of all employees, that EENO (Employee ID) contains a string from a supplied array of partial Employee IDs.
When I use this code
// IEnumerable<string> employeeIds is a collection of partial Employee IDs
IQueryable<Employee> query = Employees;
foreach (string id in employeeIds)
{
query = query.Where(e => e.EENO.Contains(id));
}
return query;
I will get something like:
SELECT *
FROM Employees
WHERE EENO LIKE '%1111111%'
AND EENO LIKE '%2222222%'
AND EENO LIKE '%3333333%'
AND EENO LIKE '%4444444%'
Which doesn't make sense.
I need "OR" instead of "AND" in resulting SQL.
Thank you!
UPDATE
This code I wrote using PredicateBuilder works perfectly when I need to include these employees.
var predicate = PredicateBuilder.False<Employee>();
foreach (string id in employeeIds)
{
var temp = id;
predicate = predicate.Or(e => e.EENO.Contains(temp));
}
var query = Employees.Where(predicate);
Now, I need to write an opposite code, to exclude these employees,
here it is but it is not working: the generated SQL is totally weird.
var predicate = PredicateBuilder.False<Employee>();
foreach (string id in employeeIds)
{
var temp = id;
predicate = predicate.And(e => !e.EENO.Contains(temp)); // changed to "And" and "!"
}
var query = Employees.Where(predicate);
return query;
It's supposed to generate SQL Where clause like this one:
WHERE EENO NOT LIKE '%11111%'
AND NOT LIKE '%22222%'
AND NOT LIKE '%33333%'
But it's not happening
The SQL generated is this: http://i.imgur.com/9MDP7.png
Any help is appreciated. Thanks.
Instead of the foreach, just build the IQueryable once:
query = query.Where(e => employeeIds.Contains(e.EENO));
I'd take a look at http://www.albahari.com/nutshell/predicatebuilder.aspx. This has a great way of building Or queries, and is written by the guy that wrote LinqPad. The above link also has examples of usage.
I believe you can use Any():
var query = Employees.Where(emp => employeeIds.Any(id => id.Contains(emp.EENO)));
If you don't want to use a predicate builder, then the only other option is to UNION each of the collections together on an intermediate query:
// IEnumerable<string> employeeIds is a collection of partial Employee IDs
IQueryable<Employee> query = Enumerable.Empty<Employee>().AsQueryable();
foreach (string id in employeeIds)
{
string tempID = id;
query = query.Union(Employees.Where(e => e.EENO.Contains(tempID));
}
return query;
Also keep in mind that closure rules are going to break your predicate and only end up filtering on your last criteria. That's why I have the tempID variable inside the foreach loop.
EDIT: So here's the compendium of all the issues you've run across:
Generate ORs instead of ANDS
Done, using PredicateBuilder.
Only last predicate is being applied
Addressed by assigning a temp variable in your inner loop (due to closure rules)
Exclusion predicates not working
You need to start with the correct base case. When you use ORs, you need to make sure you start with the false case first, that way you only include records where AT LEAST ONE predicate matches (otherwise doesn't return anything). The reason for this is that the base case should just get ignored for purposes of evaluation. In other words false || predicate1 || predicate2 || ... really is just predicate1 || predicate2 || ... because you're looking for at least one true in your list of predicates (and you just need a base to build on). The opposite applies to the AND case. You start with true so that it gets "ignored" for purposes of evaluation, but you still need a base case. In other words: true && predicate1 && ... is the same as predicate1 && .... Hope that addresses your last issue.

checking if all the items in list occur in another list using linq

I am stuck with a problem here. I am trying to compare items in a list to another list with much more items using linq.
For example:
list 1: 10,15,20
list 2: 10,13,14,15,20,30,45,54,67,87
I should get TRUE if all the items in list 1 occur in list 2. So the example above should return TRUE
Like you can see I can't use sequenceEquals
Any ideas?
EDIT:
list2 is actually not a list it is a column in sql thas has following values:
<id>673</id><id>698</id><id>735</id><id>1118</id><id>1120</id><id>25353</id>.
in linq I did the following queries thanks to Jon Skeets help:
var query = from e in db
where e.taxonomy_parent_id == 722
select e.taxonomy_item_id;
query is IQueryable of longs at this moment
var query2 = from e in db
where query.Contains(e.taxonomy_item_id)
where !lsTaxIDstring.Except(e.taxonomy_ids.Replace("<id>", "")
.Replace("</id>", "")
.Split(',').ToList())
.Any()
select e.taxonomy_item_id;
But now I am getting the error Local sequence cannot be used in LINQ to SQL implementation of query operators except the Contains() operator.
How about:
if (!list1.Except(list2).Any())
That's about the simplest approach I can think of. You could explicitly create sets etc if you want:
HashSet<int> set2 = new HashSet<int>(list2);
if (!list1.Any(x => set2.Contains(x)))
but I'd expect that to pretty much be the implementation of Except anyway.
This should be what you want:
!list1.Except(list2).Any()
var result = list1.All(i => list2.Any(i2 => i2 == i));

SELECT MAX query returns only 1 variable + codeigniter

I use codeigniter and have an issue about SELECT MAX ... I couldnot find any solution at google search...
it looks like it returns only id :/ it's giving error for other columns of table :/
Appreciate helps, thanks!
Model:
function get_default()
{
$this->db->select_max('id');
$query = $this->db->getwhere('gallery', array('cat' => "1"));
if($query->num_rows() > 0) {
return $query->row_array(); //return the row as an associative array
}
}
Controller:
$default_img = $this->blabla_model->get_default();
$data['default_id'] = $default_img['id']; // it returns this
$data['default_name'] = $default_img['gname']; // it gives error for gname although it is at table
To achieve your goal, your desire SQL can look something like:
SELECT *
FROM gallery
WHERE cat = '1'
ORDER BY id
LIMIT 1
And to utilise CodeIgniter database class:
$this->db->select('*');
$this->db->where('cat', '1');
$this->db->order_by('id', 'DESC');
$this->db->limit(1);
$query = $this->db->get('gallery');
That is correct: select_max returns only the value, and no other column. From the specs:
$this->db->select_max('age');
$query = $this->db->get('members');
// Produces: SELECT MAX(age) as age FROM members
You may want to read the value first, and run another query.
For an id, you can also use $id = $this->db->insert_id();
See also: http://www.hostfree.com/user_guide/database/active_record.html#select
CodeIgniter will select * if nothing else is selected. By setting select_max() you are populating the select property and therefore saying you ONLY want that value.
To solve this, just combine select_max() and select():
$this->db->select('somefield, another_field');
$this->db->select_max('age');
or even:
$this->db->select('sometable.*', FALSE);
$this->db->select_max('age');
Should do the trick.
It should be noted that you may of course also utilize your own "custom" sql statements in CodeIgniter, you're not limited to the active record sql functions you've outlined thus far. Another active record function that CodeIgniter provides is $this->db->query(); Which allows you to submit your own SQL queries (including variables) like so:
function foo_bar()
{
$cat = 1;
$limit = 1;
$sql = "
SELECT *
FROM gallery
WHERE cat = $cat
ORDER BY id
LIMIT $limit
";
$data['query'] = $this->db->query($sql);
return $data['query'];
}
Recently I have been utilizing this quite a bit as I've been doing some queries that are difficult (if not annoying or impossible) to pull off with CI's explicit active record functions.
I realize you may know this already, just thought it would help to include for posterity.
2 helpful links are:
http://codeigniter.com/user_guide/database/results.html
http://codeigniter.com/user_guide/database/examples.html

Fetch Single Item Using DataContext

I'm doing the following:
public MyItem FetchSingleItem(int id)
{
string query = "SELECT Something FROM Somewhere WHERE MyField = {0}";
IEnumerable<MyItem> collection = this.ExecuteQuery<MyItem>(query, id);
List<MyItem> list = collection.ToList<MyItem>();
return list.Last<MyItem>();
}
It's not very elegant really and I was hoping there's something a little better to get a single item out using DataContext. I'm extending from DataContext in my repository. There's a valid reason why before you ask, but that's not the point in this question ;)
So, any better ways of doing this?
Cheers
If it is SQL Server, change your SQL to:
SELECT TOP 1 Something FROM Somewhere ...
Or alternatavely, change these lines
List<MyItem> list = collection.ToList<MyItem>();
return list.Last<MyItem>();
into this one:
return collection.First();
myDataContext.MyItem.Where(item => item.MyField == id)
.Select(item => item.Something)
.FirstOrDefault();
The record returned is undefined, since you have no ORDER BY. So it's hard to do an exact translation. In general, though, reverse the order and take the First():
var q = from s in this.Somewhere
where s.MyField == id
orderby s.Something desc
select s.Something;
return q.First();
Relational tables are unordered. So if you don't specify the record you want precisely, you must consider the returned record as randomly selected.