Insert column name at runtime in c# [duplicate] - c#

How can I pass a columns name by parameter,
follow an example
DataTable dt = new DataTable();
// Here I fill my datatable
for (int i = 0; i < dt.Rows.Count; i++)
{
for (int j = 0; j < dt.Columns.Count; j++)
{
string columnsname = dt.Rows[i][dt.columns[j].toString()].toString();
SqlCommand comando = new SqlCommand();
comando.commandText = "UPDATE Sales.Store SET #columnname = #demographics where id = #id";
comando.Parameters.Add(new SqlParameter("#columnname", columname));
comando.Parameters.Add(new SqlParameter("#dados2", dados2));
comando.ExecuteNonQuery();
comando.Clear();
comando.Dispose()
}
}
This doesn't work, but I have 88 columns, and I need update all data in every 88 columns in each row.

You cannot parameterize column names.
To do what you want you will need to resort to dynamic SQL.

Well, if you have 30,000 rows with 88 columns, and you need to update all 88 columns, you probably want to rethink your database schema.
Itay.

I have figured out a way to include a work around for parametrized column names. I had the same problem but came up with a different way and since I would be the only one using the column names then I believe this is still a safe bet.
String sqlcomm = "SELECT * FROM Asset WHERE " + assetColName + " = ";
command.CommandText = sqlcomm + "$assetColValue";
//command.CommandText = #"SELECT * FROM Asset WHERE $assetColName = '$assetColValue'";
//command.Parameters.AddWithValue("$assetColName", assetColName);
command.Parameters.AddWithValue("$assetColValue", assetColValue);
As you can see from the code above. I tried almost what you did which I then had to comment out. I then concatenated strings together and was able to use my parametrized column name and value which then the value is securely added. The column name however is not secured but this is a method that only I will be using so its still somewhat safe. You can add regular expressions if you want to be more secure but you get the idea of the fix.

Just concatenate the sql string:
"UPDATE Contracts set " + columnName + " = #columnValue where ID = #ID"
Where column name is a string that represents a column in the table

Related

C# SQLite multiple keyword with like command

I used SQLite. The user will pull the days from checkbox and I'll show it in data grid view but the date is recorded as day and time so I have to use like instead of in command.
DataSet dataSet122;
listBox1.Items.Clear();
SQLiteConnection connection = new SQLiteConnection("Data Source =log.sqlite;Version=3;");
string search = checkBoxComboBox1.Text;
string[] array = search.Split(',');
for (int i = 0; i < array.Length; i++)
{
array[i] = "'" + array[i] + "'";
}
string names = String.Join(",", array);
listBox2.Items.Add(names);
string query = "SELECT * FROM Gemkay1 WHERE ZAMAN LIKE (" + names + ")";
command = new SQLiteCommand(query, connection);
connection.Open();
adapter = new SQLiteDataAdapter(command);
dataSet122 = new DataSet();
adapter.Fill(dataSet122, "Gemkay1");
dataGridViewSummary1.DataSource = dataSet122.Tables["Gemkay1"];
SQL syntax for all people where name ends with SMITH or WRIGHT:
WHERE name LIKE '%SMITH' OR name LIKE '%WRIGHT'
LIKE is not the same as IN - it accepts a single string argument on the right hand side. If you want multiple LIKEs you must repeat the LIKE clause separated by OR
IN can be used with multiple string but it does not accept wildcards:
WHERE name IN ('playwright', 'cartwright', 'shipwright')
If you try and put a wildcard in it will literally match that character.
-
As an aside, don't make SQL like you're doing there, with string concatenation of the values. Concatenate parameters in instead and give them values, for example:
var names = new []{"%wright", "%smith"};
var sql = new SqliteCommand("SELECT * FROM t WHERE 1=0 ");
for(int p = 0; p<names.Length; p++){
sql.CommandText += " OR name like #p" + p;
sql.Parameters.AddWithValue("#p"+p, names[p]);
}
This I what I mean when I say "concatenate parameters in, then give them a value".
If you ever work with sqlserver read this blog post
Use IN operator to select data where multiple values
"SELECT * FROM Gemkay1 WHERE ZAMAN IN ('2021-02-01','2021-02-02')";
to ignore time from date you can use date function:
"SELECT * FROM Gemkay1 WHERE date(ZAMAN) IN ('2021-02-01','2021-02-02')";
See SQLite date and time functions documentation for more info.

SqlBulkCopy datatypes conversion

