Using code for creating SQL query - c#

I like to do something like this in code:
string[] columns = new string[]{"col1, col2"};
var query = SELECT(columns).FROM("ANY_TABLE");
string strQuery = query.ToString();
and strQuery should contain now a sql query as string like:
SELECT col1, col2 FROM ANY_TABLE
I tried already to find something like this but I don't know any labels which I could use.
I know there is LINQ but I think it only works with EF and not sure whether it can output such a string.
THANKS

A simple approach could be:
string[] columns = new string[] { "col1", "col2" };
var query = #"SELECT ";
foreach(var column in columns)
{
query += " " + column + ", ";
}
query = query.Substring(0, query.Length-2);
query += #" FROM ANY_TABLE ";
string strQuery = query.ToString();

Related

SQL find rows with key specified in list, C#

I want to make a sql query in C# that finds all rows with a key that is specified in a list. Can I do this with one query? I suppose that is much more efficent than my solution which finds one item at the time inside a for loop, se below:
foreach (int i in list)
{
string Q = "... where pk = " + i.ToString();
using (SqlCommand CM = new SqlCommand(Q, C))
{
using (SqlDataReader R = CM.ExecuteReader())
{
while (R.Read())
{
...
}
}
}
}
list contains different in values.
Thanks in advance!
Replace
string Q = "... where pk = " + i.ToString();
with
string Q = "... where pk IN ('" + string.Join("','", list)+"')";
then you can remove the loop. The result should look like ... where pk IN ('1','2','3')
You can use the IN keyword and pass your list by converting it to a comma seperated string in your query.
Something like
string Q = "select * from tablename where pk IN " + (comma seperated list here);

Using an arbitrary number of parameters in ORMLite Query

I am in the process of fixing some of our bad sql queries that are vulnerable to sql injection. Most are straight queries with no inputs, but our search field takes search terms that are not parameterised. A snippet is below:
using (var db = ORMLite.Open())
{
StringBuilder sb = new StringBuilder();
sb.Append("select * from column1, column2");
if (terms.Count() > 0)
{
sb.Append("where (column1 like '%#term0%' or " + column2 + " like '%#term0%') ");
if (terms.Count() > 1)
{
for (int i = 1; i < terms.Count(); i++)
{
sb.Append("and (column1 like '%#term" + i + "%' or " + column2 + " like '%#term" + i + "%') ");
}
}
}
List<POCO> testQuery = db.Select<POCO>(sb.ToString());
}
The #term components are where I intend to use parameters (they used to be of the form '" + term[i] + '", but any term with malicious code would just be inserted. When I move to my select statement, I would like to add the parameters. This is normally done as so:
List testQuery = db.Select(sb.ToString(), new { term0 = "t", term1 = "te", term2 = "ter" });
However I can have any number of terms (term.count() is the number of terms). How can I pass in an anonymous object with any number of terms? Or is there a better method?
I'm looking for almost the same thing in Postgresql. Based on this SO question
the answer looks like "you have to perform multiple queries."
I can get the unique row IDs from my table given the partial parameterized
query, and then directly paste those unique IDs back into the query -- since those
row IDs will be safe.
Here's an example of what I mean, but the c# is probably wrong (sorry):
string query = "SELECT unique_id FROM table WHERE (column1 LIKE '%#term%' OR column2 LIKE '%#term%')";
string safeIDs;
List uniqueRowIDs = db.Select(query, new {term = term[0]});
for (int i = 1; i < terms.Count(); i++) {
// Loop to narrow down the rows by adding the additional conditions.
safeIDs = uniqueRowIDs.Aggregate( (current, next) => current + string.Format(", '{0}'", next) );
uniqueRowIDs = db.Select(
query + string.Format(" AND unique_id IN ({0})", safeIDs),
new {term = term[i]});
}
// And finally make the last query for the chosen rows:
safeIDs = uniqueRowIDs.Aggregate( (current, next) => current + string.Format(", '{0}'", next) );
List testQuery = db.Select(string.Format("SELECT * FROM table WHERE unique_id IN ({0});", safeIDs));
Another option for your case specifically could be to just get all of the values that
are like term0 using a parameterized query and then, within the c# program, compare
all of the results against the remaining terms the user entered.

