I am trying to do this task.
I have this table in my database.
items_table
------------------
Item_Name | Item ID
A | 1
B | 1
C | 2
D | 2
E | Null
F |
G | 1
H |
I | Null
Select * from items_table where Item_ID is Null or Item_ID is empty
Loop(while there are items without Item_ID)
Check the count of first Item_ID
if first Item_ID count is less than 8
(update items_table values (current Item_ID ) where Item_Name is current row Item_Name )
otherwise check next Item_ID
If no Item_ID count is less than 8, insert max((Item_ID)+1) where Item_Name is current row Item_Name
For the above table this code should do something like this.
E,F,H,I have Group_ID null or empty
Now i have to insert Item_ID for all these items.
First check count of all existing Item_IDs in the table. If any item_ID is used with less than 8 items, than insert that Item_ID for current Item. If no Item_ID has count less than 8, than create a new Item_ID which should be maximum Item_ID + 1.
I am trying to write this but can't figure out how can i loop through rows and count IDs than insert existing one or new one.
private static void FIllGroupID(string connectionString)
{
string queryStringNoGroupID =
"Use Items select * from table_items_shelves where Item_ID is Null or Item_ID = '';";
SqlCommand GetAllWithoutID = new SqlCommand(queryStringNoGroupID);
DataTable DataTableAllWithoutID = new DataTable();
SqlDataAdapter adapterAllWithoutID = new SqlDataAdapter(GetAllWithoutID);
adapterAllWithoutID.Fill(DataTableAllWithoutID);
foreach (DataRow row in DataTableAllWithoutID.Rows)
{
}
}
How do i loop through existing item_ids and count them. If count is less than 8 than insert the same ID in current row or else create max(item_id)+1 and insert that.
Now who is going to remove negative votes from the question?
const string str = #"Data Source=localhost;Initial Catalog=Items;Integrated Security=True";
static void Main(string[] args)
{
const string connectionString = str;
DataTable DataTableAllWithoutID = new DataTable();
#if !test
string queryString = "select * from table_items_shelves;";
SqlDataAdapter adapterAllWithoutID = new SqlDataAdapter(queryString, connectionString);
adapterAllWithoutID.Fill(DataTableAllWithoutID);
adapterAllWithoutID.Dispose();
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
string insertString = "Update table_items_shelves Set Item_ID = #Item_ID where Item_Name = '#key';";
SqlCommand insertCommand = new SqlCommand(insertString, connection);
insertCommand.Parameters.Add("#Item_ID", SqlDbType.Int);
insertCommand.Parameters.Add("#key", SqlDbType.NVarChar);
#else
DataTableAllWithoutID.Columns.Add("Item_Name", typeof(string));
DataTableAllWithoutID.Columns.Add("Item_ID", typeof(object));
foreach (List<object> row in input)
{
DataRow newRow = DataTableAllWithoutID.Rows.Add();
newRow.ItemArray = row.ToArray();
}
#endif
//this code will get empty items
List<DataRow> nullOrEmpty = DataTableAllWithoutID.AsEnumerable()
.Where(x => x.Field<object>("Item_ID") == null)
.ToList();
//this creates a dictionary of valid items
Dictionary<int, List<DataRow>> dict = DataTableAllWithoutID.AsEnumerable()
.Where(x => x.Field<object>("Item_ID") != null)
.GroupBy(x => x.Field<object>("Item_ID"), x => x)
.ToDictionary(x => Convert.ToInt32(x.Key), x => (List<DataRow>)x.ToList());
//create IEnumerator for the null items
IEnumerator<DataRow> emptyRows = nullOrEmpty.GetEnumerator();
Boolean noMoreEmptyRows = false;
if (emptyRows != null)
{
foreach (int key in dict.Keys)
{
Console.WriteLine(key.ToString());
//get count of items
int count = dict[key].Count;
int itemID = (int)key;
for (int index = count; count < 8; count++)
{
if (emptyRows.MoveNext())
{
//get an item from the null list
emptyRows.Current["Item_ID"] = itemID;
insertCommand.Parameters["#Item_ID"].Value = itemID;
insertCommand.Parameters["#key"].Value = emptyRows.Current["Item_Name"];
insertCommand.ExecuteNonQuery();
Console.WriteLine("current item ID " + itemID);
Console.WriteLine("current count " + count);
//Console.ReadKey();
}//end if
else
{
noMoreEmptyRows = true;
break;
}//end else
}//end for
if (noMoreEmptyRows)
break;
}//end foreach
if (!noMoreEmptyRows)
{
//increment key to one greater than max value
int maxKey = dict.Keys.Max() + 1;
int count = 0;
while (emptyRows.MoveNext())
{
//get an item from the null list
emptyRows.Current["Item_ID"] = maxKey.ToString();
insertCommand.Parameters["#Item_ID"].Value = maxKey.ToString();
insertCommand.Parameters["#key"].Value = emptyRows.Current["Item_ID"];
insertCommand.ExecuteNonQuery();
count++;
if (count == 8)
{
maxKey++;
count = 0;
}
}
}
}//end if
#if test
foreach (DataRow row in DataTableAllWithoutID.Rows)
{
Console.WriteLine("Item_Name : {0} Item ID : {1}",
row["Item_Name"], row["Item_ID"]);
}
#endif
FIllGroupID(str);
Console.ReadKey();
}
Related
Increment quantity by one (+1) if the product id is exist in current datagridview
My datagridview has 9 columns:
Column 1 = Edit_Checkbox Column 2 = ItemCount
Column 3 = DGV_PRODUCT_ID Column 4 = DGV_PRODUCT_DESC
Column 5 = DGV_UNIT_PRICE Column 6 = DGV_QUANTITY
Column 7 = DGV_DISCOUNT Column 8 = DGV_TOTAL_PRICE
Column 9 = DGV_NOTES
I am trying to increment quantity by one (+1) if the product id exists in current datagridview
My code below works fine but I have an issue which is when same product id exists it will
increment the Quantity by one if the process is done sequentially, But if I entered a product id
in a non-sequential way it will not increment quantity by one, instead inserting a new row in datagridview.
For example:
Enter product id number 1003 2 item quantity will be 2
Enter product id number 3000 1 item quantity will be 1
Enter product id number 1003 1 item quantity will still be 1 when the product already exists in datagridview. How do I avoid that case ?
private void SelectedProductData()
{
int ItemCount = DGV_INVOICE.Rows.Count;
bool ProductIDExist = false;
string connstr = #"Data Source=orcl; User Id=user; password=pwd;";
string cmdtxt = #"SELECT PRODUCT_ID,
PRODUCT_DESC,
UNIT_PRICE
FROM WAREHOUSE
WHERE PRODUCT_ID = :P_Product_ID";
try
{
using (OracleConnection conn = new OracleConnection(connstr))
using (OracleCommand cmd = new OracleCommand(cmdtxt, conn))
{
conn.Open();
cmd.CommandType = CommandType.Text;
cmd.CommandText = cmdtxt;
cmd.Parameters.Add(new OracleParameter(":P_Product_ID", OracleDbType.Varchar2)).Value = TB_Product_ID.Text;
OracleDataReader oraReader = cmd.ExecuteReader();
while (oraReader.Read())
{
ItemCount++;
RowCountLabel.Text = ItemCount.ToString();
DataGridViewRow dgvRow = new DataGridViewRow();
foreach (DataGridViewRow ItemRow in DGV_INVOICE.Rows)
{
if (Convert.ToString(ItemRow.Cells[2].Value) == TB_Product_ID.Text)
{
MessageBox.Show(Convert.ToString(ItemRow.Cells[2].Value) + " 1 " + TB_Product_ID.Text);
ProductIDExist = true;
break;
}
}
if (ProductIDExist)
{
MessageBox.Show("2");
//dgvRow.Cells[5].Value = Convert.ToString(1 + Convert.ToInt64(dgvRow.Cells[5].Value));
DGV_INVOICE.Rows[dgvRow.Index].Cells[5].Value = Convert.ToString(1 + Convert.ToInt64(dgvRow.Cells[5].Value));
}
else
{
MessageBox.Show("3");
//Add the row to grid view for the first time
dgvRow.Cells.Add(new DataGridViewCheckBoxCell()); //Edit_Checkbox index 0
dgvRow.Cells[0].Value = false;
dgvRow.Cells.Add(new DataGridViewTextBoxCell()); //ItemCount index 1
dgvRow.Cells[1].Value = ItemCount;
dgvRow.Cells.Add(new DataGridViewTextBoxCell()); //DGV_PRODUCT_ID index 2
dgvRow.Cells[2].Value = oraReader.GetValue(0);
dgvRow.Cells.Add(new DataGridViewTextBoxCell()); //DGV_PRODUCT_DESC index 3
dgvRow.Cells[3].Value = oraReader.GetString(1);
dgvRow.Cells.Add(new DataGridViewTextBoxCell()); //DGV_UNIT_PRICE index 4
dgvRow.Cells[4].Value = oraReader.GetValue(2);
dgvRow.Cells.Add(new DataGridViewTextBoxCell()); //DGV_QUANTITY index 5
dgvRow.Cells[5].Value = "1";
dgvRow.Cells.Add(new DataGridViewTextBoxCell()); //DGV_DISCOUNT index 6
dgvRow.Cells[6].Value = "0";
//dgvRow.Cells.Add(new DataGridViewTextBoxCell()); //DGV_TOTAL_PRICE index 7
//dgvRow.Cells[7].Value = "0";
//dgvRow.Cells.Add(new DataGridViewTextBoxCell()); //DGV_NOTES index 8
//dgvRow.Cells[8].Value = "-";
DGV_INVOICE.Rows.Add(dgvRow);
dgvRow.Selected = true;
}
}
}
}
catch (Exception EX)
{
MessageBox.Show(EX.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
TB_ORDER_NOTE.Text = EX.Message; /* Testing Purpose */
}
}
Instead, try
bool ProductIDExist = false;
foreach (DataGridViewRow ItemRow in DGV_INVOICE.Rows)
{
//Check if the product Id exists with the same Price
ProductIDExist |= Convert.ToString(ItemRow.Cells[1].Value) == TB_Product_ID.Text
if (productIDExist)
{
break;
}
}
if (productIDExist)
{
//increase quantity by 1
}
else
{
//add a new row
}
Need your help. I have to get csv file from user and update database columns according to that file.
For Example:
File has following fields:
Name | Description | Date
Another file has:
Name | Price
Please Guide me how i could do this in c# using sql 2008.
From checkboxes user can select columns which he wants to update.
else if (count == 4 && dataGridView1.ColumnCount - 1 == 4)
{
for (int i = 0; i < dataGridView1.RowCount - 1; i++)
{
int id = Convert.ToInt32(dataGridView1.Rows[i].Cells["Id"].Value);
ItemsC.UpdateDatacolumns(id, columnsList[1], dataGridView1.Rows[i].Cells[1].Value.ToString(), columnsList[2], dataGridView1.Rows[i].Cells[2].Value.ToString(), columnsList[3], dataGridView1.Rows[i].Cells[3].Value.ToString());
}
}
else if (count == 5 && dataGridView1.ColumnCount - 1 == 5)
{
for (int i = 0; i < dataGridView1.RowCount - 1; i++)
{
int id = Convert.ToInt32(dataGridView1.Rows[i].Cells["Id"].Value);
ItemsC.UpdateDatacolumns(id, columnsList[1], dataGridView1.Rows[i].Cells[1].Value.ToString(), columnsList[2], dataGridView1.Rows[i].Cells[2].Value.ToString(), columnsList[3], dataGridView1.Rows[i].Cells[3].Value.ToString(), columnsList[4], dataGridView1.Rows[i].Cells[4].Value.ToString());
}
}
In Backend Just Override Update Method:
public static bool UpdateDatacolumns(int id, string column, string value, string column2, string value2)
{
SqlConnection conn = database.getConnection();
string query = "update destinationItemTable set " + column + "= #value ," + column2+" =#value2 where Id=#id";
try
{
var p1 = new SqlParameter("#id", id);
var p2 = new SqlParameter("#value", value);
var p3 = new SqlParameter("#value2", value2);
var cmd = new SqlCommand { CommandText = query, CommandType = CommandType.Text };
cmd.Parameters.Add(p1);
cmd.Parameters.Add(p2);
cmd.Parameters.Add(p3);
cmd.Connection = conn;
conn.Open();
bool result = (cmd.ExecuteNonQuery() > 0);
cmd.Dispose();
return result;
}
catch (Exception ex)
{
}
finally
{
conn.Close();
}
}
I already have this code to make stringBuilder for every employee, I get all the employeesId from another table . But if I get more than 1000 employees, I get the error ORA-07195 , I know this error is related to a Maximum of expression in a list . Therefore how can I send every 500 employees to my query in Data Access Objects.
Public List<GraphModel> countRequestCreatedByTypeDefaulPage(int year, int month, String employeeID)
{
int count = 0;
int countEmployeess = 0;
string employeesid = "";
DataView dv = _employeeOverrideBO.getRelatedEmployees(year, month, employeeID);
StringBuilder listEmployees = new StringBuilder();
for (int i = 0; i < countEmployees; i += 500)
{
foreach (DataRowView rowView in dv)
{
DataRow row = rowView.Row;
String employee = row["EMPLOYEE_ID"].ToString();
if (count > 0)
listEmployees.Append(",");
listEmployees.Append("'").Append(employee).Append("'");
count++;
}
}
countEmployeess++;
employeesid = listEmployees.ToString();
return _requestDAO.countRequestCreatedByTypeDefaulPage(employeesid);
Also this is my query in Data Access Object
public List<GraphModel> countRequestCreatedByTypeDefaulPage(string employeesIds)
{
String sql = " select NVL(TO_CHAR(RR.REASON_NM_NEW), 'Total') as SERIES1, count(*) AS VAL" +
" from REQUEST R, REQUEST_PERSON RP, REQUEST_REASON RR " +
" WHERE R.STATUS IN ('CREATED', 'PENDING APPROVAL', 'APPROVED BY MANAGER', 'APPROVED', 'IN PROCESS') " +
" AND R.REQUEST_ID = RP.REQUEST_ID" +
" AND RP.REQUEST_ROLE = 'REQUESTOR' " +
" AND RR.REASON_ID = R.REASON_ID" +
" AND RP.EMPLOYEE_ID IN (" + employeesIds + ") " +
" group by rollup (RR.REASON_NM_NEW) " +
" ORDER BY count(*) DESC";
OracleCommand cmd = new OracleCommand(sql);
try
{
DataTable dataTable = Data_base_Access.executeSQL(cmd, ConfigurationManager.ConnectionStrings["stage"].ToString());
return (GraphModel.convertToList(dataTable));
}
catch (Exception ex)
{
Log.writeError("Request DAO", ex);
throw new DataAccessException("There was an error counting the open requests");
}
}
Also this query get the count to list called GraphModel
public static List<GraphModel> convertToList(System.Data.DataTable dataTable)
{
List<GraphModel> list = new List<GraphModel>();
foreach (DataRow dtRow in dataTable.Rows)
{
list.Add(convertToGraphModel(dtRow));
}
return list;
}
public static GraphModel convertToGraphModel(DataRow dtRow)
{
GraphModel graphModel = new GraphModel();
if (dtRow.Table.Columns.Contains("SERIES1") && dtRow["SERIES1"] != DBNull.Value)
{
graphModel.SERIES1 = Convert.ToString(dtRow["SERIES1"]);
}
if (dtRow.Table.Columns.Contains("SERIES2") && dtRow["SERIES2"] != DBNull.Value)
{
graphModel.SERIES2 = Convert.ToString(dtRow["SERIES2"]);
}
if (dtRow.Table.Columns.Contains("VAL") && dtRow["VAL"] != DBNull.Value)
{
graphModel.VAL = Convert.ToInt32(dtRow["VAL"]);
}
return graphModel;
}
}
I really appreciate your help because I am research a lot and I dont know what can I do
Split the list into 1000 item lists and change the query into this:
" AND (RP.EMPLOYEE_ID IN (" + ids_1_1000 + ") OR RP.EMPLOYEE_ID IN (" + ids_1001_2000 + "))" +
One of the features I love most about Oracle is the Oracle Call Interface (OCI) that lets you access some of the more powerful features or Oracle with programming languages. In particular, for this example, the ability to do bulk inserts should prove very helpful.
If, instead of the approach you have above, which is trying to insert thousands of literals into a single SQL statement, you put those values into a table and do a join, I think you will:
Spare the shared pool from having to compile a really nasty SQL statement
Eliminate the need to escape strings or worry about SQL Injection
Have a lightning fast query that substitutes a join (bread and butter for a database) for a giant in-list
Step 1: Create a GTT:
create global temporary table employee_list (
employee_id varchar2(100) not null
) on commit preserve rows;
The GTT is based on a session, so even if you have this code running in multiple instances, each GTT will act as a blank slate for each instance -- no possibility of collisions of data.
Step 2: Within your code, create a transaction to handle the fact that you need the insert to the table and the select on the data to occur as part of the same transaction:
OracleTransaction trans = conn.BeginTransaction(IsolationLevel.ReadCommitted);
Step 3: Use ODP.net's bulk insert capabilities to insert all of your employee Ids at once. I encourage you to benchmark this versus inserting them one at a time. You'll be amazed. If you have more than 50,000, then maybe you need to break the up into chunks, but with a single field, I think this should be more than adequate:
// string[] employeesIds
OracleCommand cmd = new OracleCommand("insert into employee_list values (:EMPLOYEE)",
conn);
cmd.Transaction = trans;
cmd.Parameters.Add(new OracleParameter("EMPLOYEE", OracleDbType.Varchar2));
cmd.Parameters[0].Value = employeesIds;
cmd.ArrayBindCount = employeesIds.Length;
cmd.ExecuteNonQuery();
Note employeeIds should be an array.
Step 4: Change your SQL from an in-list to a join:
select NVL(TO_CHAR(RR.REASON_NM_NEW), 'Total') as SERIES1, count(*) AS VAL
from
REQUEST R,
REQUEST_PERSON RP,
REQUEST_REASON RR,
employee_list e -- added this
WHERE R.STATUS IN ('CREATED', 'PENDING APPROVAL', 'APPROVED BY MANAGER',
'APPROVED', 'IN PROCESS')
AND R.REQUEST_ID = RP.REQUEST_ID
AND RP.REQUEST_ROLE = 'REQUESTOR'
AND RR.REASON_ID = R.REASON_ID
AND RP.EMPLOYEE_ID = e.employee_id -- changed this
group by rollup (RR.REASON_NM_NEW)
ORDER BY count(*) DESC
And here's what it would all look like together:
public List<GraphModel> countRequestCreatedByTypeDefaulPage(string[] employeesIds)
{
OracleTransaction trans = conn.BeginTransaction(IsolationLevel.ReadCommitted);
OracleCommand cmd = new OracleCommand("insert into employee_list values (:EMPLOYEE)",
conn);
cmd.Transaction = trans;
cmd.Parameters.Add(new OracleParameter("EMPLOYEE", OracleDbType.Varchar2));
cmd.Parameters[0].Value = employeesIds;
cmd.ArrayBindCount = employeesIds.Length;
cmd.ExecuteNonQuery();
String sql = ""; // code from above goes here
cmd = new OracleCommand(sql, conn);
cmd.Transaction = trans;
DataTable dataTable = null;
try
{
dataTable = Data_base_Access.executeSQL(cmd,
ConfigurationManager.ConnectionStrings["stage"].ToString());
return (GraphModel.convertToList(dataTable));
}
catch (Exception ex)
{
Log.writeError("Request DAO", ex);
throw new DataAccessException("There was an error counting the open requests");
}
finally
{
trans.Rollback();
}
return dataTable;
}
I resolved my problem with this code..
public List<GraphModel> countRequestCreatedByTypeDefaulPage(int year, int month, String employeeID)
{
int count = 0;
int countEmployees = 0;
Dictionary<string, int> dataChart = new Dictionary<string, int>();
DataView dv = _employeeOverrideBO.getRelatedEmployeesRequests(year, month, employeeID);
StringBuilder listEmployees = new StringBuilder();
foreach (DataRowView rowView in dv)
{
if (countEmployees == 500)
{
List<GraphModel> listReturn = _requestDAO.countRequestCreatedByTypeDefaulPage(listEmployees.ToString());
foreach(GraphModel model in listReturn){
if (dataChart.ContainsKey(model.SERIES1))
{
dataChart[model.SERIES1] = dataChart[model.SERIES1] + model.VAL;
}
else
{
dataChart[model.SERIES1] = model.VAL;
}
}
listEmployees = new StringBuilder();
count = 0;
countEmployees = 0;
}
DataRow row = rowView.Row;
String employee = row["EMPLOYEE_ID"].ToString();
if (count > 0)
listEmployees.Append(",");
listEmployees.Append("'").Append(employee).Append("'");
count++;
countEmployees++;
}
//Last Call
List<GraphModel> listReturnLast = _requestDAO.countRequestCreatedByTypeDefaulPage(listEmployees.ToString());
foreach (GraphModel model in listReturnLast) {
if (dataChart.ContainsKey(model.SERIES1))
{
dataChart[model.SERIES1] = dataChart[model.SERIES1] + model.VAL;
}
else
{
dataChart[model.SERIES1] = model.VAL;
}
}
List<GraphModel> list = new List<GraphModel>();
foreach (KeyValuePair<string, int> entry in dataChart)
{
GraphModel model = new GraphModel();
model.SERIES1 = entry.Key;
model.VAL = entry.Value;
list.Add(model);
}
return list;
}
First a bit of background:
I developed a method of retrieving rows from a table in SQL in batches or pages, by using IEnumerable and yield. It works great when you need read only access but not so well when you need to do updates to the underlying data as well.
So I wrote a method that takes in a generic DataTable, and builds up an update statement which then gets passed to SQL along with the entire DataTable as a table valued parameter.
The method looks like this:
string[] validColumns = SQL_Columns.Split(',');
foreach(DataColumn column in p_UpdatesTable.Columns)
{
if(!validColumns.Contains(column.ColumnName))
{
throw new Exception("Column '" + column.ColumnName + "' is not valid for this table");
}
}
//Establish SQL Connection
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
StringBuilder commandBuilder = new StringBuilder();
commandBuilder.Append("UPDATE Table SET ");
List<string> columnsToUpdate = new List<string>(p_UpdatesTable.Columns.Count);
foreach(DataColumn column in p_UpdatesTable.Columns)
{
if (!column.ColumnName.Equals("UID", StringComparison.InvariantCultureIgnoreCase))
{
StringBuilder columnBuilder = new StringBuilder();
columnBuilder.Append(column.ColumnName);
columnBuilder.Append(" = U.");
columnBuilder.Append(column.ColumnName);
columnsToUpdate.Add(columnBuilder.ToString());
}
}
commandBuilder.Append(string.Join(",", columnsToUpdate.ToArray()));
commandBuilder.Append(" FROM #UpdateTable AS U WHERE UID = U.UID");
using (SqlCommand sqlCommand = new SqlCommand(commandBuilder.ToString(), sqlConnection))
{
SqlParameter updateTableParameter = sqlCommand.Parameters.Add("UpdateTable", SqlDbType.Structured);
updateTableParameter.Value = p_UpdatesTable;
int rowsAffected = sqlCommand.ExecuteNonQuery();
if(rowsAffected != p_UpdatesTable.Rows.Count)
{
throw new Exception("Update command affected " + rowsAffected + " rows out of the " + p_UpdatesTable.Rows.Count + " expected.");
}
}
sqlConnection.Dispose();
}
I then built this method to populate the update table:
private void AddUpdate(ref DataTable p_UpdateTable, string p_ColumnName, long p_uid, object p_value)
{
if(!StronglyTypedDataset.Columns.Contains(p_ColumnName))
{
throw new ArgumentException("Table '" + p_ColumnName + "' does not exist in table", "p_ColumnName");
}
if(!p_UpdateTable.Columns.Contains(p_ColumnName))
{
DataColumn columnToAdd = p_UpdateTable.Columns.Add(p_ColumnName);
columnToAdd.DataType = StronglyTypedDataset.Columns.Cast<DataColumn>().Where(c => c.ColumnName.Equals(p_ColumnName)).First().DataType;
}
var existingRow = p_UpdateTable.Rows.Cast<DataRow>().Where(r => Convert.ToInt64(r["UID"]) == p_uid).FirstOrDefault();
if(existingRow != null)
{
existingRow[p_ColumnName] = p_value;
}
else
{
DataRow newRow = p_UpdateTable.NewRow();
newRow["UID"] = p_uid;
newRow[p_ColumnName] = p_value;
p_UpdateTable.Rows.Add(newRow);
}
}
There are a few times where I need to call this so this is more of a convenience method than anything else.
Now the problem: there is a possibility where I add a bunch of columns and values for one UID, but for another I might add more columns or not add values for existing columns. The problem with this is the update as it is will obliterate whatever is in the database already with a null value, which I don't want unless I explicitly say "make this null".
I was thinking of getting around this by supplying a value as a default, which I can then check for in my update statement and then using a CASE in the UPDATE statement that checks for this value, and using the original value (so in essence I could just ignore the "U." before the column name). The problem is that the table is generic so there could be anything in there, and in the case of actual data that somehow matches my default value, things would break.
I should note that this update table will be built up into a batch and a batch updated at once, not on a row by row basis.
Is there a value that is guaranteed not to be used, perhaps a GUID (I know there could still be a collision) or something like that?
An example:
Say my table looks like this after one row:
| UID | column 1 | column 2 |
row 1 | 1 | x | y |
On the second row it looks like this:
| UID | column 1 | column 2 | column 3 |
row 1 | 1 | x | y | ? |
row 2 | 2 | x | y | z |
The value for row 1 column 3 never gets set, so it gets defaulted to null. When I use my update statement, SQL will go and set that value to null even if there is something already in the table, but I don't want it to update the field for that row at all since I didn't specify a value for it.
I want to be able to put a value in place of the ? instead of it defaulting to null, so then I can change the update statement to something like UPDATE Table SET Column1 = U.Column1, Column2 = U.Column2, Column3 = CASE WHEN U.Column3 = somevalue THEN Column3 ELSE U.Column3 END FROM #UpdateTable U.
You could make yourself a little Maybe<T> that can either be an actual value for T, in which case you push through the update, or it could be a special not-a-value. It could look something like this:
public sealed class Maybe<T> {
private readonly T value;
private readonly bool hasValue;
private Maybe() {
hasValue = false;
}
public readonly Maybe<T> Nothing = new Maybe();
public Maybe(T value) {
this.value = value;
hasValue = true;
}
public T Value {
get {
return value;
}
}
public bool HasValue {
get {
return value;
}
}
}
Which you could use like this:
private void AddUpdate<T>(DataTable p_UpdateTable, string p_ColumnName, long p_uid, Maybe<T> p_value) {
// ...
if(existingRow != null) {
if(p_value.HasValue)
existingRow[p_ColumnName] = p_value.Value;
}
else {
DataRow newRow = p_UpdateTable.NewRow();
newRow["UID"] = p_uid;
if(p_value.HasValue)
newRow[p_ColumnName] = p_value.Value;
p_UpdateTable.Rows.Add(newRow);
}
// ...
}
You don't need the ref for the DataTable parameter, by the way.
I wound up taking a slightly different route, not using an identifier but using another column entirely.
My AddUpdate looks like this now:
private void AddUpdate(DataTable p_UpdateTable, string p_ColumnName, long p_uid, object p_value)
{
if (!StronglyTypedDataSet.Columns.Contains(p_ColumnName))
{
throw new ArgumentException("Table '" + p_ColumnName + "' does not exist in table", "p_ColumnName");
}
if (!p_UpdateTable.Columns.Contains(p_ColumnName))
{
var matchingColumn = StronglyTypedDataSet.Columns.Cast<DataColumn>().Where(c => c.ColumnName.Equals(p_ColumnName)).First();
DataColumn columnToAdd = p_UpdateTable.Columns.Add(p_ColumnName, matchingColumn.DataType);
columnToAdd.MaxLength = matchingColumn.MaxLength;
DataColumn setNullColumn = p_UpdateTable.Columns.Add(p_ColumnName + "_null", typeof(bool));
setNullColumn.DefaultValue = false;
}
var existingRow = p_UpdateTable.Rows.Cast<DataRow>().Where(r => Convert.ToInt64(r["UID"]) == p_uid).FirstOrDefault();
if (existingRow != null)
{
existingRow[p_ColumnName] = p_value;
if (p_value == null || p_value == DBNull.Value)
{
existingRow[p_ColumnName + "_null"] = true;
}
}
else
{
DataRow newRow = p_UpdateTable.NewRow();
newRow["UID"] = p_uid;
newRow[p_ColumnName] = p_value;
if (p_value == null || p_value == DBNull.Value)
{
newRow[p_ColumnName + "_null"] = true;
}
p_UpdateTable.Rows.Add(newRow);
}
}
This way, if a column gets defaulted to null because it gets added only after some rows get added to the table, I can do a check in my update statement that doesn't update the value.
The update statement looks like this now:
string[] validColumns = SQL_Columns.Split(',');
var trimmed = validColumns.Select(c => c.Trim());
foreach(DataColumn column in p_UpdatesTable.Columns)
{
if(!column.ColumnName.EndsWith("_null") && !trimmed.Contains(column.ColumnName))
{
throw new Exception("Column '" + column.ColumnName + "' is not valid for table");
}
}
string tableTypeName = "dbo.UpdateSpecific" + Guid.NewGuid().ToString().Replace("-", "").Replace("{", "").Replace("}", "");
StringBuilder tableTypeBuilder = new StringBuilder();
tableTypeBuilder.Append("CREATE TYPE ");
tableTypeBuilder.Append(tableTypeName);
tableTypeBuilder.Append(" AS TABLE (");
List<string> tableTypeColumns = new List<string>(p_UpdatesTable.Columns.Count);
StringBuilder commandBuilder = new StringBuilder();
commandBuilder.Append("UPDATE Table SET ");
List<string> columnsToUpdate = new List<string>(p_UpdatesTable.Columns.Count);
foreach (DataColumn column in p_UpdatesTable.Columns)
{
//build command to create table type
StringBuilder columnTypeBuilder = new StringBuilder();
columnTypeBuilder.Append("[");
columnTypeBuilder.Append(column.ColumnName);
columnTypeBuilder.Append("] ");
if(column.DataType == typeof(int))
{
columnTypeBuilder.Append("INT");
}
else if(column.DataType == typeof(long))
{
columnTypeBuilder.Append("BIGINT");
}
else if(column.DataType == typeof(bool))
{
columnTypeBuilder.Append("BIT");
}
else if(column.DataType == typeof(string))
{
columnTypeBuilder.Append("VARCHAR(");
columnTypeBuilder.Append(column.MaxLength);
columnTypeBuilder.Append(")");
}
else if(column.DataType == typeof(byte[]))
{
columnTypeBuilder.Append("IMAGE");
}
tableTypeColumns.Add(columnTypeBuilder.ToString());
//build actual update statement
if (!column.ColumnName.Equals("UID", StringComparison.InvariantCultureIgnoreCase) && !column.ColumnName.EndsWith("_null"))
{
StringBuilder columnBuilder = new StringBuilder();
columnBuilder.Append(column.ColumnName);
columnBuilder.Append(" = (CASE WHEN U.");
columnBuilder.Append(column.ColumnName);
columnBuilder.Append(" IS NULL THEN (CASE WHEN ISNULL(U.");
columnBuilder.Append(column.ColumnName);
columnBuilder.Append("_null, 0) = 1 THEN U.");
columnBuilder.Append(column.ColumnName);
columnBuilder.Append(" ELSE C.");
columnBuilder.Append(column.ColumnName);
columnBuilder.Append(" END) ELSE U.");
columnBuilder.Append(column.ColumnName);
columnBuilder.Append(" END)");
columnsToUpdate.Add(columnBuilder.ToString());
}
}
tableTypeBuilder.Append(string.Join(",", tableTypeColumns.ToArray()));
tableTypeBuilder.Append(")");
commandBuilder.Append(string.Join(",", columnsToUpdate.ToArray()));
commandBuilder.Append(" FROM Table AS C JOIN #UpdateTable AS U ON C.UID = U.UID");
//Establish SQL Connection
using (SqlConnection sqlConnection = new SqlConnection(context.strContext[(int)eCCE_Context._CONNECTION_STRING]))
{
sqlConnection.Open();
try
{
using (SqlCommand createTableTypeCommand = new SqlCommand(tableTypeBuilder.ToString(), sqlConnection))
{
createTableTypeCommand.ExecuteNonQuery();
}
using (SqlCommand sqlCommand = new SqlCommand(commandBuilder.ToString(), sqlConnection))
{
SqlParameter updateTableParameter = sqlCommand.Parameters.Add("#UpdateTable", SqlDbType.Structured);
updateTableParameter.Value = p_UpdatesTable;
updateTableParameter.TypeName = tableTypeName;
int rowsAffected = sqlCommand.ExecuteNonQuery();
if (rowsAffected != p_UpdatesTable.Rows.Count)
{
throw new Exception("Update command affected " + rowsAffected + " rows out of the " + p_UpdatesTable.Rows.Count + " expected.");
}
}
}
finally
{
string dropStatement = "IF EXISTS (SELECT * FROM sys.types st JOIN sys.schemas ss ON st.schema_id = ss.schema_id WHERE st.name = N'"+ tableTypeName.Substring(tableTypeName.IndexOf(".")+1) +"' AND ss.name = N'dbo') DROP TYPE " + tableTypeName;
using (SqlCommand dropTableTypeCommand = new SqlCommand(dropStatement, sqlConnection))
{
dropTableTypeCommand.ExecuteNonQuery();
}
}
sqlConnection.Dispose();
}
Tested and working :)
I am working on importing data from an Excel sheet to database. The Excel sheet contains few empty rows and I want to remove those empty rows, then insert cleared data into database.
I have written a code by referring other code, this is the code for inserting values:
OleDbConnection cnn = new OleDbConnection(#"Provider=Microsoft.Jet.OLEDB.4.0;Data Source='" + txtExcelFile.Text + "';Extended Properties= 'Excel 8.0;HDR=Yes;IMEX=1'");
//DataTable dt = new DataTable();
try
{
cnn.Open();
OleDbDataAdapter data = new OleDbDataAdapter("select * from [Customers$]", cnn);
data.Fill(dsExcel);
dgvCustomers.ColumnHeadersVisible = false;
SqlConnection connection = new SqlConnection("Data Source=COMPUTER-8EB749;Initial Catalog=KITS;Integrated Security=true");
connection.Open();
for (int i = 0; i < dsExcel.Tables[0].Rows.Count; i++)
{
string ID = ds.Tables[0].Rows[i][0].ToString();
Int16 CustID = Convert.ToInt16(ID);
string CustName = dsExcel.Tables[0].Rows[i][1].ToString();
string CardScheme = dsExcel.Tables[0].Rows[i][2].ToString();
string Outlet = dsExcel.Tables[0].Rows[i][3].ToString();
string TerminalNum = dsExcel.Tables[0].Rows[i][4].ToString();
Int32 Terminal = Convert.ToInt32(TerminalNum);
string Date1 = dsExcel.Tables[0].Rows[i][5].ToString();
DateTime Date = Convert.ToDateTime(Date1);
string Time = dsExcel.Tables[0].Rows[i][6].ToString();
DateTime DateTime = Convert.ToDateTime(Time);
string Amount1 = ds.Tables[0].Rows[i][7].ToString();
double Amount = Convert.ToDouble(Amount1);
SqlCommand com = new SqlCommand("insert into Customer(CustID,CustName,CardScheme,Outlet,TerminalNum,TranDate,TranDateTime,Amount) values ('" + CustID + "','" + CustName + "','" + CardScheme + "','" + Outlet + "','" + Terminal + "','" + Date + "','" + DateTime + "','" + Amount + "')", connection);
com.ExecuteNonQuery();
}
connection.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
MessageBox.Show("Data Inserted Successfully.");
}
Can anyone say me how can I remove empty rows so that i can insert only data?!
This will remove all rows that which each of it's columns contain either nothing or white space:
dataTable = dataTable.Rows
.Cast<DataRow>()
.Where(row => !row.ItemArray.All(field => field is DBNull ||
string.IsNullOrWhiteSpace(field as string)))
.CopyToDataTable();
Try this.
public bool InsertRowsToDataBase()
{
try
{
DataTable excelTable = new DataTable();
string connString = #"Provider=Microsoft.Jet.OLEDB.4.0;Data Source='" + txtExcelFile.Text + "';Extended Properties= 'Excel 8.0;HDR=Yes;IMEX=1'";
using (OleDbConnection cnn = new OleDbConnection(connString))
{
string query = "select * from [Customers$]";
using (OleDbDataAdapter data = new OleDbDataAdapter(query, cnn))
{
data.Fill(excelTable);
}
}
dgvCustomers.ColumnHeadersVisible = false;
connString = "Data Source=COMPUTER-8EB749;Initial Catalog=KITS;Integrated Security=true";
using (SqlConnection connection = new SqlConnection(connString))
{
connection.Open();
for (int i = 0; i < excelTable.Rows.Length; i++)
{
//takes from the 3rd row
if (i > 1)
{
DataRow row = excelTable.Rows[i];
object ID = row[0];
if (ID != null && !String.IsNullOrEmpty(ID.ToString().Trim()))
{
Int16 CustID = Convert.ToInt16(ID);
string CustName = row[1].ToString();
string CardScheme = row[2].ToString();
string Outlet = row[3].ToString();
string TerminalNum = row[4].ToString();
Int32 Terminal = Convert.ToInt32(TerminalNum);
string Date1 = row[5].ToString();
DateTime Date = Convert.ToDateTime(Date1);
string Time = row[6].ToString();
DateTime DateTime = Convert.ToDateTime(Time);
string Amount1 = row[7].ToString();
double Amount = Convert.ToDouble(Amount1);
string columnNames = "CustID,CustName,CardScheme,Outlet,TerminalNum,TranDate,TranDateTime,Amount";
string query = String.Format("insert into Customer(0}) values ('{1}', '{2}','{3}','{4}','{5}','{6}','{7}','{8}')",
columnNames, CustID, CustName, CardScheme, Outlet, Terminal, Date, DateTime, Amount);
using (SqlCommand com = new SqlCommand(query, connection))
{
com.ExecuteNonQuery();
}
}
}
//this is your last row. do whatever you want with this
DataRow lastRow = excelTable.Rows[excelTable.Rows.Count - 1];
}
}
return true;
}
catch (Exception exception)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(exception);
return false;
}
}
Please note that I am just checking if ID is null and not inserting any such rows as ID will be the PK in your table.
This will remove all empty rows from the data table:
DataTable dt = dt.Rows
.Cast<DataRow>()
.Where(row => !row.ItemArray.All(f => f is DBNull))
.CopyToDataTable();
OR
DataTable dt = dt.Rows
.Cast<DataRow>()
.Where(row => !row.ItemArray.All(f => f is DBNull ||
string.IsNullOrEmpty(f as string ?? f.ToString())))
.CopyToDataTable();
try
{
OpenOleDBConnection();
OleDbDataAdapter dataAdapter = new OleDbDataAdapter("select * from [" + SelectedSheet + "]", Connection);
dataAdapter.Fill(DataTable);
if ((DataTable != null) && (DataTable.Rows != null) && (DataTable.Rows.Count > 0))
{
List<System.Data.DataRow> removeRowIndex = new List<System.Data.DataRow>();
int RowCounter = 0;
foreach (System.Data.DataRow dRow in DataTable.Rows)
{
for(int index = 0; index < DataTable.Columns.Count; index++)
{
if (dRow[index] == DBNull.Value)
{
removeRowIndex.Add(dRow);
break;
}
else if (string.IsNullOrEmpty(dRow[index].ToString().Trim()))
{
removeRowIndex.Add(dRow);
break;
}
}
RowCounter++;
}
// Remove all blank of in-valid rows
foreach (System.Data.DataRow rowIndex in removeRowIndex)
{
DataTable.Rows.Remove(rowIndex);
}
}
}
catch(Exception e)
{
WPFMessageBox.Show(e.Message, Globalization.GetValue("Import_ImportOption_FormHeader"), WPFMessageBoxButtons.OK, WPFMessageBoxImage.Error);
}
finally
{
CloseOleDBConnection();
}
Here I m also skipping the rows if they have blank entry in any of the row.
I've made this private method that does the trick.
It takes a DataTable as argument and returns the same DataTable without empty rows.
private DataTable StripEmptyRows(DataTable dt)
{
List<int> rowIndexesToBeDeleted = new List<int>();
int indexCount = 0;
foreach(var row in dt.Rows)
{
var r = (DataRow)row;
int emptyCount = 0;
int itemArrayCount = r.ItemArray.Length;
foreach(var i in r.ItemArray) if(string.IsNullOrWhiteSpace (i.ToString())) emptyCount++;
if(emptyCount == itemArrayCount) rowIndexesToBeDeleted.Add(indexCount);
indexCount++;
}
int count = 0;
foreach(var i in rowIndexesToBeDeleted)
{
dt.Rows.RemoveAt(i-count);
count++;
}
return dt;
}
To check Empty Rows
Foreach(DataRow as row in datable.Rows) {
var isEmpty = row.ItemArray.All(c => c is DBNull);
if(!isEmpty) {
//Your Logic
}
}
Why not simply ignore empty rows directly before you are inserting them?
if(string.IsNullOrEmpty(ID + CustName + CardScheme /*.. and so on */))
{
continue;
}
Like this:
for (int i = 0; i < dsExcel.Tables[0].Rows.Count; i++)
{
string ID = ds.Tables[0].Rows[i][0].ToString();
Int16 CustID = Convert.ToInt16(ID);
string CustName = dsExcel.Tables[0].Rows[i][1].ToString();
string CardScheme = dsExcel.Tables[0].Rows[i][2].ToString();
string Outlet = dsExcel.Tables[0].Rows[i][3].ToString();
string TerminalNum = dsExcel.Tables[0].Rows[i][4].ToString();
Int32 Terminal = Convert.ToInt32(TerminalNum);
string Date1 = dsExcel.Tables[0].Rows[i][5].ToString();
DateTime Date = Convert.ToDateTime(Date1);
string Time = dsExcel.Tables[0].Rows[i][6].ToString();
DateTime DateTime = Convert.ToDateTime(Time);
string Amount1 = ds.Tables[0].Rows[i][7].ToString();
double Amount = Convert.ToDouble(Amount1);
/*** Add this if-statement to you code! ***/
if(string.IsNullOrEmpty(ID + CustName + CardScheme + Outlet + TerminalNum + Date1 + Time + Amount1))
{
continue;
}
SqlCommand com = new SqlCommand("insert into Customer(CustID,CustName,CardScheme,Outlet,TerminalNum,TranDate,TranDateTime,Amount) values ('" + CustID + "','" + CustName + "','" + CardScheme + "','" + Outlet + "','" + Terminal + "','" + Date + "','" + DateTime + "','" + Amount + "')", connection);
com.ExecuteNonQuery();
}
I modified Cfrim's answer. You need to check for both empty and whitespace strings. The white space comes from the deleted cells and the empty space comes from deleted data.
private DataTable StripEmptyRows(DataTable dt)
{
List<int> rowIndexesToBeDeleted = new List<int>();
int indexCount = 0;
foreach(var row in dt.Rows)
{
var r = (DataRow)row;
int emptyCount = 0;
int itemArrayCount = r.ItemArray.Length;
foreach (var i in dr.ItemArray)
{
if (string.IsNullOrEmpty(i.ToString()) || string.IsNullOrWhiteSpace(i.ToString()))
emptyCount++;
}
if(emptyCount == itemArrayCount) rowIndexesToBeDeleted.Add(indexCount);
indexCount++;
}
int count = 0;
foreach(var i in rowIndexesToBeDeleted)
{
dt.Rows.RemoveAt(i-count);
count++;
}
return dt;
}
for (int i = dt.Rows.Count - 1; i >= 0; i--) {
if (dt.Rows[i][1] == DBNull.Value) {
dt.Rows[i].Delete();
}
}
dt.AcceptChanges();
return dt;
Your DB itself has empty rows?? Thats quite strange. May be filter it while you do a select query by saying a primary key column is not NULL
public static DataTable RemoveEmptyRows(DataTable dt)
{
List removeRowIndex = new List();
foreach (DataRow dRow in dt.Rows)
{
for (int index = 0; index < dt.Columns.Count; index++)
{
if (string.IsNullOrEmpty(dRow[index].ToString().Trim()))
{
removeRowIndex.Add(dRow);
break;
}
else if (dRow[index] == DBNull.Value)
{
removeRowIndex.Add(dRow);
break;
}
}
}
foreach (DataRow rowIndex in removeRowIndex)
{
dt.Rows.Remove(rowIndex);
}
return dt;
}
this works perfect for me:
dt.Load(cmd.ExecuteReader());
var x = dt.Rows.Cast<DataRow>()
.Where(row => !Array.TrueForAll(row.ItemArray, value =>
{ return value.ToString().Length == 0; }
));
dt = x.CopyToDataTable();
I change a little in #Levitikon post https://stackoverflow.com/a/9233696/5848472
with #shA.t comment , and this code remove all empty Rows and Columns in datatable:
dt = ds.Tables[tablename].Rows
.Cast<DataRow>()
.Where(row => !row.ItemArray.All(field => field is DBNull ||
string.IsNullOrWhiteSpace(field as string ?? field.ToString())))
.CopyToDataTable();
foreach (var column in dt.Columns.Cast<DataColumn>().ToArray())
{
if (dt.AsEnumerable().All(dr => dr.IsNull(column)))
dt.Columns.Remove(column);
}
This worked for me. If we do not check rows and directly do CopyToDataTable() then you may get an exception when the data table has empty rows.
var rows = tbl.Rows.Cast<DataRow>()
.Where(row => !row.ItemArray.All(field => field is DBNull || String.IsNullOrWhiteSpace(field as string ?? field.ToString())));
if (rows.Any())
tbl = rows.CopyToDataTable();
Based on existing answers I use following
public static bool AllColumnsEmpty(this DataRow row)
{
if (row == null)
{
return true;
}
else
{
foreach (var value in row.ItemArray)
{
if (value != null && value.ToString() != "")
{
return false;
}
}
return true;
}
}
public static void RemoveEmptyRows(this DataTable data)
{
var rowsToDelete = data.Rows.Cast<DataRow>()
.Where(row => row.AllColumnsEmpty())
.ToList();
rowsToDelete.ForEach(row => data.Rows.Remove(row));
}
Usage is then
someDatatable.RemoveEmptyRows();
To remove Empty Rows from DataTable:
dt.Rows.Cast<DataRow>().ToList().FindAll(Row =>
{ return String.IsNullOrEmpty(String.Join("", Row.ItemArray)); }).ForEach(Row =>
{ dt.Rows.Remove(Row); });