Combine two data tables with null values on both tables - C# - c#

I have two data tables as shown below.
datatable1:
table1_id(PK) DriverID Vehicle
111 Ram00 VRN01
112 Shyam00 VRN02
113 Ram00 VRN03
datatable2:
table2_id(PK) DriverID exit_time
AA1 Ram00 10.10AM
AA2 Hari00 11.20PM
Combined Output
table1_id DriverID Vehicle table2_id exit_time
111 Ram00 VRN01 AA1 10.10AM
112 Shyam00 VRN02 NULL NULL
113 Ram00 VRN03 AA1 10.10AM
NULL Hari00 NULL AA2 11:20PM
DriverID is common in both table. But just merging two datatable will not give this result.
Please help to achieve this.
datatable1.Merge(datatable2);

You can use Two Data tables to combine into one Data table via Coding and Remove the Extra Column later the For loop ends,Check my code it will work.
string Qry = "select tab1.table_id,'' as DriverID,vehicle,tab1.driver_id Tab1DrvrID,exit_time from tab1 " +
"full join tab2 on tab2.driver_id=tab1.driver_id";
cmd = new SqlCommand(Qry, con);
da = new SqlDataAdapter(cmd);
dt = new DataTable();
da.Fill(dt);
//string DrvrID;
for (int i = 0; i < dt.Rows.Count; i++)
{
string Qry2 = "select tab1.table_id,'' as DriverID,vehicle,tab1.driver_id Tab1DrvrID,tab2.driver_id Tab2DrvrID,exit_time from tab1 " +
"full join tab2 on tab2.driver_id=tab1.driver_id ";
cmd = new SqlCommand(Qry2, con);
SqlDataAdapter daa = new SqlDataAdapter();
DataTable dtt = new DataTable();
daa = new SqlDataAdapter(cmd);
daa.Fill(dtt);
if (dtt.Rows.Count > 0)//
{
string s=dtt.Rows[i]["Tab1DrvrID"].ToString();
if (s=="")
{
dt.Rows[i]["DriverID"] = dtt.Rows[i]["Tab2DrvrID"].ToString();
}
else
{
dt.Rows[i]["DriverID"] = dtt.Rows[i]["Tab1DrvrID"].ToString();
}
}
else
{
}
dt.AcceptChanges();
}
dt.Columns.Remove("Tab1DrvrID");

Merge works properly if columns from two DATATABLES are matched in the same DATATYPE. If the column has a NULL value in the first row of DATATABLE the column DATATYPE will be String. So the second DATATABLE, if have value Date or any other type, will miss the match on merge . To resolve this problem you need to make the two DATATABLES columns same DATATYPE.
private DataTable MergeDataTable(DataTable dataTable1, DataTable dataTable2)
{
var dtCloned = dataTable2.Clone();
foreach (DataColumn column in dataTable2.Columns)
{
var col = dataTable1.Columns[column.ColumnName];
if (col != null && col.DataType != column.DataType )
{
dtCloned.Columns[column.ColumnName].DataType = col.DataType;
}
}
foreach (DataRow d in dataTable2.Rows)
{
dtCloned.ImportRow(d);
}
dataTable1.Merge(dtCloned);
return dataTable1;
}

Related

Linq to datatable with unknown columns