I have two DB tables, they have the same columns but their data types are different(E.g.: "Check" column is of a type integer in table 1, but varchar in table2). I am trying to copy the data from one table to another by using BulkCopy. I have a code like:
using (SqlBulkCopy sbc = new SqlBulkCopy(connectionString, SqlBulkCopyOptions.KeepIdentity))
{
cmdSQLT = new SqlCommand("SELECT " + ColumnsNames + " FROM [transfer].[" + SelectedScheme + ".OldTable]", conn);
cmdSQLT.CommandTimeout = 1200;
reader = cmdSQLT.ExecuteReader();
sbc.ColumnMappings.Add("CHECK", "CHECK");
sbc.DestinationTableName = "[" + SelectedScheme + "_Newtable]";
sbc.BulkCopyTimeout = 1200;
sbc.WriteToServer(reader);
}
I am getting an error saying
The locale id '0' of the source column 'CHECK' and the locale id
'1033' of the destination column 'CHECK' do not match.
This is happening due to the data types differences between the tables. How can I make data type conversion in the previous code?
Your help is much appreciated!
You can do the conversion on source select using a CAST() statement.
However, if the target connection have access to the source database then instead of doing a SqlBulkCopy a single "insert into < target > select ... from < source >" statement would be a much more effective solution.
ie:
var colNames = ColumnsNames.Split(',').ToArray();
for(int i=0;i< colNames.Length;i++)
{
if (colNames[i].ToUpper() == "CHECK")
{
colNames[i] = "cast(Check as varchar(10))"
}
}
ColumnsNames = string.Join(",",colNames);
Might not be what you expect but for simply updating one table based on the rows of another table, improved performance and scalability can be achieved with basic INSERT, UPDATE, and DELETE statements. For example:
INSERT tbl_A (col, col2)
SELECT col, col2
FROM tbl_B
WHERE NOT EXISTS (SELECT col FROM tbl_A A2 WHERE A2.col = tbl_B.col);
If it's more about column/table sync, the Merge keyword could be what you are looking for.

Select Query returns empty record

