I have a Datagridview of 46 columns consists of Date, Int and String Type. I want to insert the values to another oracle table.
private void loopGrid()
{
foreach (DataGridViewRow datarow in this.dataGridView1.Rows)
{
writeRecord(datarow);
}
}
private void writeRecord(DataGridViewRow datarow)
{
string sqlqry = "insert into Table (Date,Number) values(:Date,:Number)";
OracleCommand cmd = new OracleCommand(sqlqry, conn);
cmd.Parameters.Add(new OracleParameter(":Date", datarow["DATE"]))
cmd.Parameters.Add(new OracleParameter(":Number", datarow["Number"]));
cmd.CommandText = sqlqry;
cmd.ExecuteNonQuery();
}
I don't quite understand what I did wrong about passing the parameters. I'm pretty new to C#, what's the right way to pass the Datatype and value to the other oracle table?
Depending on the Oracle driver you are using, there may be an overload for the .Add method that accepts the value as the second parameter, or there may be an .AddWithValue method that does it explicitly. I think for ODP.net you have it right as just .Add.
Either way, these will derive the datatype from the datatype of the value.
Regarding your parameters, you need to omit the colon : character when invoking the parameters. I know in SQL Server, Sybase, SQLite and others you include them (a # in those cases), but in Oracle you leave them out during the parameter declaration. Interestingly, I think PostgreSQL accepts either methodology.
As such, I think this will work:
private void writeRecord(DataGridViewRow datarow)
{
string sqlqry = "insert into Table (Date,Number) values(:Date,:Number)";
OracleCommand cmd = new OracleCommand(sqlqry, conn);
cmd.Parameters.Add(new OracleParameter("Date", datarow["DATE"]))
cmd.Parameters.Add(new OracleParameter("Number", datarow["Number"]));
// cmd.CommandText = sqlqry; -- not necessary, handled in constructor
cmd.ExecuteNonQuery();
}
If not, maybe try explicit conversions with either the type or value:
Yuck:
cmd.Parameters.Add(new OracleParameter("Date", Convert.ToDateTime(datarow["DATE"])))
cmd.Parameters.Add(new OracleParameter("Number", Convert.ToDecimal(datarow["Number"])));
Better:
cmd.Parameters.Add(new OracleParameter("DATE", OracleDbType.Date,
datarow["DATE"], ParameterDirection.Input);
cmd.Parameters.Add(new OracleParameter("Number", OracleDbType.Decimal,
datarow["Number"], ParameterDirection.Input));
And if worse comes to worse, the bulletproof (although verbose) way to do it would be:
cmd.Parameters.Add(new OracleParameter("DATE", OracleDbType.Date));
cmd.Parameters.Add(new OracleParameter("Number", OracleDbType.Decimal));
cmd.Parameters[0].Value = datarow["DATE"];
cmd.Parameters[1].Value = datarow["Number"];
As a footnote, this last way is actually preferred if you have multiple rows to insert, as you create the parameters once and modify the values and execute the insert multiple times:
cmd.Parameters.Add(new OracleParameter("DATE", OracleDbType.Date));
cmd.Parameters.Add(new OracleParameter("Number", OracleDbType.Decimal));
foreach (DataRow datarow in datarows)
{
cmd.Parameters[0].Value = datarow["DATE"];
cmd.Parameters[1].Value = datarow["Number"];
cmd.ExecuteNonQuery();
}
-- EDIT --
Based on your feedback and my improved proofreading skills, this is what I'd recommend. I added a transaction in there for good measure:
string sqlqry = "insert into Table (Date,Number) values(:Date,:Number)";
OracleTransaction trans = conn.BeginTransaction(IsolationLevel.ReadCommitted);
OracleCommand cmd = new OracleCommand(sqlqry, conn, trans);
cmd.Parameters.Add(new OracleParameter("DATE", OracleDbType.Date));
cmd.Parameters.Add(new OracleParameter("Number", OracleDbType.Decimal));
foreach (DataGridViewRow datarow in this.dataGridView1.Rows)
{
cmd.Parameters[0].Value = datarow.Cells["DATE"].Value;
cmd.Parameters[1].Value = datarow.Cells["Number"].Value;
cmd.ExecuteNonQuery();
}
trans.Commit();
For what it's worth, you also need some exception handling.
You would probably want to use:
cmd.Parameters.Add(new OracleParameter(DbType.Date, datarow["DATE"]))
cmd.Parameters.Add(new OracleParameter(DbType.Double, datarow["Number"]));
Please see https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlparameter.dbtype(v=vs.110).aspx
Related
I am trying to develop a small POS application, and I need to do some data insertion into database, but I am having troubles inserting into two tables at once.
I am using this stored procedure:
// insertpurchase stored procedure
BEGIN TRANSACTION
DECLARE #id int;
INSERT INTO tblsuppling_general (Supplier, Bill_nr, Date, Total, Note)
VALUES (#supplier, #billnr, #date, #total, #note)
SELECT #id = SCOPE_IDENTITY(); // here I want to get ID of the insert of the supply and use on every row in second table
INSERT INTO tblpurchase_details (ID_of_supplying, Barcode, Name, Category, Stock, VAT, QTY, Unit, Supplying_price, Price, Earn_pcs, Bill_nr)
VALUES (#id, #barcode, #name, #category, #stock, #vat, 1, #unit, #sup_price, #price, #earn, #billnr)
COMMIT
And the C# code:
try
{
conn.Open();
foreach (DataGridViewRow row in dtgartikulli.Rows)
{
if (!row.IsNewRow)
{
SqlCommand cmd = new SqlCommand("insertpurchase", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Clear();
cmd.Parameters.Add(new SqlParameter("#supplier", cmbsupplier.Text));
cmd.Parameters.Add(new SqlParameter("#billnr", txtbillnr.Text));
cmd.Parameters.Add(new SqlParameter("#date", DateTime.Now));
cmd.Parameters.Add(new SqlParameter("#total", txttotali.Text));
cmd.Parameters.Add(new SqlParameter("#note", txtnote.Text));
cmd.Parameters.Add(new SqlParameter("#barcode", row.Cells[0].Value));
cmd.Parameters.Add(new SqlParameter("#stock", row.Cells[3].Value));
cmd.Parameters.Add(new SqlParameter("#price", row.Cells[2].Value));
cmd.Parameters.Add(new SqlParameter("#sup_price", row.Cells[4].Value));
cmd.Parameters.Add(new SqlParameter("#earn", row.Cells[1].Value));
cmd.Parameters.Add(new SqlParameter("#fitimi", row.Cells[5].Value));
cmd.Parameters.Add(new SqlParameter("#category", row.Cells[6].Value));
cmd.Parameters.Add(new SqlParameter("#unit", row.Cells[9].Value));
cmd.Parameters.Add(new SqlParameter("#vat", row.Cells[7].Value));
cmd.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
MessageBox.Show("Insertion of transaction failed" + ex.ToString());
}
finally
{
foreach (DataGridViewRow row in dtgartikulli.Rows)
{
if (!row.IsNewRow)
{
SqlCommand cmd = new SqlCommand("insertproducts", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Clear();
cmd.Parameters.Add(new SqlParameter("#barcode", row.Cells[0].Value));
cmd.Parameters.Add(new SqlParameter("#stock", row.Cells[3].Value));
cmd.Parameters.Add(new SqlParameter("#price", row.Cells[2].Value));
cmd.Parameters.Add(new SqlParameter("#sup_price", row.Cells[4].Value));
cmd.Parameters.Add(new SqlParameter("#earn", row.Cells[1].Value));
cmd.Parameters.Add(new SqlParameter("#fitimi", row.Cells[5].Value));
cmd.Parameters.Add(new SqlParameter("#category", row.Cells[6].Value));
cmd.Parameters.Add(new SqlParameter("#unit", row.Cells[9].Value));
cmd.Parameters.Add(new SqlParameter("#vat", row.Cells[7].Value));
cmd.ExecuteNonQuery();
}
}
conn.Close();
fshi();
MessageBox.Show("Transaction is saved successfully");
}
The insert is done, but I want it to be made in a relationship of one to many. For example in table one only one row with general information of the bill, and in the second table the details of the bill(products information). With the above code I am getting in table one as much rows as in table two. For example if I have four rows with product information in table two, I am getting same repeated data in four rows in table with general data of the purchase bill, instead of just one row. My question is what to do, in order to achieve a one to many insertion(one row for general info of purchase- table) and (many rows for purchased product info-table)
Thanks in advance
You can break your procedure in two, one for the tblsuppling_general and another for the tblpurchase_details.
Or you can change your current procedure to:
IF NOT EXISTS (
SELECT 1 FROM tblsuppling_general WHERE
)
BEGIN
INSERT INTO tblsuppling_general (Supplier, Bill_nr, Date, Total, Note)
VALUES (#supplier, #billnr, #date, #total, #note)
END
etc...
But be aware that this kind of insert-if-not-exists is problematic under heavy load because the execution of IF NOT EXISTS and INSERT INTO are not atomic.
Personally, I recommend using two procedures.
I have two SQL queries:
SqlCommand cmdone = new SqlCommand("update HardwareDetails Set Transstat = #Transstat where AssetNo = #AssetNo", con);
cmdone.Parameters.AddWithValue(#"Transstat", "Raised");
cmdone.Parameters.AddWithValue(#"AssetNo", txtAsset.Text);
cmdone.ExecuteNonQuery();
cmdone.Dispose();
And:
SqlCommand cmd = new SqlCommand("Insert into TransferRequest(FrmName,FrmEmpId,ToName) values (#FrmName,#FrmEmpId,#ToName", con);
cmd.Parameters.AddWithValue(#"FrmName", txtfrm.Text);
cmd.Parameters.AddWithValue(#"FrmEmpId", Global.transferorid);
cmd.Parameters.AddWithValue(#"ToName", txtName.Text);
cmd.ExecuteNonQuery();
cmd.Dispose();
Is there a way to combine them into a single query?
Put a semi-colon between the two SQL statements, and add all the parameters.
using (SqlCommand cmd = new SqlCommand("UPDATE HardwareDetails SET Transstat = #Transstat WHERE AssetNo = AssetNo; INSERT INTO TransferRequest (FrmName, FrmEmpId, ToName) VALUES (#FrmName, #FrmEmpId, #ToName)", con))
{
cmd.Parameters.AddWithValue(#"Transstat", "Raised");
cmd.Parameters.AddWithValue(#"AssetNo", txtAsset.Text);
cmd.Parameters.AddWithValue(#"FrmName", txtfrm.Text);
cmd.Parameters.AddWithValue(#"FrmEmpId", Global.transferorid);
cmd.Parameters.AddWithValue(#"ToName", txtName.Text);
cmd.ExecuteNonQuery();
}
Comments:
Its best practice (because its safer) to create your cmd within a using block.
AddWithValue should not be used, instead create the SqlParameter using its constructor and specify the type and precision. E.g. cmd.Parameters.Add(new SqlParameter("#Transstat", SqlDataType.VarChar, 6) { Value = "Raised"});
As pointed out by Liam as it stands this does break the Single Responsibility Principle. Personally I would only use this method if the two statements are linked/related in some way.
string query = #"
update HardwareDetails Set Transstat = #Transstat where AssetNo = #AssetNo
Insert into TransferRequest(FrmName,FrmEmpId,ToName) values (#FrmName,#FrmEmpId,#ToName)";
SqlCommand cmd= new SqlCommand(query,con);
cmd.Parameters.AddWithValue(#"Transstat", "Raised");
cmd.Parameters.AddWithValue(#"AssetNo", txtAsset.Text);
cmd.Parameters.AddWithValue(#"FrmName", txtfrm.Text);
cmd.Parameters.AddWithValue(#"FrmEmpId", Global.transferorid);
cmd.Parameters.AddWithValue(#"ToName", txtName.Text);
cmd.ExecuteNonQuery();
cmd.Dispose();
How do I insert into parameters from different tables using OleDB?
I have 3 tables:
1. itemTbl
2. crateTbl
3. contentTbl
itemTbl has: itemID, itemName, itemDesc
crateTbl has: crateID, crateName
contentTbl has: crateID, itemID, qty
contentTbl is the contents of the crate and the qty of each
I need it to select values in different tables I use WHERE. I have tried a similar code using a local db and service based db and they allow me, but OleDB doesn't let me use VALUES((SELECT))....
Error message:
System.Data.OleDb.OleDbException: 'Query input must contain at least one table or query.
My code:
cmd.Dispose();
cmd.CommandText = #"INSERT INTO contentTbl(crateID,itemID,qty) VALUES((SELECT crateTbl.crateID FROM crateTbl WHERE crateTbl.crateID=?),(SELECT itemTbl.itemID FROM itemTbl WHERE itemTbl.itemID = ?), ?)";
cmd.Connection = con;
cmd.Parameters.Add(new OleDbParameter("crateID", txtCrate.Text));
cmd.Parameters.Add(new OleDbParameter("itemID", txtItem.Text));
cmd.Parameters.Add(new OleDbParameter("qty", txtQty.Text));
con.Open();
cmd.ExecuteNonQuery();
con.Close();
MessageBox.Show("Done!");
da.SelectCommand = new OleDbCommand("SELECT * FROM contentTbl", con);
da.Fill(dt);
dgvContent.DataSource = dt;
The error message is quite descriptive. Access doesn't support subqueries without a main query, so change the syntax round to use one of the subqueries as your main query:
INSERT INTO contentTbl(crateID,itemID,qty)
SELECT crateTbl.crateID,(SELECT itemTbl.itemID FROM itemTbl WHERE itemTbl.itemID = ?), ?
FROM crateTbl WHERE crateTbl.crateID=?
Note that parameters are passed by position, and rewriting this query does require you to re-order parameters:
cmd.Parameters.Add(new OleDbParameter("itemID", txtItem.Text));
cmd.Parameters.Add(new OleDbParameter("qty", txtQty.Text));
cmd.Parameters.Add(new OleDbParameter("crateID", txtCrate.Text));
If you don't like the main query/subquery syntax, you can go for a cross join too:
INSERT INTO contentTbl(crateID,itemID,qty)
SELECT crateTbl.crateID, itemTbl.itemID, ?
FROM crateTbl,itemTbl
WHERE crateTbl.crateID=? AND itemTbl.itemID = ?
(Parameter order needs to be adjusted again but you can figure that out).
I am not sure about the syntax. I always use it like (this is from VB, but C# should be similar) :
cmd.Parameters.Addwithvalue("crateID", txtCrate.Text)
I have tried this and it sort of works, but for some reason works only with values for the items table as ID's. If I have an ID (they're ints) that crateTbl has but itemTbl doesn't, it doesn't insert.
cmd.Dispose();
cmd.CommandText = #"INSERT INTO contentTbl(itemID,crateID,qty) SELECT itemTbl.itemID,(SELECT crateTbl.crateID FROM crateTbl WHERE crateTbl.crateID = ?), ? FROM itemTbl WHERE itemTbl.itemID=?";
cmd.Connection = con;
cmd.Parameters.Add(new OleDbParameter("itemID", txtItem.Text));
cmd.Parameters.Add(new OleDbParameter("qty", txtQty.Text));
cmd.Parameters.Add(new OleDbParameter("crateID", txtCrate.Text));
con.Open();
cmd.ExecuteNonQuery();
con.Close();
MessageBox.Show("Done!");
dt2.Clear();
I have an inventory system and this code is for when a user creates a new item. It's supposed to insert a 0 value in the inventory table since it's a new item. My code is:
string queryAdd4 = "INSERT INTO [inventory]([item_id],[item_qty],[item_date],[item_type]) VALUES(#myID,#myQty,#myDate,#myType)";
using (SqlCommand cmd = new SqlCommand(queryAdd4, Con))
{
cmd.Parameters.Add(new SqlParameter("#myID", item_id));
cmd.Parameters.Add(new SqlParameter("#myQty", 0));
cmd.Parameters.Add(new SqlParameter("#myDate", dateNow));
cmd.Parameters.Add(new SqlParameter("#myType", 1));
Con.Open();
cmd.ExecuteNonQuery();
Con.Close();
}
With that code, i'm getting an error saying:
The parameterized query '(#myID int,#myQty bigint,#myDate datetime,#myType int)
INSERT INT' expects the parameter '#myQty', which was not supplied
Out of curiosity, I tried replacing the 0 beside the #myQty with 1 and the query worked without problems. I also tried manually running the query through the Server Explorer and that worked as well. So I'm guessing 0 is not a valid number to insert when using parameterized queries? If so, how would I go about doing it?
When using two parameters with SqlParameter Constructor, there are two choices:
SqlParameter(string parameterName, SqlDbType dbType)
SqlParameter(string parameterName, object value)
When using an integer, the first choice is used. If you want to use the two parameter constructor, you have to cast 0 to an object:
cmd.Parameters.Add(new SqlParameter("#myQty", (object)0));
Also regard the oneliner from Sinatr in the comments:
cmd.Parameters.Add(new SqlParameter("#myQty", 0) { SqlDbType = SqlDbType.Int });
try to set the specific type to your parameter like here;
Take a look at a database and set it according to the type which is set to the column.
string queryAdd4 = "INSERT INTO [inventory]([item_id],[item_qty],[item_date],[item_type]) VALUES(#myID,#myQty,#myDate,#myType)";
using (SqlCommand cmd = new SqlCommand(queryAdd4, Con))
{
cmd.Parameters.Add(new SqlParameter("#myID", item_id));
var parameter = new SqlParameter()
parameter.ParameterName = "#myQty";
parameter.SqlDbType = SqlDbType.Int;
parameter.Direction = ParameterDirection.Input;
parameter.Value = 0;
cmd.Parameters.Add(parameter);
cmd.Parameters.Add(new SqlParameter("#myDate", dateNow));
cmd.Parameters.Add(new SqlParameter("#myType", 1));
Con.Open();
cmd.ExecuteNonQuery();
Con.Close();
Sources:
List of types:
https://msdn.microsoft.com/en-us/library/system.data.sqldbtype(v=vs.110).aspx
Configuring query parameters:
https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/configuring-parameters-and-parameter-data-types
Hope it helps.
From C# Code, I'm trying to call a PACKAGE.PROCEDURE() from Oracle. In this simple example I should get one value from the procedure call, but all I get is error:
wrong number or types of arguments in call to 'RETURN_NUM'
The procedure is declared as follows:
PROCEDURE return_num(xNum OUT NUMBER) AS
BEGIN
xNum:= 50;
dbms_output.put_line('hello world ' || xNum);
END;
C# code:
Oraclecon.Open();
OleDbCommand myCMD = new OleDbCommand("TEST.return_num", Oraclecon);
myCMD.CommandType = CommandType.StoredProcedure;
myCMD.Parameters.Add("xNum", OleDbType.Numeric);
OleDbDataReader myReader;
myReader = myCMD.ExecuteReader();
Can some one please point out what I'm doing wrong. Then in a real scenario I would like to call a procedure that returns a set of values from a custom Type, such as:
TYPE r_interface_data IS RECORD
(
object_id VARCHAR2(16),
obj_type VARCHAR2(32)
);
TYPE t_interfase_data IS TABLE OF r_interface_data;
How can I approach that. Thanks!
UPDATE: In my particular case I ended-up doing the following approach
using (OleDbCommand cmd = new OleDbCommand("PACKAGE.procedure_name"))
{
cmd.CommandType = CommandType.StoredProcedure;
SqlManager sqlManager = new SqlManager();
return sqlManager.GetDataSet(cmd);
}
I don't think you're that far off... try this:
OracleCommand cmd = new OracleCommand("return_num", Oraclecon);
cmd.Parameters.Add(new OracleParameter("xNum", OracleDbType.Decimal,
ParameterDirection.Output));
cmd.CommandType = CommandType.StoredProcedure;
cmd.ExecuteNonQuery();
OracleDecimal d = (OracleDecimal)cmd.Parameters[0].Value;
double result = d.ToDouble();
result now contains the out parameter from the procedure.
I think your problem is you were attempting to use a DbDataReader on a stored procedure. DbDataReader is for queries.
Also, I used ODP.net -- that may or may not have contributed to your issue, that you were using Ole.