Is there a better way to dynamically build an SQL WHERE clause than by using 1=1 at its beginning?

I'm building some SQL query in C#. It will differ depending on some conditions stored as variables in the code.
string Query="SELECT * FROM Table1 WHERE 1=1 ";
if (condition1)
Query += "AND Col1=0 ";
if (condition2)
Query += "AND Col2=1 ";
if (condition3)
Query += "AND Col3=2 ";
It works, but testing 1=1 doesn't seem elegant. If I didn't use it, I would have to remember and check every time if "where" keyword was already added or not to the query.
Is there a nicer solution?
Save the conditions in a list:
List<string> conditions = new List<string>();
if (condition1) conditions.Add("Col1=0");
//...
if (conditions.Any())
Query += " WHERE " + string.Join(" AND ", conditions.ToArray());
One solution is to simply not write queries manually by appending strings. You could use an ORM, like Entity Framework, and with LINQ to Entities use the features the language and framework offer you:
using (var dbContext = new MyDbContext())
{
IQueryable<Table1Item> query = dbContext.Table1;
if (condition1)
{
query = query.Where(c => c.Col1 == 0);
}
if (condition2)
{
query = query.Where(c => c.Col2 == 1);
}
if (condition3)
{
query = query.Where(c => c.Col3 == 2);
}
PrintResults(query);
}
A slight bit of overkill in this simple case but I've used code similar to this in the past.
Create a function
string AddCondition(string clause, string appender, string condition)
{
if (clause.Length <= 0)
{
return String.Format("WHERE {0}",condition);
}
return string.Format("{0} {1} {2}", clause, appender, condition);
}
Use it like this
string query = "SELECT * FROM Table1 {0}";
string whereClause = string.Empty;
if (condition 1)
whereClause = AddCondition(whereClause, "AND", "Col=1");
if (condition 2)
whereClause = AddCondition(whereClause, "AND", "Col2=2");
string finalQuery = String.Format(query, whereClause);
This way if no conditions are found you don't even bother loading a where statement in the query and save the sql server a micro-second of processing the junk where clause when it parses the sql statement.
There is another solution, which may also not be elegant, but works and solves the problem:
String query = "SELECT * FROM Table1";
List<string> conditions = new List<string>();
// ... fill the conditions
string joiner = " WHERE ";
foreach (string condition in conditions) {
query += joiner + condition;
joiner = " AND "
}
For:
empty conditions list, the result will be simply SELECT * FROM Table1,
a single condition it will be SELECT * FROM Table1 WHERE cond1
each following condition will generate additional AND condN
Just do something like this:
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT * FROM Table1";
var conditions = "";
if (condition1)
{
conditions += "Col1=#val1 AND ";
command.AddParameter("val1", 1);
}
if (condition2)
{
conditions += "Col2=#val2 AND ";
command.AddParameter("val2", 1);
}
if (condition3)
{
conditions += "Col3=#val3 AND ";
command.AddParameter("val3", 1);
}
if (conditions != "")
command.CommandText += " WHERE " + conditions.Remove(conditions.Length - 5);
}
It's SQL injection safe and IMHO, it's pretty clean. The Remove() simply removes the last AND;
It works both if no conditions have been set, if one have been set or if multiple have been set.
Just append two lines at back.
string Query="SELECT * FROM Table1 WHERE 1=1 ";
if (condition1) Query+="AND Col1=0 ";
if (condition2) Query+="AND Col2=1 ";
if (condition3) Query+="AND Col3=2 ";
Query.Replace("1=1 AND ", "");
Query.Replace(" WHERE 1=1 ", "");
E.g.
SELECT * FROM Table1 WHERE 1=1 AND Col1=0 AND Col2=1 AND Col3=2
will become to
SELECT * FROM Table1 WHERE Col1=0 AND Col2=1 AND Col3=2
While
SELECT * FROM Table1 WHERE 1=1
will become to
SELECT * FROM Table1
=====================================
Thanks for pointing out a flaw of this solution:
"This could break the query if, for any reason, one of the conditions contains the text "1=1 AND " or " WHERE 1=1 ". This could be the case if the condition contains a subquery or tries to check if some column contains this text, for example. Maybe this isn't a problem in your case but you should keep it in mind… "
In order to get rid of this issue, we need to distinguish the "main" WHERE 1=1 and those from subquery, which is easy:
Simply make the "main" WHERE special: I would append a "$" sign
string Query="SELECT * FROM Table1 WHERE$ 1=1 ";
if (condition1) Query+="AND Col1=0 ";
if (condition2) Query+="AND Col2=1 ";
if (condition3) Query+="AND Col3=2 ";
Then still append two lines:
Query.Replace("WHERE$ 1=1 AND ", "WHERE ");
Query.Replace(" WHERE$ 1=1 ", "");
Use this:
string Query="SELECT * FROM Table1 WHERE ";
string QuerySub;
if (condition1) QuerySub+="AND Col1=0 ";
if (condition2) QuerySub+="AND Col2=1 ";
if (condition3) QuerySub+="AND Col3=2 ";
if (QuerySub.StartsWith("AND"))
QuerySub = QuerySub.TrimStart("AND".ToCharArray());
Query = Query + QuerySub;
if (Query.EndsWith("WHERE "))
Query = Query.TrimEnd("WHERE ".ToCharArray());
Why not using an existing Query Builder ?
Something like Sql Kata.
It supports complex where conditions, joins and subqueries.
var query = new Query("Users").Where("Score", ">", 100).OrderByDesc("Score").Limit(100);
if(onlyActive)
{
query.Where("Status", "active")
}
// or you can use the when statement
query.When(onlyActive, q => q.Where("Status", "active"))
it works with Sql Server, MySql and PostgreSql.
If this is SQL Server, you can make this code much cleaner.
This also assumes a known number of parameters, which may be a poor assumption when I think about the possibilities.
In C#, you would use:
using (SqlConnection conn = new SqlConnection("connection string"))
{
conn.Open();
SqlCommand command = new SqlCommand()
{
CommandText = "dbo.sample_proc",
Connection = conn,
CommandType = CommandType.StoredProcedure
};
if (condition1)
command.Parameters.Add(new SqlParameter("Condition1", condition1Value));
if (condition2)
command.Parameters.Add(new SqlParameter("Condition2", condition2Value));
if (condition3)
command.Parameters.Add(new SqlParameter("Condition3", condition3Value));
IDataReader reader = command.ExecuteReader();
while(reader.Read())
{
}
conn.Close();
}
And then on the SQL side:
CREATE PROCEDURE dbo.sample_proc
(
--using varchar(50) generically
-- "= NULL" makes them all optional parameters
#Condition1 varchar(50) = NULL
#Condition2 varchar(50) = NULL
#Condition3 varchar(50) = NULL
)
AS
BEGIN
/*
check that the value of the parameter
matches the related column or that the
parameter value was not specified. This
works as long as you are not querying for
a specific column to be null.*/
SELECT *
FROM SampleTable
WHERE (Col1 = #Condition1 OR #Condition1 IS NULL)
AND (Col2 = #Condition2 OR #Condition2 IS NULL)
AND (Col3 = #Condition3 OR #Condition3 IS NULL)
OPTION (RECOMPILE)
--OPTION(RECOMPILE) forces the query plan to remain effectively uncached
END
The quickest literal solution to what you're asking that I can think of is this:
string Query="SELECT * FROM Table1";
string Conditions = "";
if (condition1) Conditions+="AND Col1=0 ";
if (condition2) Conditions+="AND Col2=1 ";
if (condition3) Conditions+="AND Col3=2 ";
if (Conditions.Length > 0)
Query+=" WHERE " + Conditions.Substring(3);
It doesn't seem elegant, sure, to which I would refer you to CodeCaster's recommendation of using an ORM. But if you think about what this is doing here, you're really not worried about 'wasting' 4 characters of memory, and it's really quick for a computer to move a pointer 4 places.
If you have the time to learn how to use an ORM, it could really pay off for you. But in regards to this, if you're trying to keep that additional condition from hitting the SQL db, this will do it for you.
Depending on the condition, it might be possible to use boolean logic in the query. Something like this :
string Query="SELECT * FROM Table1 " +
"WHERE (condition1 = #test1 AND Col1=0) "+
"AND (condition2 = #test2 AND Col2=1) "+
"AND (condition3 = #test3 AND Col3=2) ";
I like the fluent interface of stringbuilder, so I made some ExtensionMethods.
var query = new StringBuilder()
.AppendLine("SELECT * FROM products")
.AppendWhereIf(!String.IsNullOrEmpty(name), "name LIKE #name")
.AppendWhereIf(category.HasValue, "category = #category")
.AppendWhere("Deleted = #deleted")
.ToString();
var p_name = GetParameter("#name", name);
var p_category = GetParameter("#category", category);
var p_deleted = GetParameter("#deleted", false);
var result = ExecuteDataTable(query, p_name, p_category, p_deleted);
// in a seperate static class for extensionmethods
public StringBuilder AppendLineIf(this StringBuilder sb, bool condition, string value)
{
if(condition)
sb.AppendLine(value);
return sb;
}
public StringBuilder AppendWhereIf(this StringBuilder sb, bool condition, string value)
{
if (condition)
sb.AppendLineIf(condition, sb.HasWhere() ? " AND " : " WHERE " + value);
return sb;
}
public StringBuilder AppendWhere(this StringBuilder sb, string value)
{
sb.AppendWhereIf(true, value);
return sb;
}
public bool HasWhere(this StringBuilder sb)
{
var seperator = new string [] { Environment.NewLine };
var lines = sb.ToString().Split(seperator, StringSplitOptions.None);
return lines.Count > 0 && lines[lines.Count - 1].Contains("where", StringComparison.InvariantCultureIgnoreCase);
}
// http://stackoverflow.com/a/4217362/98491
public static bool Contains(this string source, string toCheck, StringComparison comp)
{
return source.IndexOf(toCheck, comp) >= 0;
}
IMHO, I think that your approach is wrong:
Query the database by concatenating string is NEVER a good idea (risk of SQL injection and the code can easily be broken if you do some changes elsewhere).
You can use an ORM (I use NHibernate) or at least use SqlCommand.Parameters
If you absolutely want to use string concatenation, I would use a StringBuilder (it is the right object for string concatenation):
var query = new StringBuilder("SELECT * FROM Table1 WHERE");
int qLength = query.Length;//if you don't want to count :D
if (Condition1) query.Append(" Col1=0 AND");
if (Condition2) query.Append(" Col2=0 AND");
....
//if no condition remove WHERE or AND from query
query.Length -= query.Length == qLength ? 6 : 4;
As the last thought, Where 1=1 is really ugly but SQL Server will optimize it anyway.
The Dapper SqlBuilder is a pretty good option. It's even used in production on StackOverflow.
Read Sam's blog entry about it.
As far as I know, it's not part of any Nuget package, so you'll need to copy paste its code into your project or download the Dapper source and build the SqlBuilder project. Either way, you'll also need to reference Dapper for the DynamicParameters class.
I see this used all the time in Oracle while building dynamic SQL within stored procedures. I use it in queries while exploring data issues as well just to make switching between different filters of data faster... Just comment out a condition or add it back in easily.
I find it's pretty common and easy enough to understand to someone reviewing your code.
Using string function you can also do it this way:
string Query = "select * from Table1";
if (condition1) WhereClause += " Col1 = #param1 AND "; // <---- put conditional operator at the end
if (condition2) WhereClause += " Col1 = #param2 OR ";
WhereClause = WhereClause.Trim();
if (!string.IsNullOrEmpty(WhereClause))
Query = Query + " WHERE " + WhereClause.Remove(WhereClause.LastIndexOf(" "));
// else
// no condition meets the criteria leave the QUERY without a WHERE clause
I personally feel easy to to remove the conditional element(s) at the end, since its position is easy to predict.
I thought of a solution that, well, perhaps is somewhat more readable:
string query = String.Format("SELECT * FROM Table1 WHERE "
+ "Col1 = {0} AND "
+ "Col2 = {1} AND "
+ "Col3 = {2}",
(!condition1 ? "Col1" : "0"),
(!condition2 ? "Col2" : "1"),
(!condition3 ? "Col3" : "2"));
I'm just not sure whether the SQL interpreter will also optimize away the Col1 = Col1 condition (printed when condition1 is false).
public static class Ext
{
public static string addCondition(this string str, bool condition, string statement)
{
if (!condition)
return str;
return str + (!str.Contains(" WHERE ") ? " WHERE " : " ") + statement;
}
public static string cleanCondition(this string str)
{
if (!str.Contains(" WHERE "))
return str;
return str.Replace(" WHERE AND ", " WHERE ").Replace(" WHERE OR ", " WHERE ");
}
}
Realisation with extension methods.
static void Main(string[] args)
{
string Query = "SELECT * FROM Table1";
Query = Query.addCondition(true == false, "AND Column1 = 5")
.addCondition(18 > 17, "AND Column2 = 7")
.addCondition(42 == 1, "OR Column3 IN (5, 7, 9)")
.addCondition(5 % 1 > 1 - 4, "AND Column4 = 67")
.addCondition(Object.Equals(5, 5), "OR Column5 >= 0")
.cleanCondition();
Console.WriteLine(Query);
}
Here is a more elegant way:
private string BuildQuery()
{
string MethodResult = "";
try
{
StringBuilder sb = new StringBuilder();
sb.Append("SELECT * FROM Table1");
List<string> Clauses = new List<string>();
Clauses.Add("Col1 = 0");
Clauses.Add("Col2 = 1");
Clauses.Add("Col3 = 2");
bool FirstPass = true;
if(Clauses != null && Clauses.Count > 0)
{
foreach(string Clause in Clauses)
{
if (FirstPass)
{
sb.Append(" WHERE ");
FirstPass = false;
}
else
{
sb.Append(" AND ");
}
sb.Append(Clause);
}
}
MethodResult = sb.ToString();
}
catch //(Exception ex)
{
//ex.HandleException()
}
return MethodResult;
}
As has been stated, creating SQL by concatenation is never a good idea. Not just because of SQL injection. Mostly because it's just ugly, difficult to maintain and totally unnecessary. You have to run your program with trace or debug to see what SQL it generates. If you use QueryFirst (disclaimer: which I wrote) the unhappy temptation is removed, and you can get straight in ta doin it in SQL.
This page has a comprehensive coverage of TSQL options for dynamically adding search predicates. The following option is handy for situations where you want to leave the choice of combinations of search predicates to your user.
select * from table1
where (col1 = #param1 or #param1 is null)
and (col2 = #param2 or #param2 is null)
and (col3 = #param3 or #param3 is null)
OPTION (RECOMPILE)
QueryFirst gives you C# null to db NULL, so you just call the Execute() method with nulls when appropriate, and it all just works.
<opinion>Why are C# devs so reluctant to do stuff in SQL, even when it's simpler. Mind boggles.</opinion>
For longer filtering steps StringBuilder is the better approach as many says.
on your case I would go with:
StringBuilder sql = new StringBuilder();
if (condition1)
sql.Append("AND Col1=0 ");
if (condition2)
sql.Append("AND Col2=1 ");
if (condition3)
sql.Append("AND Col3=2 ");
string Query = "SELECT * FROM Table1 ";
if(sql.Length > 0)
Query += string.Concat("WHERE ", sql.ToString().Substring(4)); //avoid first 4 chars, which is the 1st "AND "
Concise, elegant and sweet, as shown in the image below.

How to generate SQL statement from List?

If I have a list of Strings, ie. List<String>, how can I generate a SQL statement such as:
SELECT Column1 FROM Table1 WHERE Column1 IN ('String1','String2','String3')
where 'String1','String2','String3' are the contents of List<String>?
No LINQ etc. as I am using VS2005.
Take a look on following version
[Test]
public void Test()
{
var list = new List<string> {"String1", "String2", "String3"};
string values = ArrayToString(list);
string sql = string.Format("SELECT Column1 FROM Table1 WHERE Column1 IN ( {0} )", values);
}
private static string ArrayToString(IEnumerable<string> array)
{
var result = new StringBuilder();
foreach (string element in array)
{
if (result.Length > 0)
{
result.Append(", ");
}
result.Append("'");
result.Append(element);
result.Append("'");
}
return result.ToString();
}
result statement SELECT Column1 FROM Table1 WHERE Column1 IN ( 'String1', 'String2', 'String3' )
List<string> lst=new List<string>();lst.Add("Hello");lst.Add("Hello World");
string s="";
foreach(string l in lst)s+="\""+l+"\"";
s=Regex.Replace(s,"\"\"","\",\"");
string output="SELECT Column1 FROM Table1 WHERE Column1 ("+s+")";
try :
List<String> strlist = new List<string>();
strlist.Add("st1");
strlist.Add("st2");
strlist.Add("st3");
string query = "SELECT Column1 FROM Table1 WHERE Column1 IN (";
for (int i = 0; i < strlist.Count; i++)
{
query += "\'" + strlist[i] + "\'" + (i == strlist.Count - 1 ? "" : ",");
}
query += ")";
List<string> items = new List<string>();
items.Add("string1");
items.Add("string2");
items.Add("string3");
string AllItems = "";
foreach (string item in items)
{
AllItems += string.Format("\"{0}\",",item);
}
AllItems = AllItems.TrimEnd(',');
string YourSQLQuery = string.Format("SELECT Column1 FROM Table1 WHERE Column1 IN ({0})", AllItems);
MessageBox.Show(YourSQLQuery);
Don't for get to guard against SQL Injection.
string sql_list = "";
foreach (string s in lst)
sql_list+=string.Format("{0},",s.Replace("'","''"));
sql_list = string.Format("({0})",sql_list.substring(0,sql_list.length-2));
that might help some, and use string builder, or not.
Please don't use the other answers that have been submitted so far. They contain SQL injection for no obvious reason.
List<String> strlist = new List<string>();
strlist.Add("st1");
strlist.Add("st2");
strlist.Add("st3");
var dynamicPart = string.Join(", ",
Enumerable.Range(0, strlist.Count).Select(i => "#" + i).ToArray());
for(i = 0 to strlist.Count)
{ /* add parameter to SqlCommand here with name ("#" + i) */ }
string query = "SELECT Column1 FROM Table1 WHERE Column1 IN (" +
dynamicPart + ")";
Use parameters instead of literals for multiple reasons (research them!).
And instead of a clumsy concatenation loop use string.Join which does all of that for us.
To properly handle sql injection, a better answer may be to make the query of the form...
select results.* from (
select pk from table where column=value1 union
select pk from table where column=value2 union
select pk from table where column=value3 union
select pk from table where column=value4 union
select pk from table where column=value5
) filtered join table as results on filtered.pk = results.pk
and then make it more c# friendly
string items_filter = "";
int item_index=0;
OracleParameterCollection parameters = new OracleParameterCollection(); // Not sure what class to use here exactly, but just collect a bunch of stored procedure parameters
foreach (string item in list_of_items) {
string item_name = string.Format("i_item{0}",item_index);
string item_sql = string.Format("select pk from table where column=:{0} union",item_name);
parameters.Add(new Parameter("item_name",item));
item_index+=1;
}
if (items_filter.IsNullOrEmpty())
return;
string sql = String.Format("select results.* from ({0}) filtered join table as results on filtered.pk = results.pk",items_filter);
OracleCommand c = new OracleCommand();
c.command = sql;
c.parameters = parameters;
c.execute();
More or less.
Since you said its an internal operation and hence there is no need to be worried about SQL Injection, then you can achieve what you want by this.
string str = "";
foreach(string s in list)
str += "'" + s.Replace("'", "''") + "',";
str = str.SubString(0, str.Length - 1);
str = "SELECT Column1 FROM Table1 WHERE Column1 IN (" + str + ")";
//str will have your command ready.
I have tested it. It works perfectly.
// Assume your list (List<string>) is named "myList"
// Please put the next line in an external string resource...
string selectStatement = "SELECT Column1 FROM Table1 WHERE Column1 IN ({0})";
StringBuilder stringBuilder = new StringBuilder("(");
foreach(string colName in myList)
stringBuilder.Append(String.Format("'{0}',", colName));
stringBuilder.Append(")");
return String.Format(selectStatement, stringBuilder.ToString().Replace(",)", ")");

How to get table name from string that contain SELECT statement

i have a string that contain a sql command,
something like this:
strCommand = "Select [Feild1],
[Feild2]
From TableName
Order By [Feild1] desc" ;
How can find table name in this string?
The solutions so far have all gone with the searching within strings approach. You've not mentioned if your SQL queries will always look similar, but there are many variants of a query to include which these solutions will break on. Consider...
SELECT Field1, Field2 FROM TableName
SELECT Field1, Field2 FROM [TableName]
SELECT Field1, Field2 FROM dbo.TableName
SELECT Field1, Field2 FROM Table1Name, Table2Name
If the query you're trying to parse is one you have the database for, you can get SQL server to do the hard work of parsing the query for you, instead of trying to account for all the cases in SQL. You can execute a query using SET SHOWPLAN_ALL ON, which will produce a table of the query plan. You can then analyse the Arguments column, which contains all of the fields the query will involve in a standard format. An example program is below:
SqlConnection conn = new SqlConnection(CONNECTIONSTRING);
conn.Open();
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "SET SHOWPLAN_ALL ON";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT [Field1], [Field2] FROM [TableName]";
DataTable dt = new DataTable();
dt.Load(cmd.ExecuteReader());
Regex objectRegex = new Regex(#"^OBJECT:\(\[(?<database>[^\]]+)\]\.\[(?<schema>[^\]]+)\]\.\[(?<table>[^\]]+)\]\.\[(?<field>[^\]]+)\]\)$", RegexOptions.ExplicitCapture);
List<string> lstTables = new List<string>();
foreach (DataRow row in dt.Rows)
{
string argument = row["Argument"].ToString();
if (!String.IsNullOrEmpty(argument))
{
Match m = objectRegex.Match(argument);
if (m.Success)
{
string table = m.Groups["schema"] + "." + m.Groups["table"];
if (!lstTables.Contains(table))
{
lstTables.Add(table);
}
}
}
}
Console.WriteLine("Query uses the following tables: " + String.Join(", ", lstTables));
This will deal with all forms of query name and return all tables which are involved in the query, no matter how they are included.
If this is the same pattern all of the time then:
string tableName = strCommand.Split(' ', strCommand)[4];
but if you can add / remove fields just iterate through the splitted string and search for "From", and the next string will be your table name
I would say- what is after "From" as a more reliable way of getting the table name. Loop through the array created, when you reach "From", the next one is the table.
This is the Method which gives us tablename just change the SQL query string, connection String
Works with simple query, joins too
public static List<string> getTablenames(string connString, string QueryString)
{
SqlConnection con = new SqlConnection(connString);
con.Open();
DataTable dt = con.GetSchema("Tables");
List<string> getTableName = new List<string>();
List<string> tablenames = new List<string>();
foreach (DataRow dr in dt.Rows)
tablenames.Add(dr[2].ToString());
for (int i = 0; i < dt.Rows.Count; i++)
{
string myTable = tablenames[i];
Boolean checkMyTable = QueryString.Contains(myTable);
if (checkMyTable == true)
getTableName.Add(myTable);
}
con.Close();
return getTableName;
}
You can use the substring (This way it does not matter how many column you have to select)
string table = strCommand.ToLower().Substring(strCommand.IndexOf("FROM".ToLower())).Split(' ')[0];
ISun's answer met my needs but one change is required to get the table name:
string table = strCommand.ToLower().Substring(strCommand.IndexOf("FROM".ToLower())).Split(' ')[1];
not
string table = strCommand.ToLower().Substring(strCommand.IndexOf("FROM".ToLower())).Split(' ')[0];
If you want a solution in SQL, try this
declare #q varchar(1000) = 'Select [Feild1], [Feild2] From TableName Order By [Feild1] desc',
#tableName varchar(100) = '',
#temp varchar(1000),
#temp2 char(1)
declare #frmIndex int = CHARINDEX('From', #q, 0);
declare #flag int = 0, #counter int = 1;
select #temp = SUBSTRING(#q, #frmIndex, len(#q))
set #temp = LTRIM(REPLACE(#temp,'From',''))
while(#flag <> 1)
begin
set #temp2 = SUBSTRING(#temp, #counter, 1)
if(#temp2 = ' ')
set #flag = 1
select #tableName = #tableName + #temp2
set #counter = #counter + 1
end
select #tableName as TableName

Categories