bulk insert with DB2 iSeries (C#) - c#

I'm trying to make a bulk insert into db2 using C#.
CREATE TABLE tsdta.ftestbk1
(
NUM numeric(8,0),
TEXT varchar(30)
)
And here is my test code:
using IBM.Data.DB2.iSeries;
...
using (iDB2Connection connection = new iDB2Connection("xxx"))
{
string sql = #"insert into tsdta.ftestbk1
values
(
#num,
#text
)";
connection.Open();
iDB2Command command = connection.CreateCommand();
command.CommandType = CommandType.Text;
command.CommandText = sql;
command.Prepare();
command.DeriveParameters();
for (int i = 0; i < 5; i++)
{
command.Parameters.Add("#num", i);
command.Parameters.Add("#text", $"some text for {i}");
command.AddBatch();
}
int rows = command.ExecuteNonQuery();
connection.Close();
}
When I execute the code, in the table are only NULL values.
NUM | TEXT
------------
<null> |<null>
<null> |<null>
<null> |<null>
<null> |<null>
<null> |<null>
Can anyone tell me what I'm doing wrong?

Without a Db2 instance to double check, I am doing this from memory and some old code but I have the following which is running in our prod env for me (I've adjusted it to look like yours)
using (iDB2Connection connection = new iDB2Connection("xxx"))
{
iDB2Command command = new iDB2Command("INSERT INTO TSDTA.FTESTBK1 VALUES(#NUM, #TEXT", connection);
// Initialize the parameters collection
command.DeriveParameters();
for (int i = 0; i < 5; i++)
{
command.Parameters["#NUM"].Value = i;
command.Parameters["#TEXT"].Value = $"some text {i}";
command.AddBatch();
}
command.ExecuteNonQuery();
}
We had a similar problem at the time and I got the original code from here...
C# - Insert Multiple Records at once to AS400

Related

update a table by using for loop with parameters in C#

I have a table with some columns like
now I want to use a for loop to set
out_0 = 0,
out_1 = 1,
out_2 = 2,
out_3 = 3,
out_4 = 4
so I update it with such code as
string sql = "update exchange_out set #column = #id where member_id = 6;";
SqlCommand cmd = new SqlCommand(sql, connet);
cmd.Parameters.Add("#column", SqlDbType.NVarChar);
cmd.Parameters.Add("#id", SqlDbType.Int);
int n = 0;
for (int i = 0; i < 5; i++)
{
cmd.Parameters["#column"].Value = "out_" + i;
cmd.Parameters["#gid"].Value = i;
n = cmd.ExecuteNonQuery();
MessageBox.Show("" + n);
}
but it didn't write any data into the table while it literally did five times of updating, because the messagebox returns "1" five times.
finally I solve this by
for (int i = 0; i < 5; i++){
sql = string.Format("update exchange_out set {0} = {1} where member_id = 6", "out_" + i, i);
}
but I'm still wondering why it didn't work by adding parameters?
any respond will be appreciated. :)
I'm still wondering why it didn't work by adding parameters?
Identifiers such as table and column names cannot be parameterized in this way, only data. Your attempt effectively runs a query like this:
update exchange_out set 'out_1' = 1 where member_id = 6;
It's the same in any programming language:
var data1 = "hello";
var whichData = "1";
Console.WriteLine(data+whichData); //it doesn't compile; you cannot programmatically build a variable name `data1` in this way
The way you found is reasonably the only way but you should still parameterize the data:
using var cmd = new SqlCommand(sql, connet);
cmd.Parameters.Add("#data", SqlDbType.NVarChar);
cmd.Parameters.Add("#id", SqlDbType.Int);
for (int i = 0; i < 5; i++){
sql = string.Format("update exchange_out set out_{0} = #data where member_id = #id", i);
cmd.CommandText = sql;
cmd.Parameters["#data"].Value = ...
cmd.Parameters["#id].Value = 6;
...
You could also start with an SQL stub like "UPDATE t SET " and repeatedly concatenate on identifiers and parameters:
using var cmd = new SqlCommand(sql, connet);
cmd.Parameters.Add("#data", SqlDbType.NVarChar);
cmd.Parameters.Add("#id", SqlDbType.Int);
var sql = "UPDATE exchange_out SET ";
for (int i = 0; i < 5; i++){
sql += string.Format("out_{0} = #data{0},", i);
cmd.Parameters["#data"+i].Value = ...
}
sql = sql.TrimEnd(',');
sql += " where member_id = #id";
cmd.Parameters["#id"].Value = 6;
cmd.CommandText = sql;
...
This does the update in one operation, running a query like UPDATE t SET out_1 = #data1, out_2 = #data2 ...
These are safe from SQL injection because your code controls the entire SQL; there isn't any capacity for a user to provide '; DROP TABLE Students;-- as the {0} going into the identifier in this case but take care that you don't arrange for it to be possible (don't let the user provide identifier text)..
Your non-parameter attempt is also safe from SQL injection in this case by virtue of inserting intergers that you control, rather than strings you don't, but be careful you don't universally apply the technique and one day include user-suppied strings. If you do find yourself in that suitable you should use something like a whitelist of user input - any string identifier provided by the user that isn't whitelisted should not be put in the SQL

C# DataReader in 'some cases' returns null while Sql server returns value

Summary:
C# SqlDataReader can return table in most cases but failed in only one unit test case. However, for same test, directly running the stored procedure in Microsoft SQL Server 2016 returns table with value.
Detail:
The code input #measures is a DataTable type. In most cases, the code can return table with data or empty table correctly. However, when I set #measures to a DataTable with value 14 (14 is ID for one measure/fact and procedure should return one column of type int) , the datareader will return no row and not even the schema of the table.
I tried other valid ID (measures) by which procedure returns other int column. They all work.
using (this._connection = new SqlConnection(this._connectionString))
{
SqlCommand command = new SqlCommand();
command.Connection = this._connection;
command.CommandText = "rpt_procedure";
command.CommandTimeout = this._config.ConfTimeOutSetting;
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("#measures", SqlDbType.Structured).Value = measures;
command.Parameters.Add("#parameter2", SqlDbType.Structured).Value = parameter2;
command.Parameters.Add("#Dates", SqlDbType.Structured).Value = Datetable;
command.Parameters.AddWithValue("#Gra", gra);
this._connection.OpenWithRetry(this.RetryPolicy);
using (SqlDataReader reader = command.ExecuteReader())
{
this.NumRow = 0;
DataTable schemaTable = reader.GetSchemaTable();
string[] colName = new string[colNum];
int startPoint = 0;
if (schemaTable == null)
MessageBox.Show("cannot get schema of table");
else
{
foreach (DataRow myField in schemaTable.Rows)
{
if (startPoint >= colNum)
break;
List<string> temp = new List<string>();
//For each property of the field...
colName[startPoint] = myField[schemaTable.Columns[0]].ToString();
startPoint++;
}
arrayList.Add(colName);
}
if (!reader.HasRows)
return arrayList;
while (reader.Read())
{
this.NumRow++;
string[] r = new string[colNum];
for (int i = 0; i < colNum; i++)
{
r[i] = reader[i].ToString();
}
arrayList.Add(r);
}
return arrayList;
}
}
SQL test code that returns value
declare #Measures tvpBigInt
insert into #Measures
select 14
declare #parameter2 tvpBigInt
insert into #parameter2
select 22
union
select 10
declare #Grain NVARCHAR(20)
select #Grain = 'Daily'
declare #Dates tvpDate
insert into #Dates
select '2016-12-30','2017-02-06'
exec rpt_procedure
#Measures = #Measures
#parameter2 = #parameter2
,#Gra = 'Daily'
,#Dates = #Dates
Action I took according to previous StackOverflow questions:
Tried dataadapter
Tried executenonquery cmd.ExecuteNonQuery();
Both did not work.

Update value in SQL table using C#

I need to change my field QB_STATUS from value R to value C. I am doing this in a loop because i cannot "requery" the table as data may have changed.
I have built a list of entries to update. The code does not error and iterates through 5 times (correct based on my idInvoices list) but the field does not get updated.
for (int i = 0; i < idInvoices.Count; i++)
{
// following command will update one row as ID_Invoice is primary key.
// ID_Invoice taken from list previously built in ReadDataToNAVArray
SqlCommand cmd = new SqlCommand("UPDATE tblINVOICES SET QB_STATUS=#Status WHERE ID_INVOICE = #IDInvoice", myConnection);
cmd.Parameters.Add("#Status", "C");
cmd.Parameters.Add("#IDInvoice", idInvoices[i]);
cmd.Dispose();
}
First, you have to execute your query: ExecuteNonQuery; second - do not create command, parameters etc within the loop, just assign values and execute:
// Make SQL readable
String sql =
#"UPDATE tblINVOICES
SET QB_STATUS = #Status
WHERE ID_INVOICE = #IDInvoice";
// wrap IDisposable into "using"
// do not recreate command in the loop - create it once
using (SqlCommand cmd = new SqlCommand(sql, myConnection)) {
cmd.Parameters.Add("#Status", SqlDbType.VarChar); //TODO: check types, please
cmd.Parameters.Add("#IDInvoice", SqlDbType.Decimal); //TODO: check types, please
// Assign parameters with their values and execute
for (int i = 0; i < idInvoices.Count; i++) {
cmd.Parameters["#Status"].Value = "C";
cmd.Parameters["#IDInvoice"].Value = idInvoices[i];
cmd.ExecuteNonQuery();
}
}
You are missing the ExecuteNonQuery in your command.
for (int i = 0; i < idInvoices.Count; i++)
{
SqlCommand cmd = new SqlCommand("UPDATE tblINVOICES SET QB_STATUS=#Status WHERE ID_INVOICE = #IDInvoice", myConnection);
cmd.Parameters.Add("#Status", "C");
cmd.Parameters.Add("#IDInvoice", idInvoices[i]);
cmd.ExecuteNonQuery();
cmd.Dispose();
}
I think you're missing cmd.ExecuteNonQuery();.
An example for a different way of using sql commands:
SqlConnection addConn = new SqlConnection();
addConn.ConnectionString = Properties.Settings.Default.yourDataBaseConnection;
addConn.Open();
SqlCommand addComm = new SqlCommand();
addComm.Connection = addConn;
addComm.CommandText = "sql command";
addComm.ExecuteNonQuery();

Fastest way to update more than 50.000 rows in a mdb database c#

I searched on the net something but nothing really helped me. I want to update, with a list of article, a database, but the way that I've found is really slow.
This is my code:
List<Article> costs = GetIdCosts(); //here there are 70.000 articles
conn = new OleDbConnection(string.Format(MDB_CONNECTION_STRING, PATH, PSW));
conn.Open();
transaction = conn.BeginTransaction();
using (var cmd = conn.CreateCommand())
{
cmd.Transaction = transaction;
cmd.CommandText = "UPDATE TABLE_RO SET TABLE_RO.COST = ? WHERE TABLE_RO.ID = ?;";
for (int i = 0; i < costs.Count; i++)
{
double cost = costs[i].Cost;
int id = costs[i].Id;
cmd.Parameters.AddWithValue("data", cost);
cmd.Parameters.AddWithValue("id", id);
if (cmd.ExecuteNonQuery() != 1) throw new Exception();
}
}
transaction.Commit();
But this way take a lot of minutes something like 10 minutes or more. There are another way to speed up this updating ? Thanks.
Try modifying your code to this:
List<Article> costs = GetIdCosts(); //here there are 70.000 articles
// Setup and open the database connection
conn = new OleDbConnection(string.Format(MDB_CONNECTION_STRING, PATH, PSW));
conn.Open();
// Setup a command
OleDbCommand cmd = new OleDbCommand();
cmd.Connection = conn;
cmd.CommandText = "UPDATE TABLE_RO SET TABLE_RO.COST = ? WHERE TABLE_RO.ID = ?;";
// Setup the paramaters and prepare the command to be executed
cmd.Parameters.Add("?", OleDbType.Currency, 255);
cmd.Parameters.Add("?", OleDbType.Integer, 8); // Assuming you ID is never longer than 8 digits
cmd.Prepare();
OleDbTransaction transaction = conn.BeginTransaction();
cmd.Transaction = transaction;
// Start the loop
for (int i = 0; i < costs.Count; i++)
{
cmd.Parameters[0].Value = costs[i].Cost;
cmd.Parameters[1].Value = costs[i].Id;
try
{
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
// handle any exception here
}
}
transaction.Commit();
conn.Close();
The cmd.Prepare method will speed things up since it creates a compiled version of the command on the data source.
Small change option:
Using StringBuilder and string.Format construct one big command text.
var sb = new StringBuilder();
for(....){
sb.AppendLine(string.Format("UPDATE TABLE_RO SET TABLE_RO.COST = '{0}' WHERE TABLE_RO.ID = '{1}';",cost, id));
}
Even faster option:
As in first example construct a sql but this time make it look (in result) like:
-- declaring table variable
declare table #data (id int primary key, cost decimal(10,8))
-- insert union selected variables into the table
insert into #data
select 1121 as id, 10.23 as cost
union select 1122 as id, 58.43 as cost
union select ...
-- update TABLE_RO using update join syntax where inner join data
-- and copy value from column in #data to column in TABLE_RO
update dest
set dest.cost = source.cost
from TABLE_RO dest
inner join #data source on dest.id = source.id
This is the fastest you can get without using bulk inserts.
Performing mass-updates with Ado.net and OleDb is painfully slow. If possible, you could consider performing the update via DAO. Just add the reference to the DAO-Library (COM-Object) and use something like the following code (caution -> untested):
// Import Reference to "Microsoft DAO 3.6 Object Library" (COM)
string TargetDBPath = "insert Path to .mdb file here";
DAO.DBEngine dbEngine = new DAO.DBEngine();
DAO.Database daodb = dbEngine.OpenDatabase(TargetDBPath, false, false, "MS Access;pwd="+"insert your db password here (if you have any)");
DAO.Recordset rs = daodb.OpenRecordset("insert target Table name here", DAO.RecordsetTypeEnum.dbOpenDynaset);
if (rs.RecordCount > 0)
{
rs.MoveFirst();
while (!rs.EOF)
{
// Load id of row
int rowid = rs.Fields["Id"].Value;
// Iterate List to find entry with matching ID
for (int i = 0; i < costs.Count; i++)
{
double cost = costs[i].Cost;
int id = costs[i].Id;
if (rowid == id)
{
// Save changed values
rs.Edit();
rs.Fields["Id"].Value = cost;
rs.Update();
}
}
rs.MoveNext();
}
}
rs.Close();
Note the fact that we are doing a full table scan here. But, unless the total number of records in the table is many orders of magnitude bigger than the number of updated records, it should still outperform the Ado.net approach significantly...

How to retrieve data from mdx query in c#?

I am trying to get data from an MDX query using the Adomdclient library. I relied on this example http://www.yaldex.com/sql_server/progsqlsvr-CHP-20-SECT-6.html.
MDX query:
SELECT {[Measures].[Cantidad Vta],[Measures].[Monto Vta],[Measures].[ExistenciaHistorica],[Measures].[Valor Inventario historico]} DIMENSION PROPERTIES PARENT_UNIQUE_NAME ON COLUMNS , NON EMPTY Hierarchize({DrilldownLevel({[DIM SUBMARCA].[Código].[All]})}) DIMENSION PROPERTIES PARENT_UNIQUE_NAME ON ROWS FROM (SELECT ({[DIM TIENDA].[JERARQUIA TIENDA].[Región].&[Bodega],[DIM TIENDA].[JERARQUIA TIENDA].[Región].&[Cadena],[DIM TIENDA].[JERARQUIA TIENDA].[Región].&[Outlet]}) ON COLUMNS FROM [JUGUETRONHQ]) WHERE ([DIM FECHA VENTA].[JERARQUIA FECHA VENTA].[Time].&[2012-01-01T00:00:00],[DIM FECHA EXISTENCIA].[JERARQUIA FECHA EXISTENCIA].[All]) CELL PROPERTIES VALUE
Like other namespaces such as SqlClient, use a connection, a command and a datareader:
using Microsoft.AnalysisServices.AdomdClient;
...
using (AdomdConnection con = new AdomdConnection(connection_string))
{
con.Open();
using (AdomdCommand command = new AdomdCommand(query, con))
{
using (AdomdDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
Console.Write(reader[i] + (i == reader.FieldCount - 1 ? "" : ", "));
Console.WriteLine("");
}
}
}
}
However, this snippet only shows 4 of 5 columns correctly:
[DIM SUBMARCA].[Código].[All], , , 3, 825
It must be:
115200081, , , 3, 825
Perhaps need a cast but I don't know how to do it.
This looks like a problem with the MDX query, not the retrieval of the data. It's not correctly constraining on the [DIM SUBMARCA].[Código] dimension.
Your query has 1 [ALL] Level Dimension and 4 measures:
[DIM SUBMARCA].[Código].[All],
[Measures].[Cantidad Vta],
[Measures].[Monto Vta],
[Measures].[ExistenciaHistorica],
[Measures].[Valor Inventario historico]
This retrieves 1 [ALL] column and 4 Values:
[DIM SUBMARCA].[Código].[All], , , 3, 825
115200081 is a key value? You could get this value using "DIMENSION PROPERTIES MEMBER_HEY".
Retrieve data from MDX query
added the reference for Microsoft.AnalysisServices.AdomdClient.dll
AdomdConnection steps
AdomdConnection con = new AdomdConnection("connectionstring"); // connect DB con.Open(); AdomdCommand cmd = new AdomdCommand("MDX query", con); //query
AdomdDataReader reader = cmd.ExecuteReader(); //Execute query
while (reader.Read()) // read
{
Data dt = new Data(); // custom class
dt.Gender = reader[0].ToString();
dt.Eid = reader[1].ToString();
dt.salary = reader[2].ToString();
data.Add(dt);
}

Categories