How to get table name from string that contain SELECT statement - c#

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

Related

How can i retrive all grdiview columns values in one iteration without loop?

I'm facing problem in creating table programtically in asp.net c#. I'm working on sql project. I have a gridview and a button,When I click on button then I want that all gridview columns values e.g column_name,data type,allowNull,PrimaryKey etc.
All values inserted inside the "Create Table QUERY" in one iteration and table will be created, But I have a problem. I'm using for loop when loop first time execute then only one row iterate and table created in SQL only one column(Just first row) and when 2nd iteration execute then table name will be same ,so there is a issue.
Kindly tell me how can I resolve this issue.All values successfully inserted into the table but problem is in creating table. Table is created but 'ONLY LAST ' row table is created,Table contain only one row.How can i resolve this issue.
How can i do this?
Here is my "button" code aspx.cs`
public void insert(object sender, EventArgs e)
{
SqlConnection cnn = new SqlConnection("Data Source=HAMEED_KHAN\\SQLEXPRESS;Initial Catalog=db_compiler;Integrated Security=True");
string d=Session["value"].ToString();
SqlCommand cmd2=new SqlCommand("SELECT Database_id FROM Create_db WHERE Database_Name='"+d+"'",cnn);
cnn.Open();
string dbid = cmd2.ExecuteScalar().ToString();
cnn.Close();
int D_ID = Int32.Parse(dbid);
string str = "";
string type = "";
for (int i = 0; i < GridView2.Rows.Count; i++)
{
string tblname = "abc";
str=GridView2.Rows[i].Cells[1].Text.ToString();
type=GridView2.Rows[i].Cells[2].Text.ToString();
string Name = GridView2.Rows[i].Cells[1].Text.ToString();
string Type = GridView2.Rows[i].Cells[2].Text.ToString();
CheckBox allow=GridView2.Rows[i].Cells[3].Controls[0]as CheckBox;
CheckBox primary = GridView2.Rows[i].Cells[4].Controls[0] as CheckBox;
string s = Session["UID"].ToString();
int id = Int32.Parse(s);
string date = DateTime.Now.ToString();
string A = (allow.Checked == true ? "NULL" : "NOT NULL");
string P = (primary.Checked == true ? "PRIMARY KEY" : "");
// string query="USE "+d+" CREATE TABLE ABCD ("+Name+" "+Type+" "+A+")";
// SqlCommand cmd3 = new SqlCommand(query, cnn);
SqlCommand cmd = new SqlCommand("insertTbl", cnn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#Name", tblname);
cmd.Parameters.AddWithValue("#col_name", Name);
cmd.Parameters.AddWithValue("#dtype",Type);
cmd.Parameters.AddWithValue("#dbId", D_ID);
cmd.Parameters.AddWithValue("#allow",(allow.Checked==true ? "true" : "false"));
cmd.Parameters.AddWithValue("#primary", (primary.Checked == true ? "true" : "false"));
cmd.Parameters.AddWithValue("#user", id);
cmd.Parameters.AddWithValue("#date", date);
SqlDataAdapter ad = new SqlDataAdapter(cmd);
cnn.Open();
cmd.ExecuteNonQuery();
// cmd3.ExecuteNonQuery();
cnn.Close();
}
string str1=str;
string str2=type;
//string AA="ALLOW NULL";
// string queryy =string.Format(#"USE {"+d+"}; IF (NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND TABLE NAME = 'ABCDE'))CREATE TABLE ABCDE ({"+str1+"} {"+type+"} {"+AA+"})");
string queryy="USE "+d+" If not exists (select name from sysobjects where name = 'Customers') CREATE TABLE Customers("+str1+" "+type+")";
SqlCommand cmd4 = new SqlCommand(queryy, cnn);
cnn.Open();
cmd4.ExecuteNonQuery();
cnn.Close();
}
You should make sure to avoid recreating the same table by using a rerunnable script. For CREATE TABLE, checking its existence is done this way:
string query = String.Format(#"
USE {0};
IF (NOT EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = '{1}'))
CREATE TABLE {1} ({2} {3} {4})", d, "ABCD", Name, Type, A);
Notice that I have used String.Format to increase readability and avoid string concatenation (strings are immutable, so many instances are created when using + operator).
However, consider moving your CREATE TABLE outside of for loop, if your intention is to create once and insert multiple times. Anyway, existence check should be performed.
From C# 6.0, you can use both verbatim and interpolation (actually, string interpolation was introduced in 6.0). Something like this:
string query = $#"
USE {d};
IF (NOT EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = 'ABCD'))
CREATE TABLE ABCD ({Name} {Type} {A})";

Microsoft SQL Server, selecting an id and inserting into another table

I have this code for inserting some rows into the following table - and i am getting the id from another table that is already populated. Using Compact 4.0 Local Database Here with C# and Razor.
get id from sortedWord table:
Id | SortedWord
0 act
insert data into words table:
Id | Word | SortedId
0 cat 0
1 tac 0
for (var i = 0; i < words.Count(); i++){
queryString = "SELECT Id FROM SortedWords WHERE SortedWord = #0";
var sortedId = db.QuerySingle(queryString, sortWord(words[i]));
queryString = "INSERT INTO Words (Word, SortedId) VALUES (#0, #1)";
db.Query(queryString, words[i], sortedId.Id);
}
Trouble is the select statement is inefficient, is it possible to do this without a select statement, something like select into: I saw some examples here but cant make sense of it.
http://technet.microsoft.com/en-us/library/ms189872(v=sql.105).aspx
Yes you can simply use:
INSERT Words (Word, SortedID)
SELECT #0, ID
FROM SortedWords
WHERE SortedWord = #1;
Then your c# would become:
queryString = " INSERT Words (Word, SortedID) SELECT #0, ID FROM SortedWords WHERE SortedWord = #1;"
db.Query(queryString, words[i], sortedwords[i]);
Or you could just embed your first query into your second:
queryString = "INSERT INTO Words (Word, SortedId) VALUES (#0, (SELECT TOP 1 Id FROM SortedWords WHERE SortedWord = #1))";
db.Query(queryString, words[i], sortWord(words[i]));
However, if you are using SQL Server 2008 or later, I would go one further and do all the inserts at once using a Table valued parameters. The first step would be to create the type:
CREATE TYPE dbo.TwoStringList AS TABLE (Value1 VARCHAR(MAX), Value2 VARCHAR(MAX));
I have used a generic name so the type is more reusable. You can then create a procedure that accepts this type as a parameter:
CREATE PROCEDURE dbo.InsertWords #StringList dbo.TwoStringList READONLY
AS
INSERT Words (Word, SortedID)
SELECT sl.Value1, sw.ID
FROM SortedWords sw
INNER JOIN #StringList sl
ON sw.SortedWord = sl.Value2;
Then you can pass this to an SQL command with something like this:
var datatable = new DataTable();
datatable.Columns.Add(new DataColumn("Value1", typeof(string)));
datatable.Columns.Add(new DataColumn("Value2", typeof(string)));
for (var i = 0; i < words.Count(); i++)
{
var dr = datatable.NewRow();
dr[0] = words[i];
dr[1] = sortedwords[i];
datatable.Rows.Add(dr);
}
using (var connection = new SqlConnection("your Connection String"))
using (var command = new SqlCommand("dbo.InsertWords", connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter{
ParameterName = "#StringList",
SqlDbType = SqlDbType.Structured,
TypeName = "dbo.TwoStringList",
Value = datatable
});
connection.Open();
command.ExecuteNonQuery();
}
Example on SQL Fiddle
I'm not very familiar with C# syntax, but you can do something like this:
INSERT INTO Words (Word, SortedId)
SELECT #0,id
FROM sortedWords
WHERE sortedWord = #0;

How to query from SQL comparing with array members?

So I have an array containing computer names called hostnames[].
Contains: compname1, compname2, compname3 etc.
Actually it gets it's members from another SQL query.
I have a data table and I need to query all rows where hostname column has any of the computer names in my array.
something like:
select * from table where hostname in hostnames[]
How should I proceed to achieve my goal?
EDIT:
I was thinking on the below:
string temp = "'" + hostnames[0] + "'";
for(int i=1; i<hostnames[].Lenght; i++)
{
temp = temp + ",'" + hostnames[i] + "'";
}
string query = "SELECT * FROM table WHERE hostname IN (" + temp + ")";
The best way to use parameters for an IN clause is to use Table-valued parameters, in your case you will need a type as a table with one nvarchar column. I've used a generic name so the type can be reused without confusion:
CREATE TYPE dbo.StringList AS TABLE (value NVARCHAR(MAX));
Then it is simply a case of adding your values to a DataTable and passing this as a parameter to your select command:
var dataTable = new DataTable();
dataTable.Columns.Add(new DataColumn("Value", typeof(string)));
for (int i = 0; i < hostnames.Length; i++)
{
var dr = dataTable.NewRow();
dr[0] = "";
dataTable.Rows.Add(dr);
}
using (var connection = new SqlConnection("connectionString"))
using (var command = new SqlCommand("SELECT * FROM Table WHERE HostName IN (SELECT Value FROM #StringList)", connection))
{
SqlParameter stringListParameter = new SqlParameter("#StringList", SqlDbType.Structured);
stringListParameter.Value = dataTable;
stringListParameter.TypeName = "dbo.StringList";
command.Parameters.Add(stringListParameter);
// OPEN CONNECTION EXECUTE COMMAND ETC
}
There are 4 ways to achieve what you want. You choose what works best for you.
Use IN as in your code
Break down to OR. A IN (1, 2, 3) => A=1 OR A=2 OR A=3
Use Table Valued Parameters
User the sql query passed earlier. ex: "SELECT * FROM table WHERE hostname IN (Select hostname from tableusedEarlier)"

asp.net C# sql query dependant on session data

I have a Session, which is list int, and I need to make a query that will take from a database only those rows that have the PK value that exists in Session.
I was thinking of doing it with the IN function, or making a new datatable with 1 collumn and values from the Session and doing a double join, probably left...
I just dont know how to make a table from a list.
What I have so far:
String ConnString = "Data Source=BRACO-PC\SQL1;Initial Catalog=DiplomskiSQL1SQL;Integrated Security=True";
SqlConnection Conn = new SqlConnection(ConnString);
Conn.Open();
DataTable ukosarici = new DataTable();
SqlDataAdapter da = new SqlDataAdapter("Select Proizvodi.ime, TipProizvoda.tip, Proizvodi.dimenzije, Proizvodi.cijena from Proizvod LEFT JOIN TipProizvoda On Proizvod.tip=TipProizvoda.id_t WHERE Proizvod.id_p IN ", Conn);
SqlCommandBuilder cmd = new SqlCommandBuilder(da);
da.Fill(ukosarici);
GridView1.DataSource = ukosarici;
GridView1.DataBind();
Conn.Close();
Create a temporary table or table variable, insert the ints into it using INSERT or BULK INSERT, do a join in the SQL query then drop the temp table or table variable.
There are many ways you could do this, but one of my preferred methods is to serialize the list to a CSV, e.g. '1,3,5,33'. I then use a custom SQL Table function to de-serialize the list and filter in the database:
SELECT * FROM mytable t
JOIN dbo.ufn_CSVtoTextList('1,3,5,33' , ',') csv
ON csv.[Entry] = t.Id
The ufn_CSVtoTextList function CREATE script is below:
CREATE FUNCTION [dbo].[ufn_CSVToTextlist] ( #StringInput nVARCHAR(max) ,#SepChar nchar(1) = ',')
RETURNS #OutputTable TABLE ( [Entry] nVarchar(255), [index] int identity (0,1) )
AS
BEGIN
DECLARE #Entry nVarChar(255)
WHILE LEN(#StringInput) > 0
BEGIN
SET #Entry = LEFT(#StringInput,
ISNULL(NULLIF(CHARINDEX(#SepChar, #StringInput) - 1, -1),
LEN(#StringInput)))
SET #StringInput = SUBSTRING(#StringInput,
ISNULL(NULLIF(CHARINDEX(#SepChar, #StringInput), 0),
LEN(#StringInput)) + 1, LEN(#StringInput))
INSERT INTO #OutputTable ( [Entry] )
VALUES ( #Entry )
END
RETURN
END
Try by changing your SqlDataAdapter Call as follows
List<int> list ; // Assign with your session int list values
List<string> l2 = list.ConvertAll<string>(delegate(int i) { return i.ToString(); });
string query = "Select Proizvodi.ime, TipProizvoda.tip, Proizvodi.dimenzije, Proizvodi.cijena from Proizvod LEFT JOIN TipProizvoda On Proizvod.tip=TipProizvoda.id_t WHERE Proizvod.id_p IN (";
query = query + string.Join(",", l2.ToArray()) + ")";
SqlDataAdapter da = new SqlDataAdapter(query, Conn);

update sql statement with unknown name/amount of params

I have a classic ASP site, that I am slowly upgrading. I would like to create a function to securely update a SQL database without specifying parameters manually. Something just a tad more dynamic.
(I do not want to use entity framework or Linq)
Here is the code so far:
string updateSql = "UPDATE sometable" + "SET test1= #testData1 " + "WHERE a = #aData1";
SqlCommand UpdateCmd = new SqlCommand(updateSql, conn);
UpdateCmd.Parameters.Add("#testData1 ", SqlDbType.NVarChar, 10, "testData1 ");
UpdateCmd.Parameters.Add("#aData1", SqlDbType.NVarChar, 20, "aData1");
UpdateCmd.Parameters["#testData1 "].Value = "21515";
UpdateCmd.Parameters["#aData1"].Value = "32t3t";
UpdateCmd.ExecuteNonQuery();
pseudo-code (what I would like to achieve)
Create an Ilist covering all variables {get; set:} [validate type/length here]
For every variable that contains a value (without validation issues) create sql update string.
Execute it.
Possible problem:
The only problem I can foresee, is that the list may have 500 variables, but each SQL update may only have only 2 or 3 columns being updated. Is this not efficient?
you need to do something like this....needs more coding obviously....
static void Main(string[] args)
{
var values = new Dictionary<string, object>( );
values.Add( "name", "timmerz" );
values.Add( "dob", DateTime.Now );
values.Add( "sex", "m" );
SqlUpdate( "sometable", values );
}
public static void SqlUpdate( string table, Dictionary<string,object> values, string where )
{
var equals = new List<string>( );
var parameters = new List<SqlParameter>( );
var i = 0;
foreach( var item in values )
{
var pn = "#sp" + i.ToString( );
equals.Add( string.Format( "{0}={1}", item.Key, pn ) );
parameters.Add( new SqlParameter( pn, item.Value ) );
i++;
}
string command = string.Format( "update {0} set {1} where {2}", table, string.Join( ", ", equals.ToArray( ) ), where );
var sqlcommand = new SqlCommand(command);
sqlcommand.Parameters.AddRange(parameters.ToArray( ) );
sqlcommand.ExecuteNonQuery( );
}
I'm not sure I fully understand what you're trying to do, but this might be close to what you're looking for. You can create an arbitrarily long list of parameters and respective values, then build the corresponding UPDATE dynamically from that list.
//set up SqlCommand
SqlCommand UpdateCmd = new SqlCommand();
UpdateCmd.Connection = conn;
//build your dictionary (probably happens elsewhere in your code)
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("col1", "col1 value");
parameters.Add("col2", 42);
parameters.Add("col3", DateTime.Now);
//build a command string and add parameter values to your SqlCommand
StringBuilder builder = new StringBuilder("UPDATE sometable SET ");
foreach(KeyValuePair<string, object> parameter in parameters) {
builder.Append(parameter.Key).Append(" = #").Append(parameter.Key).Append(",");
UpdateCmd.Parameters.AddWithValue("#" + parameter.Key, parameter.Value);
}
builder.Remove(builder.Length - 1,1);
//set the command text and execute the command
UpdateCmd.CommandText = builder.ToString();
UpdateCmd.ExecuteNonQuery();
If you are using SQL Server 2008 you have the option of passing in the parameters and their values as a table to a Stored Procedure.
Inside the Stored Procedure you can join the table to be updated with the table passed in. That would probably be more efficient than creating hundreds of sep update statements.
Here is a link that may help http://msdn.microsoft.com/en-us/library/bb675163.aspx
And here is some sample code based on the code you posted in your question
First Create a table to play with and populate it with some data
CREATE TABLE [dbo].[sometable](
[Test1] [nvarchar](100) NULL,
[a] [nvarchar](100) NULL
) ON [PRIMARY]
Insert sometable Select 'rerere', '122342'
Insert sometable Select 'sfsfw', '343'
Insert sometable Select 'sfdrgss', '434545'
Insert sometable Select 'srgegrgeg', '3939932'
Then Create the Type in SQL Server
Create TYPE dbo.ParamsType AS TABLE
( Test1 nvarchar(100), a nvarchar(100) )
Then Create the Stored Procedure that accepts the type as a parameter
CREATE PROCEDURE usp_UpdateSomeTable
#Parameters dbo.ParamsType READONLY
AS
BEGIN
SET NOCOUNT ON;
UPDATE sometable
SET sometable.Test1 = p.Test1
FROM sometable INNER JOIN #Parameters as p
ON sometable.a = p.a;
END
GO
To test from SQL Server Management Studio you can run
Declare #t as ParamsType
Insert #t Select 'newValue1', '122342'
Insert #t Select 'morenew ', '343'
Insert #t Select 'again', '434545'
Insert #t Select 'OnceMore', '3939932'
exec usp_UpdateSomeTable #Parameters=#t
To Test from C# Try
static void Main(string[] args)
{
System.Data.DataTable YourData = new DataTable("Parameters");
DataColumn column;
DataRow row;
column = new DataColumn();
column.DataType = System.Type.GetType("System.String");
column.ColumnName = "Test1";
YourData.Columns.Add(column);
column = new DataColumn();
column.DataType = System.Type.GetType("System.String");
column.ColumnName = "a";
YourData.Columns.Add(column);
row = YourData.NewRow();
row["Test1"] = "newValue1";
row["a"] = "122342";
YourData.Rows.Add(row);
row = YourData.NewRow();
row["Test1"] = "morenew";
row["a"] = "343";
YourData.Rows.Add(row);
row = YourData.NewRow();
row["Test1"] = "again";
row["a"] = "434545";
YourData.Rows.Add(row);
SqlConnectionStringBuilder connString = new SqlConnectionStringBuilder();
connString.DataSource = "127.0.0.1";
connString.InitialCatalog = "SO";
connString.IntegratedSecurity = true;
using (SqlConnection conn = new SqlConnection())
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = "usp_UpdateSomeTable";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
SqlParameter p = cmd.Parameters.AddWithValue("#Parameters", YourData);
p.SqlDbType = SqlDbType.Structured;
p.TypeName = "dbo.ParamsType";
cmd.Connection = conn;
conn.ConnectionString = connString.ConnectionString;
conn.Open();
cmd.ExecuteNonQuery();
}
}

Categories