I want to delete from multiple SQL tables with a C# command but I get always error:
Invalid syntax near ",".
Here is code so far:
string connectionString = #"Data Source=" + System.IO.File.ReadAllText("Server.ini") + ";" + "Initial Catalog=" + "lin2world" + ";" + "User ID=" + System.IO.File.ReadAllText("User.ini") + ";" + "Password=" + System.IO.File.ReadAllText("Password.ini");
string sql = "DELETE FROM user_data, user_item, user_ActiveSkill, user_blocklist, user_deleted, user_friend, user_henna, user_history, user_log, user_macro, user_macroinfo, user_newbie, user_nobless, user_punish, user_recipe, user_skill, user_sociality, user_subjob WHERE char_id='" + textBox1.Text + "' ";
SqlConnection connection = new SqlConnection(connectionString);
SqlDataAdapter dataadapter = new SqlDataAdapter(sql, connection);
DataSet ds = new DataSet();
connection.Open();
dataadapter.Fill(ds, "char_id");
connection.Close();
MessageBox.Show("Character Deleted!!");
You can only delete from one table in a delete statement.
Use multiple delete statements within a transaction to perform the deletes
In case your not familiar with transactions in C# here is an example
using (var Conn = new SqlConnection(_ConnectionString))
{
SqlTransaction trans = null;
try
{
Conn.Open();
trans = Conn.BeginTransaction();
using (SqlCommand Com = new SqlCommand(ComText, Conn, trans))
{
/* DB work */
}
trans.Commit();
}
catch (Exception Ex)
{
if (trans != null) trans.Rollback();
return -1;
}
}
If you want to do something similar to this, you can pass all tables as a list and still do what you want.
DECLARE db_cursor CURSOR FOR
SELECT name
FROM master.dbo.sysdatabases
WHERE name IN ("user_data","user_item","user_ActiveSkill","user_blocklist","user_deleted","user_friend","user_henna","user_history","user_log","user_macro","user_macroinfo","user_newbie","user_nobless","user_punish","user_recipe","user_skill","user_sociality","user_subjob") -- use these databases
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
DELETE FROM #name WHERE char_id='whatever'
FETCH NEXT FROM db_cursor INTO #name
END
Edit: It seems like these are all related tables -- if you have a proper relationship with cascade: delete, you should only have to delete the main record and its children will all take care of themselves
You cannot delete from multiple tables in one statement within SQL Server. You will need multiple statements, one for each table:
DELETE FROM user_data WHERE PersonID = '2';
DELETE FROM user_item WHERE PersonID = '2';
etc etc
And fire each one off individually, (making sure not to violate any FKs) or create a stored procedure and fire that off (to minimise open and closing of db connections).
My experience of using triggers tells me not to use triggers, at times they will not fire when they should!
Related
I'm making a form where a user answers some questions to make a pricehold. My problem is I can't store the data from the questions into more than one sql table.
I have tried inserting the other table into the sql command (shown below) and I have tried making another sql command that basically says the same thing with a different name but splitting the name and phone number into the first one and the date created and pick up date into the second one but that only runs the first sql command and then stops so data is never stored into the second table
private void AddPhBttn_Click(object sender, RoutedEventArgs e)
{
SqlConnection furniture = new SqlConnection("Data Source=LAPTOP-F4QFMPFD\\MSSQLSERVER1;Initial Catalog=Furniture;Integrated Security=True");
furniture.Open();
SqlCommand add = new SqlCommand("insert into Customers(Name, Phone) PriceHold(DateCreated, PickUpDate) values ('" + nameTxtBox.Text + "', '" + phoneTxtbox.Text + "', '" + dateTxtBox.Text + "', '" + puDateTxtBox.Text + "')", furniture);
int i = add.ExecuteNonQuery();
if (i != 0)
{
MessageBox.Show("saved");
}
else MessageBox.Show("error");
}
As #Caius Jard said, you can't do this with an ad-hoc query.
So what is an option to do so?
Step 1: Create a Stored Procedure in the Database:
CREATE PROCEDURE usp_InsertData
#Name NVARCHAR(200),
#Phone NVARCHAR(100),
#DateCreated Date,
#PickUpDate Date
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO Customers(Name, Phone) VALUES (#Name,#Phone)
INSERT INTO PriceHold(DateCreated, PickUpDate) VALUES (#DateCreated,#PickUpDate)
END
Step 2: Call above Stored procedure in C# Code:
private void AddPhBttn_Click(object sender, RoutedEventArgs e)
{
var furniture = new SqlConnection("Data Source=LAPTOP-F4QFMPFD\\MSSQLSERVER1;Initial Catalog=Furniture;Integrated Security=True");
SqlCommand add = new SqlCommand("usp_InsertData", furniture);
add.CommandType = System.Data.CommandType.StoredProcedure;
add.Parameters.AddWithValue("#Name", nameTxtBox.Text);
add.Parameters.AddWithValue("#Phone", phoneTxtbox.Text);
add.Parameters.AddWithValue("#DateCreated", dateTxtBox.Text);
add.Parameters.AddWithValue("#PickUpDate", puDateTxtBox.Text);
furniture.Open();
int i = add.ExecuteNonQuery();
if (i != 0)
{
MessageBox.Show("saved");
}
else
{
MessageBox.Show("error");
}
furniture.Dispose();
}
You can't do this in SQL
INSERT INTO
myfirsttable(column1, column2)
mysecondtable(column3, column4, column5)
VALUES(value1, value2, value3, value4)
It's flat out a syntax error. Only one table may appear in an insert. The number of values inserted must match the number of columns
If you want to insert into two tables, run two separate inserts from your c# code
Finally, have a long read of http://bobby-tables.com - your code is currently highly insecure and while this may not matter right now because it's just some small test app, it is best to avoid embarking on a learning path that includes coding in this way. As a recruiter I've turned down many job candidates who have written SQL like this and I'd never employ someone who demonstrated this style to me
When working with data in more than one table, if you want to ensure either all insert/update/delete complete successfully or none of them are applied on your data to ensure data integrity, use transactions. I think SqlTransaction is what you're after. Read about it here.
For your specific case, this is one possibility:
private void AddPhBttn_Click(object sender, RoutedEventArgs e)
{
// Necessary input validation to collect and data from input fields. Good practice to avoid SQL injection.
AddFurniture(nameTxtBox.Text, phoneTxtbox.Text, dateTxtBox.Text, puDateTxtBox.Text);
}
private void AddFurniture(string name, string phoneNumber, string createdDate, string pickupDate)
{
string connectionString = "Data Source=LAPTOP-F4QFMPFD\\MSSQLSERVER1;Initial Catalog=Furniture;Integrated Security=True"; // This should ideally come from some configuration.
using(SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = connection.CreateCommand();
SqlTransaction transaction = connection.BeginTransaction("Add Furniture");
command.Connection = connection;
command.Transaction = transaction;
try
{
connection.Open();
command.CommandText = $"insert into Customers (Name, Phone) values ({name}, {phoneNumber});";
command.ExecuteNonQuery();
command.CommandText = $"insert into PriceHold (DateCreated, PickUpDate) values ({createdDate}, {pickupDate});";
command.ExecuteNonQuery();
// Try to commit to database.
// Both the above queries are executed at this point. If any one of them fails, 'transaction' will throw an exception.
transaction.Commit();
}
catch (Exception ex1)
{
// Considering the statements executed using the 'transaction' for this 'connection',
// one of the insert operations have clearly failed.
// Attempt to roll back the change which was applied.
MessageBox.Show($"Insert failed. Trying to roll back {ex1.Message}");
try
{
transaction.RollBack();
}
catch (Exception ex2)
{
// Rollback also failed. Possible data integrity issue. Handle it in your application.
MessageBox.Show($"Roll back failed. {ex2.Message}");
}
}
}
}
How can I add a variable to my SQL string and run it against the server successfully? I want to run this statement through my C#
protected void RunSQLQuery(string salesman, string connectionString)
{
SqlConnection cnn;
SqlCommand cmd;
StringBuilder sql = new StringBuilder();
SqlDataReader reader;
cnn = new SqlConnection(connectionString);
sql = new StringBuilder();
sql.Append("update database ");
sql.Append("set shippdate = GetDate() ");
sql.Append("where salesman = "' + salesman + "'");
sql.Append("and managerapproval is not null ");
cnn.Open();
cmd = new SqlCommand(sql.ToString(), cnn);
reader = cmd.ExecuteReader();
reader.Close();
cmd.Dispose();
cnn.Close
}
This presents multiple compile errors underlining my +salesman+ code. The errors are:
Only assignment, call, increment, decrement, and new object
expressions can be used as a statement
; expected
) expected
Too many characters in character literal Newline in constant
You are not adding the string object that salesman refers, you are adding salesman as a string literal.
Just add it as a parameter like;
var cmd = new SqlCommand("update database set shippdate = GetDate() where salesman = #salesman");
cmd.Parameters.Add("#salesman", salesman);
...
And use ExecuteNonQuery to execute your command, not SqlDataReader. This SqlDataReader is for return some data.
But more important, you should always use parameterized queries. This kind of string concatenations are open for SQL Injection attacks.
Also use using statement to dispose your connection and command automatically instead of calling Close or Dispose methods manually.
As a full example;
protected void RunSQLQuery(string salesman, string connectionString)
{
using(var cnn = new SqlConnection(connectionString))
using(var cmd = cnn.CreateCommand())
{
cmd.CommandText = #"update database set shippdate = GetDate()
where salesman = #salesman";
// I assume your column is nvarchar
cmd.Parameters.Add("#salesman", SqlDbType.NVarChar).Value = salesman;
cnn.Open();
cmd.ExecuteNonQuery();
}
}
For myself, I always prefer to use SqlParameterCollection.Add(string, SqlDbType, Int32) overload to specify my parameter type and it's size but since you never mentioned your salesman column type, I couldn't post this in my example.
As you can also see from the syntax highlighting, the compile errors are caused because you did not escape the quotes properly in sql.Append("where salesman = "' + salesman + "'");.
As a side note, you should never insert strings into sql queries without first validating them, or you are open to sql injection, e.g. if i pass "''; drop table database;" as salesman parameter. It is better to use SqlParameter.
I would suggest using the AddWithValue method from your sql command combined with the UPPER function to make it case insensitive:
SqlCommand cmd = cnn.CreateCommand();
cmd.CommandText = "UPDATE database SET shippdate = GetDate() WHERE UPPER(salesman) = UPPER(#salesMan)";
cmd.Parameters.AddWithValue("#salesMan", salesman);
if (cnn.State.Equals(ConnectionState.Closed))
{
cnn.Open();
}
cmd.ExecuteNonQuery();
cnn.Close();
As mentioned in above answers, yes, writing queries in this way is not a good way to do it. But still if you want to do it that way only, you will have to change:
sql.Append("where salesman = "' + salesman + "'");
to
sql.Append("where salesman = '" + salesman + "'");
A desktop-based software shall enable a user to create tables in a provided database as per requirement.
My problem is that ExecuteNonQuery treats only Data Manipulation Language.
What should i use for Data Definition Language, i.e to pass create command.
Thanks in advance :)
my code
3rd party edit
From the linked image
public void create(string name, int varchar_quantity, string rate)
{
con.Close();
string s = "Crate table anas('" + name + "' varchar ('"
+ varchar_quantity + "')" + rate + "' int);";
con.Open();
SqlCommand com = new SqlCommand(s, con);
com.ExecuteNonQuery();
}
You should be able to create tables with ExecuteNonQuery method.
From msdn documentation,
You can use the ExecuteNonQuery to perform catalog operations (for
example, querying the structure of a database or creating database
objects such as tables), or to change the data in a database without
using a DataSet by executing UPDATE, INSERT, or DELETE statements.
var conStr = "PUT YOUR CONNECTION STRING HERE";
using (var c = new SqlConnection(conStr))
{
c.Open();
var qry = "CREATE TABLE TEST(name varchar(30));";
using (var cmd = new SqlCommand(qry, c))
{
cmd.ExecuteNonQuery();
}
}
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();
I'm trying to create a database through C# without using SMO, is it possible?! I've got this at the moment but I'm 99% it's wrong;
connectionString2 = "SERVER=" + server + ";" + "DATABASE=master;"
+ "UID=" + uid + ";" + "PASSWORD=" + password + ";"
this.OpenConnection(); //Which opens the above connection
// The Lines array has stored the sql statements to create the databse
// and tables for that database
foreach (string line in lines)
{
create_database += line;
}
MySqlCommand cmd = new MySqlCommand(create_database, connection2);
cmd.ExecuteNonQuery();
is it actually possible do this without using SMO?!
You have to use MySqlCommand as you're doing in the example.
MySqlCommand cmd = new MySqlCommand("CREATE DATABASE YAYNEWDATABASE;", connection2);
connection2.Open();
cmd.ExecuteNonQuery();
connection2.Close();
Alternatively if you're going to be doing a lot of modification I'd suggest trying a tool like Manatee to handle your database creation/migration as part of a build script. This is making a lot of assumptions about what you're doing, but it doesn't hurt to be aware of it.
This adds migrations similar to those in rails to .net.
{
up: "CREATE TABLE Orders (ID {pk}, OrderNumber {string} NOT NULL, SubTotal {money})",
down: "DROP TABLE Orders"
}
You might use something like this:
var connection = new MySqlConnection("Data Source=server;UserId=root;PWD=pass;");
var command = new MySqlCommand("CREATE DATABASE FancyDatabase;", connection);
connection.Open();
command.ExecuteNonQuery();
connection.Close();
Note: there is no default database, and you need to have the rights. And: this is untested code, uses standard sql features.