I am having SQL query like this (will be parametrized once I make this work):
using (FbCommand cmd = new FbCommand("SELECT MAGACINID, ROBAID, SUM(KOLICINA) AS GOD" + godina.ToString() + " FROM STAVKA WHERE VRDOK = 13 OR VRDOK = 15 GROUP BY MAGACINID, ROBAID ORDER BY ROBAID", con))
{
using (FbDataAdapter da = new FbDataAdapter(cmd))
{
DataTable tempDT = new DataTable();
if (dt.Rows.Count == 0)
{
da.Fill(dt);
continue;
}
da.Fill(tempDT);
var result = dt.AsEnumerable()
.Join(tempDT.AsEnumerable(),
x => new { field1 = x["MAGACINID"], field2 = x["ROBAID"] },
y => new { field1 = y["MAGACINID"], field2 = y["ROBAID"] },
(x, y) => new {
x,
addYear = y["GOD" + godina.ToString()]
});
dt = LINQResultToDataTable(result);
}
}
And here is LINQResultToDataTable() method
private DataTable LINQResultToDataTable<T>(IEnumerable<T> Linqlist)
{
DataTable dt = new DataTable();
PropertyInfo[] allColumns = null;
if (Linqlist == null) return dt;
foreach (T Record in Linqlist)
{
if (allColumns == null)
{
allColumns = ((Type)Record.GetType()).GetProperties();
// Record at position 0 is whole object and needs to be broke into pieces
// Record at position 1 is new column
foreach (PropertyInfo GetProperty in allColumns)
{
Type colType = GetProperty.PropertyType;
if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition()
== typeof(Nullable<>)))
{
colType = colType.GetGenericArguments()[0];
}
dt.Columns.Add(new DataColumn(GetProperty.Name, colType));
}
}
DataRow dr = dt.NewRow();
foreach (PropertyInfo pinfo in allColumns)
{
dr[pinfo.Name] = pinfo.GetValue(Record, null) == null ? DBNull.Value : pinfo.GetValue
(Record, null);
}
dt.Rows.Add(dr);
}
return dt;
}
First datatable fill is okay and it fills it with these columns:
MagacinID
RobaID
God2021
Now when I try to join new table using linq I should only add God2020 as new column.
Problem is that when I am creating output of linq I cannot tell it to break x object into columns but I need to take it whole so linq result looks like this:
x (this one have ItemArray with all columns)
God2020
And new datatable looks same.
What I tried doing is inside LINQResultToDataTable method to take 0th element of Result and break it into columns, add to datatable, add new one (1st element) and populate it (see comment in that method) but I am stuck and do not know how to do that. Anyone have any solution?
Edit: I made it not clear so here is more explanation.
I have foreach loop which loops through 5 databases and inside that foreach loop is this code.
First database returns these columns
MagacinID
RobaID
God2021
Second
MagacinID
RobaID
God2020
Third
MagacinID
RobaID
God2019
Fourth
MagacinID
RobaID
God2018
Fifth
MagacinID
RobaID
God2017
I want all this merged into one datatable on columns MagacinID AND RobaID like this:
MagacinID
RobaID
God2021
God2020
God2019
God2018
God2017
By Merge, I mean use the DataTable merge, which basically does something like a full outer join on declared PK fields in a datatable, and then makes the merged-into table the result of the join;
var d1 = new DataTable();
d1.Columns.Add("SomeId");
d1.Columns.Add("Col1");
d1.Rows.Add("ID1", "Col1Val1");
d1.Rows.Add("ID2", "Col1Val2");
d1.PrimaryKey = new[] { d1.Columns["SomeId"] };
var d2 = new DataTable();
d2.Columns.Add("SomeId");
d2.Columns.Add("Col2");
d2.Rows.Add("ID1", "Col2Val1");
d2.Rows.Add("ID3", "Col2Val2");
d2.PrimaryKey = new[] { d2.Columns["SomeId"] };
var d3 = new DataTable();
d3.Columns.Add("SomeId");
d3.Columns.Add("Col3");
d3.Rows.Add("ID2", "Col3Val1");
d3.Rows.Add("ID3", "Col3Val2");
d3.PrimaryKey = new[] { d3.Columns["SomeId"] };
d1.Merge(d2, false, MissingSchemaAction.Add);
d1.Merge(d3, false, MissingSchemaAction.Add);
Here's what the above code leads to:
d1 started off with two columns, then during the first Merge it added in a third (Col2), and the values from d2's Col2 were put into d1's Col2, matching on the PK column (SomeId).
Then during the second Merge a fourth column was added to d1 (Col3), and the values out of Col3 in d3 copied into d1.
You'll notice that I deliberately made d2 have different PK values to d1, so not only do new columns get added, but new rows get added too
No new rows were added by this second merge op (d1 already had an ID1, ID2 and ID3)

There is no Row at Position -1

