I am receiving a Dictionary<string, string> and would like to forward its values to the DB inside SqlParameter. Is that even possible? This is the way I did it, and I am getting an error that column name doesn't match table definition.
SqlParameter param = new SqlParameter();
param.ParameterName = "#Values";
var sb = new StringBuilder();
foreach (var item in data)
{
sb.Append("'" + item.Value + "', ");
}
param.Value = sb.ToString().TrimEnd(',');
string insertString = $"insert into {tableName} values (#Values)";
SqlCommand command = new SqlCommand(insertString, connection);
command.Parameters.Add(param);
command.ExecuteNonQuery();
Sql server can't interpret the single variable you are passing as multiple values.
You can either generate your query with multiple variables, or use a table valued parameter.
For the first option, you must change the way you build your query:
var command = new SqlCommand();
var insertString = $"insert into {tableName} values (";
var sb = new StringBuilder(insertString);
int i = 0;
foreach (var item in data)
{
sb.Append("#P").Append(i).Append(",");
command.Parameters.Add("#P" + i, SqlDbType.VarChar).Value = item.Value;
i++;
}
command.Connection = connection;
command.CommandText = sb.ToString().TrimEnd(",") + ");";
command.ExecuteNonQuery();
Note: Code was not tested, there might be some errors.
For the second option, You must use a stored procedure. I've never tried to pass table valued parameters to an inline query and I don't think it's possible.
This post (also linked in Alex K's comment) explains how to do that.
Each value in the "Values" part of you t-SQL must be enclosed with parenthesis.
So, just change this line:
sb.Append("'" + item.Value + "', ");
to:
sb.Append("('" + item.Value + "'),"); // note: no space after the ,
Your tSQL would look something like this:
insert into myTable values ('A', 'B', 'C',)
It needs to look like this (assuming you've only got 1 column in the table):
insert into myTable values ('A'), ('B'), ('C')
And if your table contains multiple columns:
insert into myTable (myColumn) values ('A'), ('B'), ('C')
I think the best is create a split function in mssql (million of example in internet)and a stored. Pass a string comma(for example) separated to the stored Who call the function. Sorry for no example but i'm with my smartphone
Related
There is a syntax problem on Asp.Net when I try to run an insert on a table if I have a vlaue in another table that I look for.
I tried different queries and datareaders but that generates a problem, one of the 2 datareaders needs to be closed.
con.Open();
String insertRegInfo = "INSERT INTO Login (NumEmp,Nombre,Apellido,Codigo) values (#NumEmp, #Nombre,#Apellido,#Codigo) SELECT NumEmp from Empleados WHERE NumEmp = " + TxtNumEmp.Text +"" ;
MySqlCommand command = new MySqlCommand(insertRegInfo, con);
LblConfirm.Text = "Confirmado";
command.Parameters.AddWithValue("#NumEmp", TxtNumEmp.Text);
command.Parameters.AddWithValue("#Nombre", TxtNombre.Text);
command.Parameters.AddWithValue("#Apellido", TxtApellido.Text);
command.Parameters.AddWithValue("#Codigo", TxtCodigo.Text);
command.ExecuteNonQuery();
con.Close();
I expect to insert data into the table if any value is true.
Let me start saying that this is a terrible coding practice:
String insertRegInfo = "INSERT INTO Login (NumEmp,Nombre,Apellido,Codigo) values (#NumEmp, #Nombre,#Apellido,#Codigo) SELECT NumEmp from Empleados WHERE NumEmp = " + TxtNumEmp.Text +"" ;
Better is:
String insertRegInfo = "INSERT INTO Login (NumEmp,Nombre,Apellido,Codigo)
SELECT NumEmp,#Nombre,#Apellido,#Codigo from Empleados WHERE NumEmp = #NumEmp" ;
You should use parameters instead and better even Store Procedures.
However, to answer your question. All you need to do is match the number of columns of your SQL Command.
INSERT INTO Login (NumEmp,Nombre,Apellido,Codigo)
SELECT NumEmp, #Nombre,#Apellido,#Codigo from Empleados WHERE…
Note that I removed the values section from the insert. That is not required
I'm trying to understand the size parameter in SqlParameterCollection.Add() method. Simply reading:
The size as an Int32.
Doesn't really help.
Say, my SQL Server table column is defined as such:
CREATE TABLE myTable
([col1] NVARCHAR(5))
So when I'm adding a new row to that table:
string strSQL = "INSERT INTO myTable ([col1]) " +
"VALUES (#strCol1)";
string strValue = "abcdef";
using (SqlCommand cmd = new SqlCommand(strSQL, connection))
{
cmd.Parameters.Add("#strCol1", SqlDbType.NText, 5).Value =
strValue;
cmd.ExecuteNonQuery();
}
What value will be added to my col1 column? Or will cmd.Parameters.Add throw an exception?
You will get no exceptions and "abcde" will be inserted into your table
You can find a complete documentation for SqlParameter.Size Property.
Based on documentation you can see The value will be truncated to match the size and you will not receive an exception.
I iterate over an external source and get a list of strings. I then insert them into the DB using:
SqlCommand command = new SqlCommand(commandString, connection);
command.ExecuteNonQuery();
Where commandString is an insert into command. i.e.
insert into MyTable values (1, "Frog")
Sometimes the string contains ' or " or \ and the insert fails.
Is there an elegant way to solve this (i.e. #"" or similar)?
Parameters.
insert into MyTable values (#id, #name)
And
int id = 1;
string name = "Fred";
SqlCommand command = new SqlCommand(commandString, connection);
command.Parameters.AddWithValue("id", id);
command.Parameters.AddWithValue("name", name);
command.ExecuteNonQuery();
Now name can have any number of quotes and it'll work fine. More importantly it is now safe from sql injection.
Tools like "dapper" (freely available on NuGet) make this easier:
int id = 1;
string name = "Fred";
connection.Execute("insert into MyTable values (#id, #name)",
new { id, name });
You should look into using parameterized queries. This will allow you insert the data no matter the content and also help you avoid possible future SQL injection.
http://csharp-station.com/Tutorial/AdoDotNet/Lesson06
http://www.c-sharpcorner.com/uploadfile/puranindia/parameterized-query-and-sql-injection-attacks/
I'm trying to make a program that allows me to store trends in an SQL Table. I need to add a hashtag entered into a textbox into the database if it doesn't already exist, and then increment a counter by 1.
The first column is "HashTag" and the second is "Counter" which has char(10) and int properties respectively.
I'm new to SQL, so it's posing a bit of a problem. This is what I have so far.
SqlConnection connection = new SqlConnection();
connection.ConnectionString = (#"Data Source=.\SQLEXPRESS;AttachDbFilename=C:\Users\Jordan Moffat\Desktop\coursework\WindowsFormsApplication\WindowsFormsApplication\HashTags.mdf;Integrated Security=True;Connect Timeout=30;User Instance=True");
connection.Open();
HashTagReader r = new HashTagReader();
if (r.HashTagSearch(s))
MessageBox.Show("I Found it!");
else
{
SqlCommand myCommand = new SqlCommand("INSERT INTO tblHashTag (HashTag, Counter) " + "Values (s, ++)", connection);
myCommand.ExecuteNonQuery();
}
connection.Close();
Any suggestions?
Change the Counter column to Identity(1,1) and it will auto increment. You can easily do this through SQL Management Studio.
Then change your query to:
SqlCommand myCommand = new SqlCommand("INSERT INTO tblHashTag (HashTag) Values ('" + s + '")", connection);
Note: I believe SqlCommand inherits from DbCommand which implements IDisposable. You should wrap these objects with a using() statement, like so to clean up any unmanaged resources:
using(SqlCommand myCommand = new SqlCommand("INSERT INTO tblHashTag (HashTag) Values ('" + s + '")", connection))
{
...
}
If you're on SQL 2008+, you have access to the merge statement. Something like so:
CREATE TABLE #tmp
(
[HashTag] VARCHAR(10) NOT NULL ,
[Counter] INT NOT NULL
);
MERGE [#tmp] AS t
USING
(
SELECT [HashTag] ,
[Counter]
FROM ( VALUES ( '#kitties', 3) ) AS f ( [HashTag], [Counter] )
) AS s
ON t.[HashTag] = s.[HashTag]
WHEN NOT MATCHED BY TARGET
THEN
INSERT ( [HashTag], [Counter] )
VALUES
( s.[HashTag] ,
s.[Counter]
)
WHEN MATCHED
THEN
UPDATE
SET
t.[Counter] += s.[Counter]
OUTPUT
$ACTION ,
INSERTED.* ,
DELETED.*;
I'm using an output clause here just so that it says what it's doing. Execute the merge statement multiple times and see how the output changes. If you're feeling frisky, wrap this in a stored procedure that takes two parameters (hashtag and counter) and you've got yourself something nice. Enjoy!
To add to Chris' answer, to avoid duplicate inserts you should (if you 're on SQL Server 2005 or higher) add a unique index to the HashTag column to enforce the restriction.
Then in the code you should use a WHERE NOT EXISTS clause. See here: SQL Server insert if not exists best practice
So you'd wind up with:
"INSERT INTO tblHashTag (HashTag)
Values ('" + s + "')
WHERE NOT EXISTS (SELECT HashTag
FROM tblHashTag
WHERE HashTag = '" + s + '")"
You can use Parameters to avoid sql injection :
string hashtag = "Your HashTagValue";
string counter = "Your Counter Value";
SqlCommand myCommand = new SqlCommand("INSERT INTO tblHashTag (HashTag, Counter) Values (#HashTag,#Counter)", connection);
myCommand.Parameters.Add("#HashTag", SqlDbType.varchar,50).Value = hashtag; //Your hashTagvalue
myCommand.Parameters.Add("#Counter", SqlDbType.varchar,50).Value = counter; //Your Counter Value
myCommand.ExecuteNonQuery();
My current best code is:
string delNonQuery = "DELETE FROM " + Settings.DataSource + " WHERE #keycolumn=#keyuid";
SqlCommand cmd = new SqlCommand(delNonQuery,readerConn);
SqlParameter kc = new SqlParameter("keycolumn", SqlDbType.VarChar);
SqlParameter key = new SqlParameter("keyuid", SqlDbType.VarChar);
cmd.Parameters.Add(kc).Value = Settings.KeyColumn;
cmd.Parameters.Add(key).Value = Page.Request["key"].ToString().Trim();
readerConn.Open();
cmd.ExecuteScalar();
readerConn.Close();
This executes but affects a whopping zero rows. If I change the SqlDbType on keyuid to UniqueIdentifier it just ends up with me getting a dozen variations on "failed to convert character string into uniqueidentifier". I have to use a parameterized query for data cleanliness, I'm just really stuck as to how...
You can't specify a parameter for a column name - you need to concatenate it the same way you do for the table name.
This:
"DELETE FROM " + Settings.DataSource + " WHERE #keycolumn=#keyuid"
Should change to:
"DELETE FROM " + Settings.DataSource + " WHERE " + Settings.KeyColumn + " =#keyuid"
Though I would probably write it as:
string delNonQuery = string.Format("DELETE FROM {0} WHERE {1} = #keyuid",
Settings.DataSource,
Settings.KeyColumn);
For completeness sake, I will mention that this is open to SQL injection. You need to make sure your Settings values are clean.
I don't think you can parameterise the column name ("keycolumn")
Try this:
string delNonQuery = string.Format("DELETE FROM " + Settings.DataSource + " WHERE {0}=#keyuid", Settings.KeyColumn);
SqlCommand cmd = new SqlCommand(delNonQuery,readerConn);
SqlParameter key = new SqlParameter("keyuid", SqlDbType.VarChar);
cmd.Parameters.Add(key).Value = Page.Request["key"].ToString().Trim();
readerConn.Open();
cmd.ExecuteScalar();
readerConn.Close();
Usual warnings apply regarding concatanating strings to build SQL; this is likely a security risk.
The best method might be to encapsulate your SQL in a stored procedure, pass the column name and value as parameters and then execute using dynamic SQL.
You need to convert the string to GUID:
Relevant Lines:
SqlParameter key = new SqlParameter("keyuid", SqlDbType.UniqueIdentifier);
...
cmd.Parameters.Add(key).Value = new Guid(Page.Request["key"].ToString().Trim());
Which only solves the GUID/UniqueIdentifer issue