Hey all so basically I have a C# application that allows a user to enter their query about cars in stock (Fields are Car Manufacturer, Model, Age, Litre Size).
The query's contents can vary on what fields the user is looking for (e.g. a person may look for all Ford cars or another query may be all Ford cars that are 6 years old).
After the query is entered the program SHOULD return the data requested to a dataGridView on the form. My problem is that the query executes but only returns a blank record as though it cannot find a suitable record (I have only entered query's that would definitely return a record) this has lead me to believe something is wrong with my coding (particularly the parameters) but I can't figure out where I'm going wrong, could anyone lend a hand?
string ConnStr = "Provider = Microsoft.ACE.OLEDB.12.0; Data Source = H:\\School Work\\Computing A Level\\Stock checker\\Program\\Morgan's Motors Database.mdb;";
OleDbConnection conn_database = new OleDbConnection();
conn_database.ConnectionString = ConnStr;
OleDbCommand comm_database = new OleDbCommand();
comm_database.CommandText = "SELECT * FROM [Car Info] WHERE ? = ?";
comm_database.Connection = conn_database;
conn_database.Open();
OleDbDataAdapter adap_database = new OleDbDataAdapter(comm_database);
DataTable data_database = new DataTable();
for (int i = 0; i < ColumnName.Count; i++)
{
comm_database.Parameters.AddWithValue("?", ColumnName[i].ToString());
comm_database.Parameters.AddWithValue("?", EnteredFields[i].ToString());
adap_database.Fill(data_database);
}
BindingSource bind_database = new BindingSource();
bind_database.DataSource = data_database;
dataGridView1.DataSource = bind_database;
Two things are strange to me:
comm_database.CommandText = "SELECT * FROM [Car Info] WHERE ? = ?";
the field name should be real and not "?" (but "?" is correct for the value).
in your query you only specify one couple file=value but below you loop and add parameters to your dbcommand. as they are not related to anything in your sql text, it wont work as expected.
comm_database.CommandText = "SELECT * FROM [Car Info] WHERE ";
for (int i = 0; i < ColumnName.Count; i++)
{
comm_database.CommandText += (i>0 ? " AND " : "") ColumnName[i].ToString() + " = ?";
comm_database.Parameters.AddWithValue("?", EnteredFields[i].ToString());
}
I guess your problem is your select statement. You have to name your parameters explicitly in your commandtext. The statement you are using:
"SELECT * FROM [Car Info] WHERE ? = ?"
should therefore changed to something like this (depending on your parameter)
"SELECT * FROM [Car Info] WHERE BRAND = ? AND ..."
see also: OleDbCommand parameters order and priority
Column names can't be bound parameters. The...
comm_database.Parameters.AddWithValue("?", ColumnName[i].ToString());
...is interpreted as a value, not as a column name.
If that value is different from the value of the second parameter, no rows will ever be returned (since WHERE ? = ? evaluates to false, no matter what is actually in the database).
If they're equal, all rows will be returned (again, irrespective of what is actually in the database).
This is one of the situations where dynamic SQL is appropriate: construct the SQL text at run-time, once all search criteria is known (you should still use parameters for search values), something like this:
"SELECT * FROM [Car Info] WHERE COLUMN1 = ? AND COLUMN2 = ?" (etc...)
Be sure COLUMN1, COLUMN2 etc. are white-listed, or otherwise "sanitized" before being inserted into the SQL text.
There are ways to use static SQL even in the face of such changing search criteria, but this can have unintended consequences performance-wise.

Update with column name by parameter

How can I pass a columns name by parameter,
follow an example
DataTable dt = new DataTable();
// Here I fill my datatable
for (int i = 0; i < dt.Rows.Count; i++)
{
for (int j = 0; j < dt.Columns.Count; j++)
{
string columnsname = dt.Rows[i][dt.columns[j].toString()].toString();
SqlCommand comando = new SqlCommand();
comando.commandText = "UPDATE Sales.Store SET #columnname = #demographics where id = #id";
comando.Parameters.Add(new SqlParameter("#columnname", columname));
comando.Parameters.Add(new SqlParameter("#dados2", dados2));
comando.ExecuteNonQuery();
comando.Clear();
comando.Dispose()
}
}
This doesn't work, but I have 88 columns, and I need update all data in every 88 columns in each row.
You cannot parameterize column names.
To do what you want you will need to resort to dynamic SQL.
Well, if you have 30,000 rows with 88 columns, and you need to update all 88 columns, you probably want to rethink your database schema.
Itay.
I have figured out a way to include a work around for parametrized column names. I had the same problem but came up with a different way and since I would be the only one using the column names then I believe this is still a safe bet.
String sqlcomm = "SELECT * FROM Asset WHERE " + assetColName + " = ";
command.CommandText = sqlcomm + "$assetColValue";
//command.CommandText = #"SELECT * FROM Asset WHERE $assetColName = '$assetColValue'";
//command.Parameters.AddWithValue("$assetColName", assetColName);
command.Parameters.AddWithValue("$assetColValue", assetColValue);
As you can see from the code above. I tried almost what you did which I then had to comment out. I then concatenated strings together and was able to use my parametrized column name and value which then the value is securely added. The column name however is not secured but this is a method that only I will be using so its still somewhat safe. You can add regular expressions if you want to be more secure but you get the idea of the fix.
Just concatenate the sql string:
"UPDATE Contracts set " + columnName + " = #columnValue where ID = #ID"
Where column name is a string that represents a column in the table

How can I get a list of the columns in a SQL SELECT statement?

I'm wanting to get a list of the column names returned from a SQL SELECT statement. Can someone suggest an easy way to do this?
I have a tool that lets users define a query using any SQL SELECT statement. The results of the query are then presented in a custom manner. To set up the presentation, I need to know the column names so that the user can store formatting settings about each column.
Btw, the formatting settings are all being created via ASP.NET web pages, so the query results will end up in .NET if that helps with any ideas people have.
Any ideas?
You should be able to do this using the GetName method. Something like this probably:
SqlDataReader mySDR = cmd.ExecuteReader();
for(int i = 0;i < mySDR.FieldCount; i++)
{
Console.WriteLine(mySDR.GetName(i));
}
This is something you could do entirely from a asp.net page. No special/extra SQL required.
Assuming SQL Server: You could use SET FMTONLY to just return metadata (and not the actual data), e.g.:
USE AdventureWorks2008R2;
GO
SET FMTONLY ON;
GO
SELECT *
FROM HumanResources.Employee;
GO
SET FMTONLY OFF;
GO
You can get by something as following
Note : You need to fill the DataTable of the Dataset.........
DataSet1 DataSet1 = new DataSet1();
DataTable dt = DataSet1.Tables(0);
DataColumn dc = null;
foreach (DataColumn dc_loopVariable in dt.Columns) {
dc = dc_loopVariable;
Response.write(dc.ColumnName.ToString() + " " + dc.DataType.ToString() + "<br>");
}
Another method to just return meta data is
select top 0 * from table
If you know the table name you could try using:
desc <table_name>
I'm assuming you are using SQL Server.
Or as an alternative:
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'TableNameGoesHere'
ORDER BY ORDINAL_POSITION
You might want to use the second option if you are going to be using ASP.NET
This will get you more than the column name if you need more information about each column like size, ordinal,etc. A few of the most important properties are listed, but there are more.
Note, DataObjects.Column is a POCO for storing column information. You can roll your own in your code. Also, note I derive the .Net type as well, useful for converting SQL data types to .Net (C#) ones. ConnectionString and TableName would be supplied from a caller.
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
conn.Open();
SqlCommand comm = new SqlCommand("Select top(1) * from " + TableName + " Where 1=0");
comm.CommandType = CommandType.Text;
comm.Connection = conn;
using (SqlDataReader reader = comm.ExecuteReader(CommandBehavior.KeyInfo))
{
DataTable dt = reader.GetSchemaTable();
foreach (DataRow row in dt.Rows)
{
//Create a column
DataObjects.Column column = new DataObjects.Column();
column.ColumnName = (string)row["ColumnName"];
column.ColumnOrdinal = (int)row["ColumnOrdinal"];
column.ColumnSize = (int)row["ColumnSize"];
column.IsIdentity = (bool)row["IsIdentity"];
column.IsUnique = (bool)row["IsUnique"];
//Get the C# type of data
object obj = row["DataType"];
Type runtimeType = obj.GetType();
System.Reflection.PropertyInfo propInfo = runtimeType.GetProperty("UnderlyingSystemType");
column.type = (Type)propInfo.GetValue(obj, null);
//Set a string so we can serialize properly later on
column.DataTypeFullName = column.type.FullName;
//I believe this is SQL Server Data Type
column.SQLServerDataTypeName = (string)row["DataTypeName"];
//Do something with the column
}
}
}

Categories

Resources