I have a random .sql file with long sql query. I need to runtime get all table names from the insert sql statement to put into an array. I am able to get the table names (Inventory.tableA) out if the insert statement is in (one line) like the format below:
...
Insert into Inventory.tableA;
...
However, if the Insert statement is in multiple lines like below e.g.
Insert into
Inventory.tableA;
OR
Insert into
(blank line)
(blank line)
Inventory.tableA;
Then my query to retrieve the table name will fail. Can you advise how i can get the table name out from the long sql query which the insert statement can be in either one line or multiple lines? What is the best approach here?
Below is the c# query i tried which can handle for 1 line.
public List<string> GetAllTablesNames(string sp)
{
List<string> output = new List<string>();
string[] contentSplit = sp.Split(new string[] { "INSERT INTO " }, StringSplitOptions.None);
for (int a = 1; a < contentSplit.Length; a++)
{
string[] sa_tableName = contentSplit[a].Substring(0, contentSplit[a].IndexOf("\r")).Trim().Split('.');
output.Add(sa_tableName[0] + "." + sa_tableName[1]);
}
return output.Distinct().ToList();
}
Use singleline mode
List<string> tables= Regex.Matches("yourInput",#"Insert into\s+(.*?)[\s\(]+"
,RegexOptions.Singleline|RegexOptions.IgnoreCase)
.Cast<Match>().Select(x=>x.Groups[1].Value)
.ToList<string>();
//tables contains all the table names
I'm not sure what your regex is, but you could:
Change all spaces in your regex to \s+.
Replace all endlines with " " first, then run it through your regex.
\s will ignore all whitespace, so by using \s+ it will skip over tabs & CRLF. Since your examples had ;, we will capture all text that is not ;. If there is a space between, then use [^;\s]+ instead.
string text = #"Insert into
Inventory.tableA;
Insert into Orders;";
var tableNames = Regex.Matches(text, #"(?:Insert into\s+)(?<Name>[^;]+)(?:;)")
.OfType<Match>()
.Select (mt => mt.Groups["Name"].Value);
Console.WriteLine ("Tables: {0}", string.Join(" ", tableNames));
/* Tables: Inventory.tableA Orders */
Related
I have a list of items with different ids which represent a SQL table's PK values.
Is there any way to build an efficient and safe statement?
Since now I've always prepared a string representing the statement and build it as I traversed the list via a foreach loop.
Here's an example of what I'm doing:
string update = "UPDATE table SET column = 0 WHERE";
foreach (Line l in list)
{
update += " id = " + l.Id + " OR";
}
// To remove last OR
update.Remove(update.Length - 3);
MySqlHelper.ExecuteNonQuery("myConnectionString", update);
Which feels very unsafe and looks very ugly.
Is there a better way for this?
So yeah, in SQL you've got the 'IN' keyword which allows you to specify a set of values.
This should accomplish what you would like (syntax might be iffy, but the idea is there)
var ids = string.Join(',', list.Select(x => x.Id))
string update = $"UPDATE table SET column = 0 WHERE id IN ({ids})";
MySqlHelper.ExecuteNonQuery("myConnectionString", update);
However, the way you're performing your SQL can be considered dangerous (you should be fine as this just looks like ids from a DB, who knows, better to be safe than sorry). Here you're passing parameters straight into your query string, which is a potential risk to SQL injection which is very dangerous. There are ways around this, and using the inbuilt .NET 'SqlCommand' object
https://www.w3schools.com/sql/sql_injection.asp
https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand?view=dotnet-plat-ext-6.0
It would be more efficient to use IN operator:
string update = "UPDATE table SET column = 0 WHERE id IN (";
foreach (Line l in list)
{
update += l.Id + ",";
}
// To remove last comma
update.Remove(update.Length - 1);
// To insert closing bracket
update += ")";
If using .NET Core Framework, see the following library which creates parameters for a WHERE IN. The library is a port from VB.NET which I wrote in Framework 4.7 years ago. Clone the repository, get SqlCoreUtilityLibrary project for creating statements.
Setup.
public void UpdateExample()
{
var identifiers = new List<int>() { 1, 3,20, 2, 45 };
var (actual, exposed) = DataOperations.UpdateExample(
"UPDATE table SET column = 0 WHERE id IN", identifiers);
Console.WriteLine(actual);
Console.WriteLine(exposed);
}
Just enough code to create the parameterizing SQL statement. Note ActualCommandText method is included for development, not for production as it reveals actual values for parameters.
public static (string actual, string exposed) UpdateExample(string commandText, List<int> identifiers)
{
using var cn = new SqlConnection() { ConnectionString = GetSqlConnection() };
using var cmd = new SqlCommand() { Connection = cn };
cmd.CommandText = SqlWhereInParamBuilder.BuildInClause(commandText + " ({0})", "p", identifiers);
cmd.AddParamsToCommand("p", identifiers);
return (cmd.CommandText, cmd.ActualCommandText());
}
For a real app all code would be done in the method above rather than returning the two strings.
Results
UPDATE table SET column = 0 WHERE id IN (#p0,#p1,#p2,#p3,#p4)
UPDATE table SET column = 0 WHERE id IN (1,3,20,2,45)
This question already has answers here:
IN Operator in OLEDB
(2 answers)
Closed 1 year ago.
I have some data like name,firstname,surname,std,Rollno.
Using C#, I want to convert this into
('name', 'surname', 'std', 'Rollno')
so that I can use this this data to query into the SQL/MySQL DB like -
SELECT *
FROM Table1
WHERE UserCommunicationId IN ('name', 'surname', 'std', 'Rollno');
Instead of
SELECT *
FROM Table1
WHERE UserCommunicationId IN ('name,surname,std,Rollno');
You can try below below logic
public static class SQLQueryExtensions
{
public static string ColumnFormat(this String str)
{
return "(" + //Include first parenthesis
string.Join(", ", str.Split().Select(x => $"'{x}'")) //Add single quote to each column
+ ")"; //Include last parenthesis
}
}
You can do it in one line as well,
var inputStr = "name,firstname,surname,std,Rollno";
var result = "(" + string.Join(", ", inputStr.Split().Select(x => $"'{x}'")) + ")";
Try Online
Use blow logic, will solve your problem.
string inputStr = "name,firstname,surname,std,Rollno";
string result = string.Join(",", inputStr.Split(',').Select(x => string.Format("'{0}'", x)).ToList());
Output = 'name','firstname','surname','std','Rollno'
One approach I can come up is that:
Set the whole string into query as a parameter.
Split it in a WITH query.
LEFT JOIN it in the main query.
NOT NULL to check if there's any hit.
I've wrote an example below, but I am Oracle user so I am not sure if these syntax are right, not even tested, just googled around. Only take it as an reference to the explanation of the idea.
WITH RECURSIVE targets (stringBuffer, word) AS (
SELECT
#Parameter
,NULL
UNION ALL
SELECT
SUBSTRING(stringBuffer, LEAST(LENGTH(SUBSTRING_INDEX(stringBuffer, ',', 1) + 1, LENGTH(stringBuffer)))
,SUBSTRING_INDEX(stringBuffer, ',', 1)
WHERE LENGTH(word) > 0
OR LENGTH(stringBuffer) > 0 -- I am not really sure about these
)
SELECT *
FROM Table1
LEFT JOIN targets ON targets.word = Table1.UserCommunicationId
WHERE targets.word IS NOT NULL;
Then, in C#, set Parameter for your query command in string like this
string s = "name,firstname,surname,std,Rollno";
Edit:
Or, simply:
SELECT *
FROM Table1
WHERE REGEXP_LIKE(UserCommunicationId, #Parameter)
;
While setting the Parameter in C# as:
string s = "name|firstname|surname|std|Rollno";
Notice that if the keywords can be input by user, you still have the problem where user may enter .+ and it responds every data to them as long as there's no other condition added.
But personally, I think there's a potential issue in your design if you really need an unknown length of IN-CLAUSE in your query. If keywords that can be applied are limited in number, you can, rough but it's my team's current criteria, concat the WHERE section keyword by keyword in C#.
This is my code
var compainNames = (from row in DTgraph.AsEnumerable()
group row by row.Field<string>("Campaign") into grp
select new
{
CampaignName = grp.Key
}).ToList();
var dataForOneCampaint = DTgraph.Select("Campaign = " + compainNames[i].ToString()).ToList();
where DTgraph is a datatable.
I got this exception:
Cannot interpret token '{' at position 12.
Could you help please?
I debug and i can see that compainNames has 3 strings
the exception in this line
var dataForOneCampaint = DTgraph.Select("Campaign = " + compainNames[i].ToString()).ToList();
DataTable.Select method use the same rules with DataColumn.Expression property for creating filters.
From it's documentation;
User-Defined Values
User-defined values may be used within expressions to be compared with
column values. String values should be enclosed within single
quotation marks (and each single quotation character in a string value
has to be escaped by prepending it with another single quotation
character).
I believe you can use;
var dataForOneCampaint = DTgraph
.Select("Campaign = '" + compainNames[i].ToString() + "'")
.ToList();
Or use String.Format as Felipe mentioned.
Try using the ' char between the string, for sample:
var dataForOneCampaint = DTgraph
.Select(string.Format("Campaign = '{0}'", compainNames[i].ToString()))
.ToList();
I want to be able to take content from a web page text box and pass it to a SQL Server stored proc that will perform a search on a full-text catalog.
In C#, we are using a SQL Command object to setup parameters needed to call on a stored procedure: one of the parameters contains the query text:
public List<SearchItems> mySearchFunction(string query.....)
{
blah//....
SqlParameter paramQry = new SqlParameter();
paramQry.ParameterName = "#qry";
paramQry.SqlDbType = SqlDbType.NVarChar;
paramQry.Direction = ParameterDirection.Input;
paramQry.Value = query;
cmd.Parameters.Add(paramQry);
......
}
On the SQL side of things, the stored proc will use the query text as:
SELECT RequiredColumns
FROM tableName
WHERE CONTAINS((ourTableField), #qry).....
This is fine for simple (one-word) search terms.
How do I convert/pass multi-word or phrases in C# to work in SQL?
For example, if a user enters "Barack Obama" in the text field, we would want to setup the #qry value passed to the SP to look this in the query:
WHERE CONTAINS((ourTableField),'"Barack" AND "Obama"')
Do I need to do some sort of string construction in C#? I have tried this by trying to insert AND and quote literals, but the issue of escaping single and double quotes is tripping me up, and I am concerned this is not a sensible or safe way to continue trying.
I have been trying to build this in C# using a StringBuilder object, along the lines of:
List<string> queryParts = query.Split(' ').ToList();
string queryVal = string.Empty;
if (queryParts != null & queryParts.Count > 0)
{
StringBuilder sb = new StringBuilder();
sb.Append("'");
foreach (string searchPart in queryParts)
{
sb.Append("\"" + searchPart + "\"" + "AND");
}
//bit hacky, removing trailing AND
sb.Append("'");
sb.Replace("AND'", string.Empty);
sb.Append("'");
queryVal = sb.ToString();
return queryVal
Then assign paramQry.Value = queryVal;
However this results in escaping - slashes etc. being returned. I am sure this is not just a case of Visual Studio rendering these characters in the debugger -the exception that comes back is SQLException.
I have seen similar posts where it is mentioned that the Parameter object can handle escaping, but I cannot see how this works or find any clear examples that may help.
If this is not feasible, does this mean doing some sort of string manipulation in SQL?
This type of solution is new to me, so TIA for advice offered.
You can use StringBuilder to construct your sentence by adding and to every empty space, and build a sentence out of the content of the textbox
Here is a broken down version of my problem :
I have a method. The argument is a list of integers. I wish to create an optimised SQL query that returns a particular value on all rows where the id of that row is equal to one of the integers in the argument list. Simple, right ?
I have a feeling I have made it more difficult than it needs to be :
private List<string> ReturnValue(List<int> ids)
{
List<string> ValuesIWantToReturn = new List<string>();
StringBuilder sb = new StringBuilder("SELECT ValueIWantToReturnfrom table WHERE ");
foreach (int id in ids)
{
sb.Append( string.Format("ID = {0} OR ", id) );
}
sb.Remove(sb.Length - 3, 3); //remove trailing "OR"
sb.Append(";");
SqlDataReader reader = RunSelectQuery( sb.ToString() );
while (reader.Read())
{
ValuesIWantToReturn.Add(reader.GetString(0));
}
return ValuesIWantToReturn;
}
Any feedback on the general readability and structure of my code would be appreciated too. It's always nice to improve :)
You can use the IN syntax rather than OR
WHERE id IN (1,2,3,4)
It may be more efficient to pass the list of IDs as a table valued parameter to a stored procedure, and then join that to your table, but that may be over engineering the solution - depending on how long your list of numbers is.
Instead of your loop, you can use string.Join to build your list
string query = "SELECT ValueIWantToReturn from table WHERE ID IN ("
+ string.Join(",", ids)
+ ")";
(assuming that there is always at least one number in the list)