Subsonic query: issue with the produced query - c#

I'm having a problem with subsonic query. The deal is, I have a view and I want to query it's data to produce something like the following SQL statement:
select *
from myView
where (col1 like '%a%' or col2 like '%a%' or col3 like '%a%')
and col4 = 1 and col5 = 2
But instead the query that is submited to the DB is something like this:
select *
from myView
where col1 like '%a%' or col2 like '%a%' or col3 like '%a%'
and col4 = 1 and col5 = 2
Is there a way to do something like the fisrt query?
Please note I'm using .net 2.0 and subsonic 2.2
Thank you in advance.
Even do, Subsonic rules!

You need to use the Constraint Expressions: WhereExpression, AndExpression, OrExpression, EndExpression.
Anything following “WhereExpression” (or Or/AndExpression) will be wrapped in parentheses. You can close the expression by using “CloseExpression()”, or it will be closed for you if another is started (as with OrExpression above) or if the query ends.
Also see: Using Nested Where/And/Or.

If you post your current code maybe I will be able to give more specific answer but basically you have to start WhereExpression.
For example code like this:
var usersQuery = new Select(new SubSonic.TableSchema.TableColumn[] { User.UserIdColumn, User.NameColumn })
.From(User.Schema)
.WhereExpression(User.UserIdColumn.ColumnName).IsEqualTo(1).Or(User.UserIdColumn).IsEqualTo(2)
.CloseExpression()
.And(User.NameColumn).Like("a")
Gives query like:
SELECT [dbo].[Users].[UserId], [dbo].[Users].[Name]
FROM [dbo].[Users]
WHERE ([dbo].[Users].[UserId] = #UserId0 OR [dbo].[Users].[UserId] = #UserId)
AND [dbo].[Users].[Name] LIKE #Name

Related

SQL "IN" statement in linq query mistake, how resolve?

I have this query in SQL:
SELECT *
FROM TableName
WHERE myData IN (SELECT MAX(myData) AS DATA_MAX
FROM TableName
GROUP BY id1, id2)
I want replicate it in Linq (c#) - how can I do that?
This isn't really a direct answer because it doesn't implement it via LINQ; but it does solve the problem, with the minimum amount of fuss:
You can use tools like "Dapper" to execute raw queries without involving any LINQ. If you're using something like LINQ-to-SQL or Entity Framework, the data-context there also usually has a raw query API that you can use, but I'm going to show a "Dapper" implementation:
class SomeType
{
// not shown: properties that look like the columns
// of [TableName] in the database - correct names/types
}
...
var data = connection.Query<SomeType>(#"
SELECT * FROM TableName
WHERE myData IN (Select max(myData) as DATA_MAX from TableName group
by id1, id2)").AsList();
This approach makes it very easy to migrate existing SQL queries without having to rewrite everything as LINQ.
If you are using LINQ-to-SQL, DataContext has a similiar ExecuteQuery<TResult> method. Entity Framework has a SqlQuery method
Long story short - don't use LINQ, optimize the query and use a microORM like Dapper to map results to classes :
var query = "Select * "
"from ( select *, " +
" ROW_NUMBER() OVER (partition by id1,id2 order by mydata desc) AS RN " +
" From TableName ) T " +
"where RN=1";
var data = connection.Query<SomeType>(query);
LINQ isn't a replacement for SQL. ORMs in general aren't meant to write reporting queries like this one.
Reporting queries need a lot of optimization and usually have to change in production. You don't want to have to redeploy your application each time a query changes. In this case it's far better to create a view and map to it using a microOMR like Dapper.
This specific query could require two table scans, one to calculate the maximum per id1,id2 and one to find the rows with matching mydata. The intermediate data would have to be spooled into tempdb too. If mydata is covered by an index, it may not be such an expensive query. If it isn't, all the data will be scanned twice.
An alternative is to calculate the ranking of each row by mydata based on id1, id2. You can do this with one of the ranking functions like ROW_NUMBER, RANK, NTILE.
Select *
from ( select *,
ROW_NUMBER() OVER (partition by id1,id2 order by mydata desc) AS RN
From TableName) T
where RN=1
You can use that query directly with Dapper or create a view and map your entities to the view, not the table itself.
One option would be to crate a MyTableRanked view :
CREATE VIEW MyTableRanked AS
select *,
ROW_NUMBER() OVER (partition by id1,id2 order by mydata desc) AS RN
From TableName
This would allow you to write :
var query="Select * from MyTableRanked where RN=#rank";
var data = connection.Query<SomeType>(query,new {rank=2});
Allowing you to return the top N records per ID1,ID2 combination
You can try this. May be it will work.
var myData = (from c in _context.TableName
group c by new
{
c.id1,
c.id2
} into gcs
select new
{
gcs.Max(p=>p.myData)
}).AsQueryable();
var result = (from t in _context.TableName
where myData.Contains(t.myData)
select t).ToList();

How filter datatable using Like same SQL query

In sql server, I have a query:
SELECT * FROM [tableName] WHERE [colName] LIKE '%abc[xyz]%'.
It same query:
SELECT * FROM [tableName] WHERE [colName] LIKE '%abcx%' OR [colName] LIKE '%abcy%' OR [colName] LIKE '%abcz%'
In C#, I using DataTable.SELECT("colName LIKE '%abc[xyz]%'") => error.
How resolve it?
Are you using linq?
With linq you could write:
Datatable.Select(x=>x.ColName.Contains("abcx.ColName")||x.ColName.Contains("abcy")||x.ColName.Contains("abcz"));
Try this:
DataTable.SELECT("colName LIKE '%abcx%' OR colName LIKE '%abcy%' OR colName LIKE '%abcz%'")

LinqDataSource: How to assign IQueryable value to where parameters in code

I am trying to assign linq datasource in code behind but I have IQueryable query want to assign in where clouse using Any function like a sub query clause in sql
this is my sql statment
select * from table1 where col1 in (select col1 from table1 where col2 like '%xx%')
how to convert this clouse to bind it into linq datasource code behind
You can convert this query in linq.
var result = from c in db.table1
where db.table1.Any(e => e.col2.Contain("xx"))
select c;
It sounds like you need to call .ToList() on the query that returns IQueryable.

How to make parameterized query involving multiple tables with ADO.NET / C#?

I would like to make parameterized query for SQLquery that can be something like this:
SELECT * FROM Table1 WHERE Col1 IN (SELECT Col2 FROM Table2 WHERE Col3 IN (1, 2, 3));
The values come from WS interface and although I can trust the consumers I want to play it safe and use parameterized query involving DbParameters to prevent SQL injection. How to do it?
Thanks!
The key point, as you note, is to use parameters. IN clauses are notoriously problematic for that, annoyingly. Now, if you know the values are all integers (for example you are taking an int[] parameter to your C# method), then you can just about get away with things like:
cmd.CommandText = "SELECT * FROM Table1 WHERE Col1 IN (SELECT Col2 FROM Table2 WHERE Col3 IN ("
+ string.Join(",", values) + "))"; // please don't!!!
It is horrible, suggests a very bad practice (if somebody copies it for strings, you are in a world of pain), and can't use query plan caching. You could do something like:
var sb = new StringBuilder("SELECT * FROM Table1 WHERE Col1 IN (SELECT Col2 FROM Table2 WHERE Col3 IN (");
int idx = 0;
foreach(var val in values) {
if(idx != 0) sb.Append(',');
sb.Append("#p").Append(idx);
cmd.Parameters.AddWithValue("#p" + idx, val);
idx++
}
sb.Append("))");
cmd.CommandText = sb.ToString();
Which is preferable but awkward.
Or simplest: with a tool like dapper, let the library worry about it:
var data = conn.Query<YourType>(
"SELECT * FROM Table1 WHERE Col1 IN (SELECT Col2 FROM Table2 WHERE Col3 IN #values)",
new { values });
Here dapper spots the usage and "does the right thing". It also handles the "0 values" case for you.
Here is one example:
SqlCommand cmd = new SqlCommand("SELECT * FROM Table1 WHERE Col1 IN (SELECT Col2 FROM Table2 WHERE Col3 = #myparam", conn);
//define parameters used in command object
SqlParameter param = new SqlParameter();
param.ParameterName = "#myparam";
param.Value = "myvalue";
//add new parameter to command object
cmd.Parameters.Add(param);
// get data stream
reader = cmd.ExecuteReader();
Check out this link for more details:
http://csharp-station.com/Tutorial/AdoDotNet/Lesson06
The difficulty here is parameterizing each individual IN clause so that you can pass through a variable number of IDs.
Have a look at this useful article as to one approach to solve this: http://www.mikesdotnetting.com/Article/116/Parameterized-IN-clauses-with-ADO.NET-and-LINQ
Basically, it involves a little bit of string manipulation to build up the parameterized list of IN clauses, so you end up with a SQL statement that looks like this for your particular example with 3 IDs:
select * from TABLE1 where COL1 in (select COL2 from TABLE2 where COL3 IN (#p1, #p2, #p3));

Common Table Expression in EntityFramework

I have this query in Sql Server which I need to consume in EntityFramework, So how can I write a EntityFramwork code which will have the same result as this
WITH cte AS
(
SELECT *
FROM StockGroups
WHERE GroupParent ='Stationery'
UNION ALL
SELECT g.*
FROM StockGroups g
JOIN cte
ON g.GroupParent = cte.GroupName
)
SELECT *
FROM cte
I don't know how to convert it in EF, so I tried with join.
from a in db.StockGroups
join b in db.StockGroups on new { GroupParent = a.GroupParent } equals new { GroupParent = b.GroupName }
where
b.GroupName == "Stationery"
select new {
a.GroupName,
a.GroupParent,
Column1 = b.GroupName,
Column2 = b.GroupParent
}
But the result is not same, as recursive as CTE.
EF does not support recursive CTE's. Use a view or a table valued function.
Getting input from the other experts over SO, I have come up with my own way to achieve this.
IEnumerable<StockGroup> sg = dbContext.ExecuteStoreQuery<StockGroup>(
#"WITH q AS
(
SELECT *
FROM LedgerGroups
WHERE GroupParent = 'Customers'
UNION ALL
SELECT m.*
FROM LedgerGroups m
JOIN q
ON m.GroupParent = q.GroupName
)
SELECT *
FROM q
");
You cannot use CTE recursion in Entity Framework.
Use stored procedure and call that stored procedure through EF
I dont think there is support for recursive CTEs in LINQ nor in EF. The solution is to expose the CTE as a view. The article on Recursive or hierarchical queries using EF Code First and Migrations shows how to deploy such a view using EF code first migrations. Recursive or hierarchical queries using EF Code First and Migrations
Original source:
https://stackoverflow.com/a/11929928/3850405

Categories