Oracle client requires parentheses around column names? - c#

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.

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 restriction to WindowsInstaller msi database queries in sql

I am trying to run some sql queries on my msi database in c#.But it seems like some specific sql queries are not working.
WindowsInstaller.Installer ins = (WindowsInstaller.Installer)new Installer();
string strFileMsi = #"abc.msi";
Database db3 = ins.OpenDatabase(strFileMsi, WindowsInstaller.MsiOpenDatabaseMode.msiOpenDatabaseModeDirect);
string q = "SELECT File FROM File WHERE FileName LIKE '%s%'";
WindowsInstaller.View vw = db3.OpenView(q);
vw.Execute(null);
string q2="SELECT * FROM InstalExecuteSequece ORDER BY Sequence DESC"
WindowsInstaller.View vw2 = db.OpenView(q2);
vw.Execute(null);
If i run the same query without DESC keyword and all,it works fine.Similarly is the case with LIKE KEYWORD also.All of these gives sql exception.
Windows Installer implements a subset of SQL described in SQL Syntax. Within that subset there are several limitations, including three that I'll highlight here:
There is no support for DESC or LIKE, and ORDER BY may not handle strings as you expect
Update queries cannot modify a value in a primary key column
There is no way to escape the apostrophe character ' in a SQL query. If you need to match a string value like 'It's', you have to use the a question mark ? placeholder in the query and pass a record containing the value to view.Execute(record)
To overcome the limited capabilities of installer supported SQL, you can make use of the Microsoft.Deployment.WindowsInstaller.Linq assembly which is shipped with wix. To extract the last sequence number of the InstalExecuteSequece table for example, you'd simply write the following query:
var db = session.Database.AsQueryable( );
var lastSequence = db.ExecuteIntegerQuery( "SELECT `Sequence` FROM `InstalExecuteSequece` ORDER BY `Sequence`" ).Last( );

Querying Numeric TableName fails

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.

ORA 00911 Error When SQL Statement With Where Clause Ends With Semi-Colon?

I'm getting an oracle 00911 error (illegal character). I'm hoping someone can help me understand.
I'm using the following code to execute an sql statement on my oracle 11g database:
private DataTable ExecuteQuery(DbCommand query) {
DataTable result = new DataTable();
using (DbConnection con = CreateConnection()) {
try {
query.Connection = con;
query.CommandTimeout = int.MaxValue; // don't impose a timeout
using (DbDataAdapter dataAdapter = factory.CreateDataAdapter()) {
dataAdapter.SelectCommand = query;
dataAdapter.Fill(result);
}
}
...
If I give this function something with a DbCommand.CommandText property like "Select * from X;" it works fine, but given "Select * from x where y;" this will throw an oracle 00911 exception. If I remove the semicolon, however, it executes fine.
Does anyone know why it would throw an illegal character error for ending the statement with a semicolon only on certain types of statements?
Update for clarity:
The exact queries I used to test out the semi colon causing error were:
This query worked fine:
SELECT * FROM Machines;
This query generated an ORA 0911 error:
SELECT * FROM Machines WHERE ID = 47;
While this query worked fine:
SELECT * FROM Machines WHERE ID = 47 <-- only the semi colon changed
Also the provider being used is Oracle.DataAccess.Client
This behaviour depends on the DB provider used - some providers do additional queries upfront (like a SELECT COUNT(*)...) and/or add something to the query (like ROWID for example)... depending on how the provider implements this "internal behaviour" it might result in some strange things when a semicolon is present... it might even behave differently depending on whether a WHERE is present or not...
In scenarios like yours (a pure SELECT / UPDATE / DELETE statement) I NEVER add a semicolon at the end and had never this problem...
Out of curiosity: why do you have a semicolon at the end ?

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