I am receiving this sql error there is no row at position - 1.
This is what I have done.
void showData(int index)
{
Connection con = new OrderManager.Connection();
SqlDataAdapter sda = new SqlDataAdapter("Select * from [MasterDatabase].[dbo].[Neworder] Where OrderID = '" + TxtBox_OrderID.Text + "'", con.ActiveCon());
dt = new DataTable();
sda.Fill(dt);
TxtBox_OrderID.Text = dt.Rows[index][0].ToString();
ClearTextBoxes();
dataGridView1.Rows.Clear();
foreach (DataRow item in dt.Rows)
{
int n = dataGridView1.Rows.Add();
dataGridView1.Rows[n].Cells[0].Value = item["OrderID"].ToString();
dataGridView1.Rows[n].Cells[1].Value = item["Date"].ToString();
dataGridView1.Rows[n].Cells[2].Value = item["Customer_Name"].ToString();
dataGridView1.Rows[n].Cells[3].Value = item["ProductID"].ToString();
dataGridView1.Rows[n].Cells[4].Value = item["Product_Name"].ToString();
dataGridView1.Rows[n].Cells[5].Value = item["Product_Color"].ToString();
dataGridView1.Rows[n].Cells[6].Value = item["Product_PCs"].ToString();
dataGridView1.Rows[n].Cells[7].Value = item["Product_Cutting"].ToString();
dataGridView1.Rows[n].Cells[8].Value = item["Product_TotalYards"].ToString();
}
label12.Text = "Row Count: " + dt.Rows.Count.ToString();
}
I want to display only those records while navigating whose OrderID is equals to the order ID in the database.
I think your error happens on this line
TxtBox_OrderID.Text = dt.Rows[index][0].ToString();
this is not an SQL error but a simple index out of the bounds of the array.
For some reasons, when you try to use a row that is not included in the Rows collection of the datatable you get this error message instead of the less ambiguous IndexOutOfRangeException. This message comes if you pass some value for the index variable that is less than zero or bigger than the number of rows in the datatable dt.
You don't have any check on the number of rows returned by the query and thus is possible that your query doesn't return any record or simple the value of index is -1
void showData(int index)
{
Connection con = new OrderManager.Connection();
SqlDataAdapter sda = new SqlDataAdapter(".......", con.ActiveCon());
dt = new DataTable();
sda.Fill(dt);
// Protect the access to the rows collection of the table...
if(index < dt.RowsCount && index >= 0)
{
TxtBox_OrderID.Text = dt.Rows[index][0].ToString();
// the code that fills the datagrid
}
else
{
// Message for your user about a record not found
}
}
As a side note, please follow ASAP the advice given to parameterize your query. You will avoid Sql Injection and parsin problems

Insert multiple data into two tables and need to insert the last inserted id into another table

I have a form which use two tables to insert the data.
Some column in the form would be like:
scholarship name, course, year
Two tables that are involved are:
scholarshipDetail , scholarshipCourse.
scholarshipDetail table has scholarshipName and year
scholarshipCourse table has scholarshipID, course
scholarshipDetail:
schid schName year
-----------------------------
1 star 2015
2 moon 2016
scholarshipCourse:
schID course
------------------
1 maths
1 english
2 maths
Assuming that the new user wants to add new scholarship which means the id will 3 and it insert into two tables. How do I that? (MANAGED TO INSERT ALR)
NEW ERROR:
EDITED
public DataTable test(string name, string course)
{
string insertsql = "INSERT INTO Table1(schName) OUTPUT INSERTED.addID values (#schName)";
SqlCommand cmd = new SqlCommand(insertsql,conn);
cmd.Parameters.AddWithValue("#schName", name);
conn.Open();
int i = cmd.ExecuteNonQuery();
var table1Id = (int)cmd.ExecuteScalar();
string insertsql1 = "INSERT INTO Table2(ScholarshipID, DiplomaCourse) VALUES (#id, #course)";
SqlCommand cmd2 = new SqlCommand(insertsql1, conn);
cmd2.Parameters.AddWithValue("#id", table1Id);
cmd2.Parameters.AddWithValue("#course", course);
SqlDataAdapter da = new SqlDataAdapter();
da.SelectCommand = cmd;
da.SelectCommand = cmd2;
DataTable dt = new DataTable();
da.Fill(dt);
return dt;
}
The output in my table is
Table1
schID schname
-------------------
1 jj
2 jj
Table2
TableID schID Course
------------------------------
1 2 Maths
the data is being inserted twice in Table1. why is that so? (SOLVED)
Edited:
Now the problem is, there will be checkboxes which allow the user to choose which course is applicable for the scholarship.
When the user click all checkbox, only the last checkbox will insert into database.
In my codebehind(cs):
protected void Button1_Click(object sender, EventArgs e)
{
// addScholarship[] test = new addScholarship[1];
string course = "";
string Name = schName.Text;
if (DIT.Checked )
{
course = "DIT";
}
if (DFI.Checked)
{
course = "DFI";
}
addScholarship[] insertName = new addScholarship[1];
addScholarship insertedName = new addScholarship(Name,course);
scholarshipBLL obj = new scholarshipBLL();
DataTable dt = obj.test(Name, course);
}
For the latest problem you posted.
You are calling obj.test method only once after all the if blocks.
So the "course" variable will have value from the latest if block where the condition is true.
You need to call DataTable dt = obj.test(Name, course); method in every if block. That means if checkbox is checked you call insert row. If not checked then you don't insert the row.
Following is the code you should put in your button_click.
string course = "";
string Name = schName.Text;
scholarshipBLL obj = new scholarshipBLL();
List<addScholarship> addScholarshipList= new List<addScholarship>();
addScholarship scholarship;
if (DIT.Checked )
{
scholarship = new addScholarship(Name,course);
addScholarshipList.Add(insertedName);
course = "DIT";
DataTable dt = obj.test(Name, course);
}
if (DFI.Checked)
{
scholarship = new addScholarship(Name,course);
addScholarshipList.Add(insertedName);
course = "DFI";
DataTable dt = obj.test(Name, course);
}
You are executing the command twice.
int i = cmd.ExecuteNonQuery();
var table1Id = (int)cmd.ExecuteScalar();
You need to execute only one. I think removing cmd.ExecuteNoteQuery would solve your issue.
BEGIN;
INSERT INTO scholarshipDetail(schid,schName,year) VALUES(3,'sun',2017);
INSERT INTO scholarshipCourse(schID,course) VALUES(LAST_INSERT_ID(),'science');
COMMIT;

