Text search like SQL COLLATE Latin1_General_CI_AI with LINQ - c#

I can search customers table without any problems by SQL.
select Name
from Customers
where Name COLLATE Latin1_General_CI_AI like '%ozgur%'
This query can find "özgür"
When I place this table to cache and try to search this table with linq, I can't find "özgür" by "ozgur" search word.
Is there any similar way to use Latin1_General_CI_AI in C# LINQ?

The only place I've found that uses a collation is Entity SQL's ORDER BY clause.
You could use SqlQuery as shown here to use a SQL string (with parameters of course) that uses the COLLATE clause :
var query = "select Name from Customers " +
" where Name COLLATE Turkish_CI_AI like #name";
var results = myContext.Customers
.SqlQuery(query,new SqlParameter("#name","%ozgur%"))
.ToList();
I'd advise caution though. LIKE '%...%' can't benefit from any indexes that cover the name field and will have to search the entire table. Even Name = #name COLLATE ... may not use any indexes the collation doesn't match the collation the index was built with.
You should consider using full text search indexes and make full text search queries for specific words, eg:
SELECT Name from Customers WHERE CONTAINS(Name ,#thatName)
Update
Another option is to use an interceptor to change the SQL generated by a clause, as shown in this SO question. That interceptor uses a regular expression to replace LIKE with CONTAINS. A simpler expression could be used to inject the COLLATE clause before LIKE
The code isn't trivial, but at least it's an option.

Why don't you use following filer clause for unicode values
Converting the collation type will cause performance issues and prevent index usage
select Name
from Customers
where Name like N'%özgür%'

You can solve this by using a normal query (via context.Database.SqlQuery) that returns an array of ids and use this ids in your linq statement.

Related

using COLLATE in Linq to SQL

I need to add Collation into ORDER BY clause. In other words, need to create IQueryable which will be interpteted something like:
SELECT column FROM table
ORDER BY [column] COLLATE *AnyCollation* DESC
Is it possible?
As far as I know you cannot change the collation using the LINQ. However there are some workarounds like creating a function in SQL Server and changing the collation there and then using it your code.
Here is a related thread which you can refer: Using collation in Linq to Sql

can dapper replace the table name?

I had expected that dapper-dot-net could replace the table name in a query like this:
connection.Query("SELECT * FROM #Table WHERE [Id] = #Id", new {Table = tb, Id = id});
However, it seems to not replace the table name. Is that an expected limitation?
With the single exception of "in" (where dapper offers some voodoo), dapper is a direct ADO.NET tool - it doesn't change the query. So the real question is: can you parameterize a table name in SQL? In every database I know of: no you cannot - so that is not valid. Dapper doesn't attempt to solve that issue.
Perhaps consider string.Format, remembering:
to white-list the legal table-names to prevent SQL injection
to use the full [square brackets] notation around the table name to allow the full range of possible names

Is there any way to get the table hierarchy from a connection string in c#?

I have a current requirement to determine the table hierarchy from a sql statement within c#. For example, consider the following sql statement:
Select Table1.*, Table2.* from Table1
left join table2 on Table1.parentCol = Table2.childCol
That might return 7 columns, 3 for Table1 and 4 for table2. I need to know the column names, and ideally (though not mandatory) their types.
I have no control over what SQL Statement will be used, as this is a user entered field. In C# it's a very basic task to open a connection and create an SqlCommand using that statement. I have freedom to run the SQL into a SqlDataReader, or any other System.Data.SqlClient class if necessary, however I cannot find any combination that will return the columns, rather than the actual column values.
Is anyone able to help?
Many thanks and best regards
You cannot do what you are asking (easily).
More to the point, do not let users enter arbitrary TSQL (You will regret it at some point...).
Instead, create a 'Search' form that allows entering various params and use a parameterised query onto a view that joins all the tables/columns required.
There's no direct way. You'll need to parse names of all the tables from the sql query.
Once you have done that you'll need to write few queries on Information_Schema to get raw data for what you are looking for.
If you are on SQL Server, you may want to use Catalog View
ex-
Select * from sys.tables where [Name] = 'MyTable'

How to read the result of SELECT * from joined tables with duplicate column names in .NET

I am a PHP/MySQL developer, slowly venturing into the realm of C#/SQL Server and I am having a problem in C# when it comes to reading an SQL Server query that joins two tables.
Given the two tables:
TableA:
int:id
VARCHAR(50):name
int:b_id
TableB:
int:id
VARCHAR(50):name
And given the query
SELECT * FROM TableA,TableB WHERE TableA.b_id = TableB.id;
Now in C# I normally read query data in the following fashion:
SqlDataReader data_reader= sql_command.ExecuteReader();
data_reader["Field"];
Except in this case I need to differentiate from TableA's name column, and TableB's name column.
In PHP I would simply ask for the field "TableA.name" or "TableB.name" accordingly but when I try something like
data_reader["TableB.name"];
in C#, my code errors out.
How can fix this? And how can I read a query on multiple tables in C#?
The result set only sees the returned data/column names, not the underlying table. Change your query to something like
SELECT TableA.Name as Name_TA, TableB.Name as Name_TB from ...
Then you can refer to the fields like this:
data_reader["Name_TA"];
To those posting that it is wrong to use "SELECT *", I strongly disagree with you. There are many real world cases where a SELECT * is necessary. Your absolute statements about its "wrong" use may be leading someone astray from what is a legitimate solution.
The problem here does not lie with the use of SELECT *, but with a constraint in ADO.NET.
As the OP points out, in PHP you can index a data row via the "TABLE.COLUMN" syntax, which is also how raw SQL handles column name conflicts:
SELECT table1.ID, table2.ID FROM table1, table;
Why DataReader is not implemented this way I do not know...
That said, a solution to be used could build your SQL statement dynamically by:
querying the schema of the tables you're selecting from
build your SELECT clause by iterating through the column names in the schema
In this way you could build a query like the following without having to know what columns currently exist in the schema for the tables you're selecting from
SELECT TableA.Name as Name_TA, TableB.Name as Name_TB from ...
You could try reading the values by index (a number) rather than by key.
name = data_reader[4];
You will have to experiment to see how the numbers correspond.
Welcome to the real world. In the real world, we don't use "SELECT *". Specify which columns you want, from which tables, and with which alias, if required.
Although it is better to use a column list to remove duplicate columns, if for any reason you want *****, then just use
rdr.item("duplicate_column_name")
This will return the first column value, since the inner join will have the same values in both identical columns, so this will accomplish the task.
Ideally, you should never have duplicate column names, across a database schema. So if you can rename your schema to not have conflicting names.
That rule is for this very situation. Once you've done your join, it is just a new recordset, and generally the table names do go with it.

Is it possible to use query parameters to fill the IN keyword

Imagine a table with GUIDs as primary key. I would like to select a few of these rows based on their primary key. I would like to use a query like:
SELECT * FROM mytable WHERE id IN ('firstguidhere','secondguidhere');
I am using ADO.NET to query the database, so I would like to use a parametrized query instead of dynamic sql, which would obviously work, but I want to retain the benefits of parametrized queries (security, escaping, etc...).
Is it possible to fill the collection for the IN-clause using sql-parameters?
You could pass the list of GUIDs as a comma-separated string parameter and use a table-valued UDF to split them into a table to use in your IN clause:
SELECT *
FROM my_table
WHERE id IN (SELECT id FROM dbo.SplitCSVToTable(#MyCSVParam))
Erland Sommarskog has an interesting article with examples of how to split comma-separated strings into tables using a UDF.
(For performance reasons, you should ensure that your UDF is inline table-valued, rather than multi-statement.)

Categories