Querying Numeric TableName fails - c#

I am able to query the table named "012012" within SQL, but when attempting to query it from a C# application, it will say incorrect syntax near '012012'. Within SQL I would use double quotes to query this table as it doesn't work without them. Here is the code I am using:
string query = string.format("SELECT rec FROM '"+012012+"' WHERE cust = 'custname';");

If you're using SQL Server, then it does not allow un-escaped identifiers to start with numbers. So, you must use brackets like so [012012]. However, this is only part of your problem. The other part is that you are trying to use a numeric literal, and convert it to a string, but this number starts with a 0. This will get truncated by default and just become 12012. So, your best bet is to just do this:
string query = string.format("SELECT rec FROM [{0:D6}] WHERE cust = 'custname';", 012012);
The {0:D6} tells string.format to make the decimal field 6 characters wide, and pad 0's if it's shorter (which it would otherwise be 5 characters).
In this case, however, you probably don't even need to do that.. unless you actually need to derive the table name from a number, and you can just do this:
string query = "SELECT rec FROM [012012] WHERE cust = 'custname';";
I would also strongly advise against even starting to write code like this, as it is prone to SQL Injection vulnerability, you should always use parameterized queries and prepared statements. They're more work, but they are far safer.
Learning to write SQL code like this will form bad habits, which can be very dangerous later in your career.

You can't have a table name as '012012'. You don't even to use string.Format in your case. It will be useless.
If you wanna use string.Format with your table name, you can do it like;
string query = string.format("SELECT rec FROM [{0}] WHERE cust = 'custname';", "012012");

Try using square brackets:
string query = string.format("SELECT rec FROM ["+012012+"] WHERE cust = 'custname';");
Brackets are required if you use keywords or special chars in the column names or identifiers.

Related

How do I compress a SQL query string to a small string less than 258 chars?

I want to create a unique small string <= 258 chars that is suitable as a windows filename.
This is to uniquely label a Xml query result.
Here is a sample query:
SELECT * FROM ( SELECT [utcDT],
MAX(CASE WHEN[Symbol] = 'fish' THEN[Close] END) AS [fish],
MAX(CASE WHEN[Symbol] = 'chips' THEN[Close] END) AS [chips]
FROM [DATA].[1M].[ASTS_NOGAP]
WHERE [Date] >= '2011-12-27'
AND [Date] <= '2012-07-01'
AND [Symbol] IN ('fish','chips')
GROUP BY [utcDT] ) AS A
WHERE [utcDT] IS NOT NULL AND [fish] IS NOT NULL AND [chips] IS NOT NULL
ORDER BY [utcDT]
BUT is could be a longer query.
The compress is one way only, i.e. I do NOT need to decompress.
I want to end up with a unique file name like:
ksdgfsbhdfjksgdjbajysjdgyasagfdjahgdkjasgjgfjkgjkgdjkfgjskdjfgsajgdjfgjsgy.xml
EDIT1:
The generated filename must be unique to the query - such that another
app would generate the same filename for the same query.
How can I achieve this?
There is a small risk for collisions, but this should do what you need:
public string GetUniqueFileNameForQuery(string sql)
{
using (var hasher = SHA256.Create())
{
var queryBytes = Encoding.UTF8.GetBytes(sql);
var queryHash = hasher.ComputeHash(queryBytes);
// "/" may be included, but is not legal for file names
return Convert.ToBase64String(queryHash).Replace("/", "-")+".xml";
}
}
This needs using System.Security.Cryptography; at the top of the file.
I also need to add a note about working with SQL from client code languages like C#.
Most queries are going to need input of some kind: an ID field for a lookup, a date range, a username, something to tell the query which records you need out of a larger set. It's very poor practice to substitute these inputs directly into the SQL string in your C# (or other language) code. That opens you up to an issue known as SQL Injection, and it's kind of a big deal.
Instead, for most all queries, there will be a placeholder variable name for each input argument. It matters for this question because you'll have the same SQL query text for two queries that differ only by arguments.
For example, say you have this query:
SELECT * FROM Users WHERE Username = #Username
You run this query twice, once with 'jsmith' as the input, and once with 'jdoe'. The SQL didn't change, and therefore the encoded file name didn't change.
You maybe be inclined to ask to get the value of the SQL after the parameter inputs are substituted into the query, but this misunderstands what happens. The parameter inputs are never, at any time, substituted into the sql query. That's the whole point. Even the database server will instead treat them as procedure variables.
The point here is you also need a way to encode any parameter data used with your query. Here's one basic naive option:
public string GetUniqueFileNameForQuery(DbCommand query)
{
var sql = query.CommandText;
foreach(var p in query.Parameters)
{
sql = sql.Replace(p.Name, p.Value.ToString());
}
using (var hasher = SHA256.Create())
{
var queryBytes = Encoding.UTF8.GetBytes(sql);
var queryHash = hasher.ComputeHash(queryBytes);
// "/" may be included, but is not legal for file names
return Convert.ToBase64String(queryHash).Replace("/", "-")+".xml";
}
}
Note: this code could produce invalid SQL. For example, you might end up with something like this:
SELECT * FROM Users WHERE LastName = O'Brien
But since you're not actually trying to run the query, that should be okay. You also need to be careful with systems like OleDB, which uses positional matching and ? for all parameter placeholders. In this case, the parameter name won't match the placeholder, or even if it did, the first parameter would match the placeholder for all the others.