Dataset to xml Null Values

I have the code below, where from 3 tables I take the data and write an xml.
I want write (when a record column has null value) the column on the xml with null value. For example if (Category_name == Null ) to write on the xml (Null) Right now the code skip the column and don’t even have this column on the xml.
string xmlFileData = "";
string[] tables = new string[] { "category", "company", "config" };
string query;
xmlFileData += "<MyXml>";
SqlConnection conn;
dbconnect obj;
obj = new dbconnect();//initailizing class object
for (int i = 0; i < tables.Length; i++)
{
string ifemptquery;
DataSet ds = new DataSet();
DataSet ds1 = new DataSet();
conn = obj.getConnection(); //calling connection function
ifemptquery = "SELECT * FROM " + tables[i] ";
SqlCommand cmd1 = new SqlCommand(ifemptquery, conn);
conn.Open();
SqlDataAdapter da1 = new SqlDataAdapter(cmd1);
DataTable dt1 = new DataTable();
da1.Fill(dt1);
conn.Close();
if (dt1.Rows.Count > 0)
{
query = "SELECT * FROM " + tables[i] ";
SqlCommand cmd = new SqlCommand(query, conn);
conn.Open();
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(ds);
conn.Close();
conn.Dispose();
ds.DataSetName = tables[i];
string vartbname = tables[i];
string trimed_tbname = vartbname.Replace("_", "");
ds.Tables[0].TableName = trimed_tbname;
xmlFileData += ds.GetXml();
}
else
{
}
}
xmlFileData += "</MyXml>";
File.WriteAllText(Server.MapPath("~/xmlbackup/") + "Backup.xml", xmlFileData);
I have been searching the whole world for a solution of writing null fields to XML using DataSet.WriteXML(). The answer posted by Vlad is the one I also used in my project but I found that following works in a much more performance optimized way. I have created a function for your convenience. Change your dataset tables one after the other by calling the following function and replacing the tables.
private DataTable GetNullFilledDataTableForXML(DataTable dtSource)
{
// Create a target table with same structure as source and fields as strings
// We can change the column datatype as long as there is no data loaded
DataTable dtTarget = dtSource.Clone();
foreach (DataColumn col in dtTarget.Columns)
col.DataType = typeof(string);
// Start importing the source into target by ItemArray copying which
// is found to be reasonably fast for nulk operations. VS 2015 is reporting
// 500-525 milliseconds for loading 100,000 records x 10 columns
// after null conversion in every cell which may be usable in many
// circumstances.
// Machine config: i5 2nd Gen, 8 GB RAM, Windows 7 64bit, VS 2015 Update 1
int colCountInTarget = dtTarget.Columns.Count;
foreach (DataRow sourceRow in dtSource.Rows)
{
// Get a new row loaded with data from source row
DataRow targetRow = dtTarget.NewRow();
targetRow.ItemArray = sourceRow.ItemArray;
// Update DBNull.Values to empty string in the new (target) row
// We can safely assign empty string since the target table columns
// are all of string type
for (int ctr = 0; ctr < colCountInTarget; ctr++)
if (targetRow[ctr] == DBNull.Value)
targetRow[ctr] = String.Empty;
// Now add the null filled row to target datatable
dtTarget.Rows.Add(targetRow);
}
// Return the target datatable
return dtTarget;
}
Refer similar question here - dataSet.GetXml() doesn't return xml for null or blank columns
Apart from solutions mentioned there, you can also traverse through dataset and write XML using XmlTextWriter. This method is not recommended if you are dealing with huge data.

Select some of records from database and display in data grid through c# with sqlite database

