string connetionString = null;
SqlConnection connection;
SqlCommand command;
SqlDataAdapter adpter = new SqlDataAdapter();
DataSet ds = new DataSet();
XmlReader xmlFile;
string sql = null;
int ID = 0;
string Name = null, Text = null, Screenname = null;
connetionString = "myconnection";
connection = new SqlConnection(connetionString);
xmlFile = XmlReader.Create("my.XML", new XmlReaderSettings());
ds.ReadXml(xmlFile);
int i = 0;
connection.Open();
for (i = 0; i <= ds.Tables[0].Rows.Count - 1; i++)
{
ID = Convert.ToInt32(ds.Tables[0].Rows[i].ItemArray[0]);
Text = ds.Tables[0].Rows[i].ItemArray[1].ToString().Replace("'", "''");
Name = ds.Tables[0].Rows[i].ItemArray[2].ToString().Replace("'", "''");
Screenname = ds.Tables[0].Rows[i].ItemArray[3].ToString().Replace("'", "''");
//sql = "insert into nicktest values(" + ID + ",'" + Text + "'," + Name + "," + Screenname + "," + DateTime.Now.ToString() + ")";
sql = "If Exists(Select * from niktest2 Where Id = ID) " +
" BEGIN " +
" update niktest2 set Name = '" + Text + "' , Screenname = '" + Name + "', Profimg= '" + Screenname + "', InsertDateTime= '" + DateTime.Now.ToString() + "' where Id=ID" +
" END " +
" ELSE " +
" BEGIN " +
" insert into niktest2(Id,Name,Screenname,Profimg,InsertDateTime) values('" + ID + "','" + Text + "','" + Name + "','" + Screenname + "' ,'" + DateTime.Now.ToString() + "')" +
" END ";
command = new SqlCommand(sql, connection);
adpter.InsertCommand = command;
adpter.InsertCommand.ExecuteNonQuery();
}
}
after running this code only first row gets updated even my xml file is having more data.
i Want to insert all data into database with assign id to it in xml file.
Please help..
As soon as you have inserted one row, this condition will be true:
If Exists(Select * from niktest2 Where Id = ID)
So you will perform the update, rather than the insert, so you will only ever get one row in the database.
Since you are using SQL Server 2008 I would adopt a completely different approach, using Parameterised queries, MERGE, and table valued parameters.
The first step would be to create your table valued parameter (I have had to guess at your type:
CREATE TYPE dbo.nicktestTableType AS TABLE
(
Id INT,
Name VARCHAR(255),
Screenname VARCHAR(255),
Profimg VARCHAR(255)
);
Then you can write your MERGE statement to upsert to the database:
MERGE nicktest WITH (HOLDLOCK) AS t
USING #NickTestType AS s
ON s.ID = t.ID
WHEN MATCHED THEN
UPDATE
SET Name = s.Name,
Screenname = s.Screenname,
Profimg = s.Profimg,
InsertDateTime = GETDATE()
WHEN NOT MATCHED THEN
INSERT (Id, Name, Screenname, Profimg, InsertDateTime)
VALUES (s.Id, s.Name, s.Screenname, s.Profimg, GETDATE());
Then you can pass your datatable to the query as a parameter:
using (var command = new SqlCommand(sql, connection))
{
var parameter = new SqlParameter("#NickTestType", SqlDbType.Structured);
parameter.Value = ds.Tables[0];
parameter.TypeName = "dbo.nicktestTableType";
command.Parameters.Add(parameter);
command.ExecuteNonQuery();
}
If you don't want to make such a drastic change, then you should at the very least use parameterised queries, so your SQL would be:
IF EXISTS (SELECT 1 FROM nicktest WHERE ID = #ID)
BEGIN
UPDATE nicktest
SET Name = #Name,
ScreenName = #ScreeName,
InsertDateTime = GETDATE()
WHERE ID = #ID;
END
ELSE
BEGIN
INSERT (Id, Name, Screenname, Profimg, InsertDateTime)
VALUES (#ID, #Name, #Screenname, #Profimg, GETDATE());
END
Or preferably still using MERGE as the HOLDLOCK table hint prevents (or at least massively reduces the chance of) a race condition:
MERGE nicktest WITH (HOLDLOCK) AS t
USING (VALUES (#ID, #Name, #ScreenName, #ProfImg)) AS s (ID, Name, ScreenName, ProfImg)
ON s.ID = t.ID
WHEN MATCHED THEN
UPDATE
SET Name = s.Name,
Screenname = s.Screenname,
Profimg = s.Profimg,
InsertDateTime = GETDATE()
WHEN NOT MATCHED THEN
INSERT (Id, Name, Screenname, Profimg, InsertDateTime)
VALUES (s.Id, s.Name, s.Screenname, s.Profimg, GETDATE());
This will be considerably less efficient than the first solution though using table-valued parameter
Then your c# would be something like:
for (i = 0; i <= ds.Tables[0].Rows.Count - 1; i++)
{
using (var command = new SqlCommand(sql, connection))
{
command.Parameters.AddWithValue("#ID", ds.Tables[0].Rows[i][0]);
command.Parameters.AddWithValue("#Name", ds.Tables[0].Rows[i][1]);
command.Parameters.AddWithValue("#ScreeName", ds.Tables[0].Rows[i][2]);
command.Parameters.AddWithValue("#ProfImg", ds.Tables[0].Rows[i][3]);
command.ExecuteNonQuery();
}
}
Related
I'm trying to get the date to store as a string in a table, but the date keeps converting to a negative 4 digit number which correlates with the date, and I can't for the life of me figure out where I've messed up. Note that I'm using a combo of C# and SQL Server
foreach(DataRow dr in dt.Rows)
{
int qty = 0;
string pname = "";
SqlCommand cmd3 = con.CreateCommand();
cmd3.CommandType = CommandType.Text;
cmd3.CommandText = "insert into order_item values('" + orderid.ToString() + "','" + dr["product"].ToString() + "'," +
"'" + dr["price"].ToString() + "','" + dr["qty"].ToString() + "','"+ dr["total"].ToString() + "')";
cmd3.ExecuteNonQuery();
qty = Convert.ToInt32(dr["qty"].ToString());
pname = dr["product"].ToString();
SqlCommand cmd6 = con.CreateCommand();
cmd6.CommandType = CommandType.Text;
cmd6.CommandText = "update stock set product_qty = product_qty - " + qty + " where product_name = '"+pname.ToString()+"'";
cmd6.ExecuteNonQuery();
// date keeps getting updated to negative 4 digit number which coordinates with the date. ex: 14-01-2020 is converting to -2007.
SqlCommand cmd7 = con.CreateCommand();
cmd7.CommandType = CommandType.Text;
**cmd7.CommandText = "update stock_over_time set product_qty = product_qty - " + qty + ", date_changed = " + date.ToString("dd-MM-yyyy") + "" +
" where product_name = '" + pname.ToString() + "'";**
cmd7.ExecuteNonQuery();
}
The immediate problem is that:
, date_changed = " + date.ToString("dd-MM-yyyy") + "
will become
, date_changed = 15-01-2020
which is: -2006, which is (because of how dates are stored) some time in July 1894.
A bad fix for this would be to add quotes, but this is: bad - it has a range of problems to do with internationalization (is 08-01 the first of August? the 8th of January?), SQL injection, etc.
The correct fix is to use parameters throughout. For example:
cmd7.CommandText = #"
update stock_over_time
set product_qty = product_qty - #qty,
date_changed = #date
where product_name = #pname";
This, however, requires you to add parameters with the values.
The simplest way to do this would be with Dapper:
string pname = ...
int qty = ...
DateTime date = ...
con.Execute(#"
update stock_over_time
set product_qty = product_qty - #qty,
date_changed = #date
where product_name = #pname",
new { pname, qty, date });
Note: all of your database access should be parameterized, either like the above, or using raw ADO.NET, or using tools like EF etc. Not just this one place; everywhere.
A date should not be stored as a string datatype, instead change date-changed to a datetime type (or even just a date, since the values stored have no "time" element).
Also, it is advisable to use a parameterized query to avoid SQL injection
string sql = #"update stock_over_time set product_qty = product_qty - #qty, date_changed = #date where product_name = #pname";
using (SqlConnection connection = new SqlConnection(connString)
{
connection.Open();
using (SqlCommand cmd= new SqlCommand(sql, connection))
{
cmd.Parameters.Add("#qty", SqlDbType.SqlInt32).value = qty;
cmd.Parameters.Add("#date", SqlDbType.SqlDateTime).value = date;
cmd.Parameters.Add("#pname", SqlDbType.Varchar, 50).value = pname;
cmd.ExecuteNonQuery();
}
}
This question already has answers here:
Execute Insert command and return inserted Id in Sql
(8 answers)
Closed 3 years ago.
I have a C# application which creates an entry in the SQL database to table_customer_invoice and table_customer_invoice_detail, when a user sells a particular item from the application, as soon as the user enters the amount tendered, an insert in the customer_invoice table is done where ID is an identity column which I use that ID by reading the last entered ID by (SELECT TOP 1 ID FROM CUSTOMERINVOICE ORDER BY ID DESC) to insert in the table_customer_invoice_detail.
It works well the only problem is when two users sell an item together at the exact same time from different computers than the (SELECT TOP 1 ID) clashes and both users get the same ID as it looks for the last ID created. Is there any other way I can get the exact ID for the particular user?
This is the code in the application
SqlCommand cmd3 = new SqlCommand("insert into CUSTOMERINVOICE(INVOICEDATE,INVOICESTATUS,ISDEBTORINVOICE,ISLAYBYEINVOICE,INVOICETYPE,INVOICETOTAL,salesrep,dayended)" + "values(GETDATE(),0,0,0,1,'" + Convert.ToDecimal(lblTotalAmount.Text.ToString()) + "','" + txtSalesRep.Text + "',0)", conn);
cmd3.ExecuteNonQuery();
SqlDataAdapter ada2 = new SqlDataAdapter("SELECT TOP 1 ID FROM CUSTOMERINVOICE ORDER BY ID DESC", conn);
DataTable dt2 = new DataTable();
ada2.Fill(dt2);
DataRow dr2 = dt2.Rows[0];
CUSTID = Int32.Parse(dr2["ID"].ToString());
foreach (DataGridViewRow row in dgSaleBasket.Rows)
{
SqlDataAdapter ada5 = new SqlDataAdapter("SELECT itemcode,onhand,costincl FROM stmast where itemcode ='" + row.Cells[1].Value + "'", conn);
DataTable dt5 = new DataTable();
ada5.Fill(dt5);
int quantityPurchased = Int32.Parse(row.Cells[4].Value.ToString());
for (int i = 0; i < dt5.Rows.Count; i++)
{
DataRow dr5 = dt5.Rows[i];
double SellPrice = Convert.ToDouble(row.Cells[5].Value.ToString());
costinc = Convert.ToDouble(dr5["costincl"].ToString());
profit = (SellPrice - costinc) * quantityPurchased;
totalprofit = profit + totalprofit;
SqlCommand cmd4 = new SqlCommand("insert into CUSTOMERINVOICEDETAIL(INVOICEID,ITEMCODE,DESCRIPTION,QUANTITY,PRICE,profit,refund)" + "values(" + CUSTID + ",'" + row.Cells[1].Value.ToString() + "','" + row.Cells[2].Value.ToString() + "'," + row.Cells[4].Value.ToString() + ",'" + Convert.ToDecimal(row.Cells[5].Value.ToString()) + "'," + profit + ",0)", conn);
cmd4.ExecuteNonQuery();
SqlCommand cmd6 = new SqlCommand("UPDATE stmast SET onhand =onhand-" +quantityPurchased + ", lastSold =GETDATE() , lastSoldPrice=" + Convert.ToDecimal(row.Cells[5].Value.ToString()) + ",totalQtySold=totalQtySold+" + quantityPurchased + " WHERE itemcode ='" + row.Cells[1].Value + "'", conn);
cmd6.ExecuteNonQuery();
SqlCommand cmd2 = new SqlCommand("UPDATE customerinvoice set invoiceprofit=" + totalprofit + " WHERE id =" + CUSTID, conn);
cmd2.ExecuteNonQuery();
}
Maybe you can try by using INSERTED temporary table
example :
CREATE DATABASE TEST
USE TEST
CREATE TABLE client
(
id INT IDENTITY(1,1),
name varchar(20)
)
DECLARE #tempTable TABLE(id INT,
[name] varchar(20))
INSERT client(name)
OUTPUT INSERTED.* INTO #tempTable
VALUES ('Marc');
SELECT * FROM #tempTable
-- Return |1|Marc|
updatequery = #"UPDATE EMP_DETAILS SET ADVANCE_SAL = 'ADVANCE_SAL + '" +
Convert.ToDecimal(txtADV.Text) + "'' WHERE EMP_ID = '" +
Convert.ToInt64(txtEID.Text) + "')";
What is wrong with the above code?
.
.
.
The modified code which has an error [Scalar variable #ADVANCE_SAL not declared] is as follows:
var c = dtimePick.Value.TimeOfDay;
SqlParameter param = new SqlParameter("#ADVANCE_SAL", SqlDbType.Decimal);
param.Value = Convert.ToDecimal(txtADV.Text);
command.Parameters.Add(param);
updatequery = #"UPDATE EMP_DETAILS SET ADVANCE_SAL = ADVANCE_SAL + #ADVANCE_SAL , LATEST_ADVANCE_DATE = '"+c+"' WHERE ENAME = '"+txtEID.Text+"' ";
The error is that you are enclosing ADVANCE_SAL with single quote. To make it cleaner and safer from sql injection, you must parameterized the query,
updatequery = #"UPDATE EMP_DETAILS
SET ADVANCE_SAL = ADVANCE_SAL + #ADVANCE_SAL
WHERE EMP_ID = #EMP_ID"
using (SqlConnection connection = new SqlConnection(/* connection info */))
{
using (SqlCommand command = new SqlCommand(updatequery , connection))
{
// must do proper error handling
connection.Open();
command.Parameters.Add(new SqlParameter("ADVANCE_SAL", Convert.ToDecimal(txtADV.Text)));
command.Parameters.Add(new SqlParameter("EMP_ID", Convert.ToInt64(txtEID.Text)));
int results = command.ExecuteNonQuery();
}
}
Why you are adding single quote to the EMP_ID and ADVANCE_SAL they are numeric and don't need ', i think following is what you wanted...
updatequery = #"UPDATE EMP_DETAILS SET ADVANCE_SAL = ADVANCE_SAL + " +
Convert.ToDecimal(txtADV.Text) + " WHERE EMP_ID = " +
Convert.ToInt64(txtEID.Text) + ")";
There are many problems with your implementation.
1- SQL injection vulnerability - Rad more on this here
2- Your query building is not correct, it should be like following. (Quotes are not correct for ADVANCE_SAL)
updatequery = #"UPDATE EMP_DETAILS SET ADVANCE_SAL = ADVANCE_SAL + " +
Convert.ToDecimal(txtADV.Text) + " WHERE EMP_ID = '" +
Convert.ToInt64(txtEID.Text) + "'";
3- If any employee have not opted for advance salary (in that case the ADVANCE_SAL value can be null in the table). Your query will update it with NULL as NULL + SomeValue = NULL
For example
SELECT NULL +10.0 AS Total
Output
Total
-----
NULL
try this
updatequery = #"UPDATE EMP_DETAILS SET ADVANCE_SAL = 'ADVANCE_SAL + '" +
Convert.ToDecimal(txtADV.Text) + "'' WHERE ( EMP_ID = '" +
Convert.ToInt64(txtEID.Text) + "')";
I am trying to perform dynamic sql select where I am selecting from a table using a parameter.
SELECT null FROM #TableName
However I am getting error must declare table variable #TableName. I suspect this is because I am selecting from a table using a variable. I have not needed to do this before.
List<SqlParameter> sqlParams = new List<SqlParameter>()
{
new SqlParameter("TableName", "testtable"),
new SqlParameter("FieldName", "testfield"),
new SqlParameter("Find", "testfind"),
};
string sqlSelect = "SELECT null FROM #TableName
WHERE #FieldName LIKE '%' + #Find + '%' ";
DataTable dtSelect = SqlHelper.ExecuteDataset(sqlConn, CommandType.Text,
sqlSelect, 30, sqlParams.ToArray()).Tables[0];
//30 = timeout
How can I perform the above using dynamic sql? (no stored procedures please)
You cannot use parameters for things like table and column names. For those you could have a whitelist of possible values and then use string concatenation when building the SQL query.
You can't use parameters like that, so you have to build the query as a string. You could do that in SQL, but you can also just create the string in the C# code.
Make sure that the table name and field name are safe and trusted values, and doesn't come directly from an unsafe source like a web request.
string tableName = "testtable";
string fieldName = "testfield";
List<SqlParameter> sqlParams = new List<SqlParameter>() {
new SqlParameter("Find", "testfind"),
};
string sqlSelect =
"SELECT null " +
"FROM " + tableName + " " +
"WHERE " + fieldName + " LIKE '%' + #Find + '%' ";
private DataTable ExecuteDynamic(string TableName,string FieldName, string Find)
{
string sqlSelect = "SELECT * FROM " + TableName +
" WHERE " + FieldName + " LIKE '%'" + Find + "'%' ";
using (connection = new SqlConnection(Strcon))
connection.Open();
{
using (cmd = new SqlCommand(sqlSelect, connection))
{
cmd.CommandType = CommandType.Text;
cmd.CommandTimeout = 60;
adpt = new SqlDataAdapter(cmd);
dt = new DataTable();
adpt.Fill(dt);
return (dt);
}
}
}
Copy table content to another table in the same database with C#.
I got one database (Baza) with some data in two tables NEW and OLD. I need to periodically move NEW data to OLD data table (after I do some measuring). I need to compare these data in next step.
I'm using SQL Server CE wit Baza.sdf file. In any sophisticated way to copy table to table do it (some loop doing it automatically)?
Thanks
I solved it in this way:
Program reads in loop table NEW row by row and each value changes to parameter. I got 8 columns so 8 parameters(7 integers and one string)
Next each of parameter is inserted to OLD table.
Resuld is also displayed in textBox1:
SqlCeConnection conn = new SqlCeConnection("Data Source = \\Program Files\\My Program\\Program.sdf; Password ='mypassword'");
conn.Open();
try
{
SqlCeCommand cmd = new SqlCeCommand("SELECT * FROM [NEW]", conn);
SqlCeDataReader rdr = cmd.ExecuteReader();
cmd.Connection = conn;
cmd.ExecuteNonQuery();
while (rdr.Read())
{
int param1 = rdr.GetInt32(0);
int param2 = rdr.GetInt32(1);
int param3 = rdr.GetInt32(2);
int param4 = rdr.GetInt32(3);
int param5 = rdr.GetInt32(4);
int param6 = rdr.GetInt32(5);
int param7 = rdr.GetInt32(6);
string param8 = rdr.GetString(7);
textBox1.AppendText(" " + param1 + " " + param2 + " " + param3 + " " + param4 + " " + param5 + " " + param6 + " " + param7 + " " + param8);
textBox1.AppendText(System.Environment.NewLine);
SqlCeCommand ins = new SqlCeCommand("insert into [OLD] values ('" + param1 + "','" + param2 + "','" + param3 + "','" + param4 + "','" + param5 + "','" + param6 + "','" + param7 + "','" + param8 + "');");
ins.Connection = conn;
ins.ExecuteNonQuery();
}
}
catch (Exception msg)
{
MessageBox.Show(msg.ToString());
}
conn.Close();
What's wrong with:
insert into [table] (field1, field2)
select field1, field2 from [table2] where (your conditions are met)
? :-)
Since that's not supported in CE, thanks #marc_s, you could get the records from the NEW table using a select, then insert them into the OLD table, and then loop through the initial recordset to delete the rows from the NEW by ID for example.
Adding to what CodeCaster stated,
Put his code in a storedprocedure and create a job to run on a set interval of your choosing.
You can add logic to the storedprocedure to check the validity of the data as well and set it to notify you if the data was incorrect. Examples could be given if you included a more specific information in your question.