Is there any reason to use string.Format() with just a string parameter?

I'm currently updating some old code at work. I ran into a number of lines where string.Format() is used for seemingly no reason. I'm wondering if there is some use for using string.Format() without additional parameters, but I can't think of any.
What's the difference between this:
string query = String.Format(#"select type_cd, type_shpmt from codes");
and this:
string query = "select type_cd, type_shpmt from codes";
I also don't think that the # is needed since it doesn't need to be a string literal, and it's not a multi-line string.
Cut and paste failures. Someone didn't take the time to cleanup the code. There is no reason to use string.Format. Simply delete it and assign directly.
⚠️ Warning ⚠️
If the person is using the Format to combine parameters be wary; one should use the SQLCommand and SQLParameter class to avoid sql injection.
While Format should be deleted, spare # and make query being more readable:
string query =
#"select type_cd,
type_shpmt
from codes";
If you want to modify table, field names etc. (which you can't do via Sql Parameters) try using string interpolation:
string myTableName = "codes";
...
string query =
$#"select type_cd,
type_shpmt
from {myTableName}";

NHibernate LIKE query on date column

I'm using the following code to dynamically filter data (I'm using MySQL as a DB engine):
var filter = string.Empty;
if (!string.IsNullOrWhiteSpace(filterNumber))
{
filter = "t.Number LIKE :filterNumber";
}
var query = string.Format("SELECT t FROM Table t WHERE 1=1 AND {0} ORDER BY {1} {2}", filter, orderBy, orderDirection);
var q = Session.CreateQuery(query);
if (!string.IsNullOrWhiteSpace(filterNumber))
{
q.SetParameter<string>("filterNumber", "%" + filterNumber + "%");
}
q.SetFirstResult(offset);
q.SetMaxResults(limit);
return q.List<Table>();
(assume that filterNumber, orderBy, orderDirection, offset and limit are method's parameters and 1=1 was added only for the sake of this question so the query always work)
It does work when Number is a string (VARCHAR in MySQL) column but it does not work when it is a datetime or integer column.
For datetime it raises the exception:
could not execute query […] Name:filterDate - Value:%2012% and inner exception is Specified cast is not valid.
For integer/float columns the exception is:
could not execute query […] Name:filterPrice - Value:%100% and inner exception is Input string was not in a correct format.
(Date and Price are another columns in the table)
How to dynamically create such a LIKE query on date/numbers columns? This kind of query works well in MySQL:
SELECT * FROM `Table` WHERE `Date` LIKE '%2012%'
Based on #usr comment I managed to do casting in HQL:
filter = "CAST(t.Date AS String) LIKE :filterDate";
It works both for dates and numbers.
It is probably not such a good idea to use LIKE with dates/numbers if you can avoid it. If the intention is to match on year, you should use the year(alias.property) HQL function (no need to convert your query to the Criteria API, as this is the HQL syntax) and the regular equals operator. Or less/greater than.
I suspect the SQL works through automatic cast to varchar, but unless you really do need pattern matching, I would think this has significantly worse performance compared to working with date extraction or BETWEEN.
If you do need pattern matching, you should cast the date/number to string.

Oracle client requires parentheses around column names?

I was recently asked to migrate our MSSQL database to an Oracle one.
I'm using the old-traditional way to execute sql queries.
for some reason, unknown to me, Oracle requires me to put parentheses around column names (why?)
Is there a workaround for this?
The following code will fail because of the parentheses (used to work well under MSSQL)
using (var msq = new OracleConnection(sConnectionString))
{
msq.Open();
OracleCommand msc = msq.CreateCommand();
msc.CommandText = #"SELECT level_1,element_id FROM tnuot_menu_tree
WHERE level_1 IN
(SELECT mt.level_1 FROM tnuot_menu_tree mt
WHERE mt.element_id IN
(SELECT element_tree_id FROM tnuot_menu_elements
WHERE UPPER(element_link) LIKE :url))
AND level_2 = 0 AND level_3 = 0";
msc.Parameters.Add("url", SqlDbType.VarChar);
msc.Parameters["url"].Value = "%" + sName.ToUpper();
OracleDataReader mrdr = msc.ExecuteReader();
while (mrdr.Read())
{
sResult.arDirectResult.Add(mrdr[0].ToString());
sResult.arDirectResult.Add(mrdr[1].ToString());
break;
}
msc.Dispose();
mrdr.Dispose();
msq.Close();
}
Instead, in the VS server explorer, the last query gets 'translated' to
SELECT "level_1", "element_id"
FROM "tnuot_menu_tree"
WHERE ("level_1" IN
(SELECT "level_1" FROM "tnuot_menu_tree" mt
WHERE ("element_id" IN
(SELECT "element_tree_id" FROM "tnuot_menu_elements"
WHERE (UPPER("element_link") LIKE '%DEFAULT.ASPX')))))
AND ("level_2" = 0) AND ("level_3" = 0)
Which works well.
Any ideas on how to get rid of this nasty task?
Possibly, it isn't the brackets that are necessary; it's the double quotes. This is Oracle's equivalent of SQLServer's use of square brackets - it may be necessary here because the tables have been created with lower-case names, but without the double quotes Oracle automatically converts names to upper-case.
The main difference between your first and second query are the quotes (and not the parentheses). The additional parentheses aren't needed. They seem to be a strange artifact of the VS server explorer.
Contrary to popular belief, Oracle is case-sensitive. The column names level_1 and LEVEL_1 are different. If your column and table names are all upper-case, case won't matter because Oracle converts all unquoted identifiers in SQL statements to upper-case.
But if your column and tables names use lower case letters, you must put the column names in double quotes to have the proper casing retained.

Converting user-entered search query to where clause for use in SQL Server full-text search

What's the best way to convert search terms entered by a user, into a query that can be used in a where clause for full-text searching to query a table and get back relevant results? For example, the following query entered by the user:
+"e-mail" +attachment -"word document" -"e-learning"
Should translate into something like:
SELECT * FROM MyTable WHERE (CONTAINS(*, '"e-mail"')) AND (CONTAINS(*, '"attachment"')) AND (NOT CONTAINS(*, '"word document"')) AND (NOT CONTAINS(*, '"e-learning"'))
I'm using a query parser class at the moment, which parses the query entered by users into tokens using a regular expression, and then constructs the where clause from the tokens.
However, given that this is probably a common requirement by a lot of systems using full-text search, I'm curious as to how other developers have approached this problem, and whether there's a better way of doing things.
How to implement the accepted answer using .Net / C# / Entity Framework...
Install Irony using nuget.
Add the sample class from:
http://irony.codeplex.com/SourceControl/latest#Irony.Samples/FullTextSearchQueryConverter/SearchGrammar.cs
Write code like this to convert the user-entered string to a query.
var grammar = new Irony.Samples.FullTextSearch.SearchGrammar();
var parser = new Irony.Parsing.Parser(grammar);
var parseTree = parser.Parse(userEnteredSearchString);
string query = Irony.Samples.FullTextSearch.SearchGrammar.ConvertQuery(parseTree.Root);
Perhaps write a stored procedure like this:
create procedure [dbo].[SearchLivingFish]
#Query nvarchar(2000)
as
select *
from Fish
inner join containstable(Fish, *, #Query, 100) as ft
on ft.[Key] = FishId
where IsLiving = 1
order by rank desc
Run the query.
var fishes = db.SearchLivingFish(query);
This may not be exactly what you are looking for but it may offer you some further ideas.
http://www.sqlservercentral.com/articles/Full-Text+Search+(2008)/64248/
In addition to #franzo's answer above you probably also want to change the default stop word behaviour in SQL. Otherwise queries containing single digit numbers (or other stop words) will not return any results.
Either disable stop words, create your own stop word list and/or set noise words to be transformed as explained in SQL 2008: Turn off Stop Words for Full Text Search Query
To view the system list of (English) sql stop words, run:
select * from sys.fulltext_system_stopwords where language_id = 1033
I realize it's a bit of a side-step from your original question, but have you considered moving away from SQL fulltext indexes and using something like Lucene/Solr instead?
The easiest way to do this is to use dynamic SQL (I know, insert security issues here) and break the phrase into a correctly formatted string.
You can use a function to break the phrase into a table variable that you can use to create the new string.
A combination of GoldParser and Calitha should sort you out here.
This article: http://www.15seconds.com/issue/070719.htm has a googleToSql class as well, which does some of the translation for you.

Categories