I have a sample database like below, and I want to display only the red colored records in the datagrid. I have a condition for how to make those two cells red.
Sample Database
For example, I want to display records whose is value less than 10 in the book number column.
I used code like below for making them red.
Code
private void UpdateDataGridViewColor()
{
if (calledMethod == 2)
{
for (int i = 0; i < dataGridView1.RowCount; i++)
{
int j = 6;
DataGridViewCellStyle CellStyle = new DataGridViewCellStyle();
CellStyle.ForeColor = Color.Red;
if (isLate(dataGridView1[j, i].Value.ToString()))
{
dataGridView1[j, i].Style = CellStyle;
}
}
}
}
I used code something like the following.
Code
private void issueDetails()
{
calledMethod = 2;
string connectionPath = #"Data Source=Data\libraryData.dat;Version=3;New=False;Compress=True";
using (SQLiteConnection connection = new SQLiteConnection(connectionPath))
{
SQLiteCommand command = connection.CreateCommand();
connection.Open();
string query = "SELECT bookno as 'Book No.',studentId as 'Student ID', title as 'Title', author as 'Author', description as 'Description', issuedDate as 'Issued Date', dueDate as 'Due Date' FROM issuedBooks";
command.CommandText = query;
command.ExecuteNonQuery();
SQLiteDataAdapter da = new SQLiteDataAdapter(command);
DataSet ds = new DataSet();
da.Fill(ds, "issuedBooks");
int c = ds.Tables["issuedBooks"].Rows.Count;
dataGridView1.DataSource = ds.Tables["issuedBooks"];
dataGridView1.Sort(dataGridView1.Columns["Student ID"], ListSortDirection.Ascending);
dataGridView1.ReadOnly = true;
connection.Close();
this.Totals.Text = "Total Issued Books : " + Convert.ToString(c);
}
}
Have you tried,
foreach(DataGridView row in dataGridView1.Rows)
{
//check whether bookno. column in 'row' is less than 10
//and do something
}
I just read your comment that you want query from the database directly before displaying in datagridview, is that what you want?
For SQLite databases, check out this link, you can use standard SQL select statements to get the records based on your condition. Example SELECT * From <table> Where bookNum > 10
That should give you all the records with bookNum greater than 10.
First create your connection,
SQLiteConnection connection = new SQLiteConnection(connectionPath)
Then the dataadapter
SQLiteDataAdapter da = new SQLiteDataAdapter(query, connection);
Do your table mappings if any.
And then call da.Fill(ds, "issuedBooks");
I also noticed you were using As in your sql query for the column names. You can actually use tablemappings to map your database column name to the datatable column name. See this link.
Answer
public void onlyDueReport()
{
List<int> array = new List<int>();
string connectionPath = #"Data Source=Data\libraryData.dat;Version=3;New=False;Compress=True";
using (SQLiteConnection connection = new SQLiteConnection(connectionPath))
{
SQLiteCommand command = connection.CreateCommand();
connection.Open();
string query = "SELECT bookno as 'Book No.',studentId as 'Student ID', title as 'Title', author as 'Author', description as 'Description', issuedDate as 'Issued Date', dueDate as 'Due Date' FROM issuedBooks";
command.CommandText = query;
command.ExecuteNonQuery();
SQLiteDataAdapter da = new SQLiteDataAdapter(command);
DataSet ds = new DataSet();
da.Fill(ds, "issuedBooks");
int c = ds.Tables["issuedBooks"].Rows.Count;
if (c > 0)
{
for (int row = c; row > 0; row--)
{
string date = (string)(ds.Tables["issuedBooks"].Rows[c - row]["Due Date"]);
if (isLate(date))
{
int a = Convert.ToInt32(ds.Tables["issuedBooks"].Rows[c - row]["Book No."]);
array.Add(a);
}
}
}
query = "SELECT bookno as 'Book No.',studentId as 'Student ID', title as 'Title', author as 'Author', description as 'Description', issuedDate as 'Issued Date', dueDate as 'Due Date' FROM issuedBooks WHERE bookno IN (";
int[] cool = array.ToArray();
int cou = 0;
foreach (int a in cool)
{
query += a;
if (cou < cool.Length - 1) { query += ','; }
cou++;
}
query += ")";
Console.WriteLine(query);
command.CommandText = query;
command.ExecuteNonQuery();
DataSet ds1 = new DataSet();
da.Fill(ds1, "issuedBooks");
dataGridView1.DataSource = ds1.Tables["issuedBooks"];
this.Totals.Text = "";
Report_Viewer.StatusPText = " Total Pending Books : " + ds1.Tables["issuedBooks"].Rows.Count;
}
}

Categories