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)"
Related
(first sorry for my English):
I want to temporarily change auto-number column to int64 data type to import records from another database. After importing the records, I want to change it back to auto-number.
My Problem:
I try to use the table.Columns[i].AutoIncrement property to check if this column is auto-number and get its index so that I can change its datatype, but this property didn't work for me, it returned false for all columns.
I work with 2010/2013 Access database.
So I want to know what to do to get index of auto-number column?
You can use this approach
// Bogus query, we don't want any record, so add a always false condition
OleDbCommand cmd = new OleDbCommand("SELECT * FROM aTable where 1=2", con);
OleDbDataAdapter da = new OleDbDataAdapter(cmd);
DataTable test = new DataTable();
da.FillSchema(test, SchemaType.Source);
for(int x = 0; x < test.Columns.Count; x++)
{
DataColumn dc = test.Columns[x];
Console.WriteLine("ColName = " + dc.ColumnName +
", at index " + x +
" IsAutoIncrement:" + dc.AutoIncrement);
}
guys i have an SQL statement returning more than 1 value.
I am trying to use the StreamReader to get the values into an array as below
string sql = "select distinct COLUMN_NAME from INFORMATION_SCHEMA.KEY_COLUMN_USAGE where TABLE_NAME=' " + table + "' and CONSTRAINT_NAME like 'PK_%'";
SqlConnection conn2 = new SqlConnection(cnstr.connectionString(cmbDatabase.Text));
SqlCommand cmd_server2 = new SqlCommand(sql);
cmd_server2.CommandType = CommandType.Text;
cmd_server2.Connection = conn2;
conn2.Open();
//reader_sql = new StreamReader();
SqlDataReader reader_sql = null;
string[] colName = new string[200];
reader_sql = cmd_server2.ExecuteReader();
while (reader_sql.Read());
for (int rr = 0; rr < 20; rr++)
{
colName[rr] = reader_sql["COLUMN_NAME"].ToString();
}
It is not working, what am I doing wrong guys ?
You've got a stray ; turning your while into a tight loop, so instead try:
while (reader_sql.Read())
for (int rr = 0; rr < 20; rr++)
{
colName[rr] = reader_sql["COLUMN_NAME"].ToString();
}
You get the exception because
while (reader_sql.Read());
should be
while (reader_sql.Read())
{
for (int rr = 0; rr < 20; rr++)
{
colName[rr] = reader_sql["COLUMN_NAME"].ToString();
}
}
Perhaps you should remove the semicolon at the end of Read
while (reader_sql.Read())
{
for (int rr = 0; rr < 20; rr++)
colName[rr] = reader_sql["COLUMN_NAME"].ToString();
}
However, if your intention is to retrieve the columns belonging to the primary key, your code is wrong because you add 20 times the same primary key column, then repeat the same for the remaining columns ending with an array of 20 strings all equals to the last column in the primary key set. I think you should change your code to use a List(Of String) instead of a fixed length array and let the reader loop correctly on the primary key columns retrieved
List<string> pks = new List<string>();
while (reader_sql.Read())
{
pks.Add(reader_sql["COLUMN_NAME"].ToString());
}
EDIT: I have just noticed that your query contains a space before the table name. The string concatenation then produces an invalid table name, the query is syntactically right but doesn't return any data
string sql = "select distinct COLUMN_NAME from INFORMATION_SCHEMA.KEY_COLUMN_USAGE " +
"where TABLE_NAME='" + table + "' and CONSTRAINT_NAME like 'PK_%'";
^ space removed here
And while you are at it, remove the string concatenation and use a parameterized query.....
string sql = "select distinct COLUMN_NAME from INFORMATION_SCHEMA.KEY_COLUMN_USAGE " +
"where TABLE_NAME=#tName and CONSTRAINT_NAME like 'PK_%'";
SqlCommand cmd_server2 = new SqlCommand(sql, connection);
connection.Open();
cmd_server2.Parameters.AddWithValue("#tName", table);
I have an undefined amount of DataTables. I get them from my DataBase, each DataTable stands for one Table in my DataBase, I dont use all Tables of the DataBase just the few I need (these were selectet earlier in the code) and not all columns (same like the tables).
Now my problem: I want to show them in a DataGrid one below the other with breaks between them for the tablename.
This is how i get my DataTables:
List<DBTable> selectedTbl = DBObject.SingDBObj.GetSelectedTables();
foreach (DBTable tbl in selectedTbl)
{
string cols = tbl.GetSelectedColumnNames();
string query = #"SELECT " + cols + " FROM [" + DBObject.SingDBObj.DataSource + "].[" + DBObject.SingDBObj.Database + "].[" + tbl.Schema + "].[" + tbl.Name + "];";
DataTable DTShow = DBObject.SingDBObj.ExecuteQuery(query);
}
dataGridShowColmns.DataContext = ??;
Is there an easy way to do this?
You maybe mean something like:
DataSet ds = new DataSet();
// dataset is here just initialized for demonstration, you would first
// get the tables from database and populate dataset
for (int i = 0; i < ds.Tables.Count; i++)
{
DataTable dt = ds.Tables[i];
foreach (DataRow dr in dt.Rows)
{
dataGridView1.Rows.Add(dr);
}
}
In SQL name to a dataset cannot be assigned, only way you can do it in c#/VB. like
Dataset.Table[0].Name = "MyTable";
I have two databases in MySQL and SQL Server, and I want to create tables in SQL Server and copy all rows from the table in MySQL into the new table in SQL Server.
I can create table in SQL Server same as MySQL, with this code:
List<String> TableNames = new List<string>();
{
IDataReader reader=
ExecuteReader("SELECT Table_Name FROM information_schema.tables WHERE table_name LIKE 'mavara%'",MySql);
while (reader.Read()) {
TableNames.Add(reader[0].ToString());
}
reader.Close();
}
foreach (string TableName in TableNames) {
IDataReader reader =
ExecuteReader("SELECT Column_Name,IS_NULLABLE,DATA_TYPE FROM information_schema.columns where TABLE_Name='" + TableName + "'",MySql);
List<string[]> Columns = new List<string[]>();
while (reader.Read()) {
string[] column = new string[3];
column[0] = reader[0].ToString();
column[1] = reader[1].ToString();
column[2] = reader[2].ToString();
Columns.Add(column);
}
reader.Close();
// create table
string queryCreatTables= "CREATE TABLE [dbo].[" + TableName + "](\n";
foreach(string[] cols in Columns)
{
queryCreatTables +="["+ cols[0] + "] " + cols[2] + " ";
if (cols[1] == "NO")
queryCreatTables += "NOT NULL";
// else
// queryCreatTables += "NULL";
queryCreatTables += " ,\n ";
}
queryCreatTables += ")";
System.Data.SqlClient.SqlCommand smd =
new System.Data.SqlClient.SqlCommand(queryCreatTables, MsSql);
System.Data.SqlClient.SqlDataReader sreader = smd.ExecuteReader();
sreader.Close();
but I have problem to copy rows from one table into another table.
for select query, I use Idatareader, but I don't know how insert rows to another table.
For inserting rows from one table into another table please refer the below sample query
INSERT INTO Store_Information (store_name, Sales, Date)
SELECT store_name, sum(Sales), Date
FROM Sales_Information
The algorithm is as follows:
1. For each table in source database
2. Get a list of columns for that table
3. Create table in destination database
4. SELECT * FROM the table in source
5. For each row in data
6. Generate INSERT statement and execute on destination database
The information you need for a column is Name, Type, Length, etc.
Then you generate the insert statement by iterating on the columns
var insertStatement = "INSERT INTO " + tableName + " VALUES( ";
foreach( var column in columns )
insertStatement += "#" + column.Name + ",";
insertStatement[insertStatement.Length-1] = ')';
var command = new SqlCommand( insertStatement, MsSql );
// iterate over the columns again, but this time set values to the parameters
foreach( var column in columns )
command.Parameters.AddWithValue( "#"+column.Name, currentRow[column.Name] );
But I have a problem to copy rows from one table into another table.
You can use the SqlDataAdapter.UpdateBatchSize to perform Batch Updates/Inserts with a DataAdapter against the database to copy the data from one table to the other. After you get all records from the first MYSQL table using something like:
//Get the rows from the first mysql table as you did in your question
DataTable mysqlfirstTableRowsTobeCopied = GetDataTableFromMySQLTable();
Then you have to create a commend text that do the INSERT something like:
cmd.CommandText = "INSERT INTO TableName Column_Name, ... VALUES(...)";
Or you can use a stored procedure:
CREATE PROCEDURE sp_BatchInsert ( #ColumnName VARCHAR(20), ... )
AS
BEGIN
INSERT INTO TableName VALUES ( #ColumnNamne, ...);
END
Then:
DataTable mysqlfirstTableRowsTobeCopied = GetDataTableFromMySQLTable();
SqlConnection conn = new SqlConnection("Your connection String");
SqlCommand cmd = new SqlCommand("sp_BatchInsert", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.UpdatedRowSource = UpdateRowSource.None;
// Set the Parameter with appropriate Source Column Name
cmd.Parameters.Add("#ColumnName", SqlDbType.Varchar, 50,
mysqlfirstTableRowsTobeCopied.Columns[0].ColumnName);
...
SqlDataAdapter adpt = new SqlDataAdapter();
adpt.InsertCommand = cmd;
// Specify the number of records to be Inserted/Updated in one go. Default is 1.
adpt.UpdateBatchSize = 20;
conn.Open();
int recordsInserted = adpt.Update(mysqlfirstTableRowsTobeCopied);
conn.Close();
This code actually is quoted from a full tutorial about this subject in the codeproject, you can refer to it for more information:
Multiple Ways to do Multiple Inserts
Assuming that you already have a datatable with rows that you want to merge with another table...
There are two ways to do this...again, assuming you've already selected the data and put it into a new table.
oldTable.Load(newTable.CreateDataReader());
oldTable.Merge(newTable);
Usually that's sth. you can do in SQL directly: INSERT INTO table FROM SELECT * FROM othertable;
For insertion of all record into new table(If the second table is not exist)
Select * into New_Table_Name from Old_Table_Name;
For insertion of all records into Second Table(If second table is exist But the table structure should be same)
Insert into Second_Table_Name from(Select * from First_Table_Name);
Just in case it can help someone, I've found an easy way to do it in C# using SqlDataAdapter. It automatically detects the structure of the table so you don't have to enumerate all the columns or worry about table structure changing. Then bulk inserts the data in the destination table.
As long as the two tables have the same structure, this will work. (For example, copying from a production table to a dev empty table.)
using(SqlConnection sqlConn = new SqlConnection(connectionStringFromDatabase))
{
sqlConn.Open();
using(var adapter = new SqlDataAdapter(String.Format("SELECT * FROM [{0}].[{1}]", schemaName, tableName), sqlConn))
{
adapter.Fill(table);
};
}
using(SqlConnection sqlConn = new SqlConnection(connectionStringDestination))
{
sqlConn.Open();
// perform bulk insert
using(var bulk = new SqlBulkCopy(sqlConn, SqlBulkCopyOptions.KeepIdentity|SqlBulkCopyOptions.KeepNulls, null))
{
foreach(DataColumn column in table.Columns)
{
bulk.ColumnMappings.Add(column.ColumnName, column.ColumnName);
}
bulk.DestinationTableName = String.Format("[{0}].[{1}]", schemaName, tableName);
bulk.WriteToServer(table);
}
}
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