Update with column name by parameter - 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.

Insert column name at runtime in c# [duplicate]

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

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.

SQL Update - Not updating multiple rows in a for loop

I have a DataGridView(DGV) linked to a SQlite database. I want to update some info on the DGV. So, I have a context menu that lets me change one column and update the DB as well. I want to have the ability to select multiple rows and edit as well. For ex: if i select five rows and change the type from alarms to errors ; the change is reflected in the DGV and then when i look into the database , the change isnt reflected. Only one row is updated.
My code snippet is below
foreach (DataGridViewRow r in dataGridView1.SelectedRows)
{
SQLiteTransaction SQLiteTrans = connection.BeginTransaction();
SQLiteCommand cmd = connection.CreateCommand();
MessageBox.Show(r.ToString());
if (r.Cells["typeDataGridViewTextBoxColumn"].Value.ToString().Contains("#") == false)
{
r.Cells["typeDataGridViewTextBoxColumn"].Value = r.Cells["typeDataGridViewTextBoxColumn"].Value.ToString() + " # " + max;
}
else
{
r.Cells["typeDataGridViewTextBoxColumn"].Value = r.Cells["typeDataGridViewTextBoxColumn"].Value.ToString().Substring(0, r.Cells["typeDataGridViewTextBoxColumn"].Value.ToString().IndexOf("#")) + "# " + max;
}
string querytext = "Update LogDatabase set Type = \"" + r.Cells["typeDataGridViewTextBoxColumn"].Value + "\" where HashKey = \"" + r.Cells["hashKeyDataGridViewTextBoxColumn"].Value.ToString() + "\"";
cmd.CommandText = querytext;
cmd.ExecuteNonQuery();
SQLiteTrans.Commit();
}
I dont have too much experience with SQL. So, im not sure if something is wrong with how ive updated the database!
What do i have to edit to make sure all the rows are updated in the DB as well?!
Help appreciated.
Edit: Tried checking the query before its sent.
When i try editing multiple rows in the DGV without sorting the DGV under any column it works and updates all the rows simultaneously... But When I try to sort them based on "Type" and then edit the rows, the same query is passed ! :| (Hash Key doesnt change)
Its like, the one row keeps moving up the list of rows and is always the row r in the for loop.
Edit 2: Its definitely a problem with the rows of the DGV
Everytime I sort the DGV and then try to edit the fields, the queries have hashkey values different from the once that i selected. Its like the row ids are changed completely after one update. It looks like the DGV automatically sorts once one row is updated !
Is there a way to disable this???!
It looks like you weren't increasing max counter.
foreach (DataGridViewRow row in dataGridView1.SelectedRows)
{
using (SQLiteTransaction SQLiteTrans = connection.BeginTransaction())
{
SQLiteCommand cmd = connection.CreateCommand();
MessageBox.Show(row.ToString());
var txtValue = row.Cells["typeDataGridViewTextBoxColumn"].Value;
if (txtValue.Contains("#"))
{
txtValue = String.Format("{0} # {1}", txtValue, max);
}else
{
txtValue = txtValue.SubString(0, txtValue.IndexOf("#")) + "# " + max.ToString();
}
string querytext = "Update LogDatabase set Type = #type where HashKey = #key";
//Create your SqlParameters here!
cmd.CommandText = querytext;
cmd.ExecuteNonQuery();
SQLiteTrans.Commit();
max++; //You weren't increasing your counter!!
}
}
Your problem is that you are using double quotes in your SQL query to delimit your string values. Replace them for single quotes:
string querytext = "Update LogDatabase set Type = '" +
r.Cells["typeDataGridViewTextBoxColumn"].Value +
"' where HashKey = '" +
r.Cells["hashKeyDataGridViewTextBoxColumn"].Value.ToString() +
"'";
Also, beware of SQL Injection. Your code is not dealing with that.

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