How to fill DataTable from multiple tables? - sql-server

I have written a stored procedure which gives me result from 3 tables separately. Please have a look on the screenshot.
DataTable dataTable = ds.Tables[0]; gives me top result from screenshot, DataTable dataTable = ds.Tables[1]; gives me middle one from screenshot and ds.Tables[2]; gives me last one.
Now, I want to get all the table information. Please have a look on my code. I am not getting the result.
System.ArgumentException: 'Column 'race_header' does not belong to
table Table2.'
Please provide the solutions for this.
public List<ClsStakesRaces> RaceNameDetails(int? Id)
{
List<ClsStakesRaces> clsStakes = new List<ClsStakesRaces>();
SqlParameter[] prms = new SqlParameter[1];
string sSQL;
sSQL = "exec StakesRacesGetRaceDetails #Id";
prms[0] = new SqlParameter("#Id", SqlDbType.Int);
prms[0].Value = Id;
ds = clsUtilities.CreateCommandwithParams(sSQL, prms);
DataTable dataTable = new DataTable();
dataTable = ds.Tables[0];
dataTable= ds.Tables[1];
dataTable = ds.Tables[2];
foreach (DataRow dr in dataTable.Rows)
{
clsStakes.Add(
new ClsStakesRaces
{
RaceName = Convert.ToString(dr["race_header"]),
HorseId = Convert.ToInt32(dr["HorseID"]),
Horse = Convert.ToString(dr["Horse"]),
Details = Convert.ToString(dr["Details"]),
Breeding = Convert.ToString(dr["Breeding"]),
Earning = Convert.ToDecimal(dr["Earning"]),
PreviousWinner = Convert.ToString(dr["PreviousWinners"])
});
}
return clsStakes;
}

Looking at your code:
DataTable dataTable = new DataTable();
dataTable = ds.Tables[0]; // actually, forget the new DataTable() bit, changed my mind, point to ds.Tables[0] instead
dataTable= ds.Tables[1]; // actually, forget ds.Tables[0], point to ds.Tables[1] instead
dataTable = ds.Tables[2]; // actually, forget ds.Tables[1], point to ds.Tables[2] instead
So then
foreach (DataRow dr in dataTable.Rows)
is actually
foreach (DataRow dr in ds.Tables[2].Rows)
Your third table (ds.Tables[2]) doesn't have a column called race_header, hence your error.
You probably want to return a single statement based upon a JOIN condition in the SQL stored procedure. If you can't do that for some reason, you will need to iterate each of ds.Tables separately, stitching the objects together in memory.

Related

ds.Tables["TableName"] threw an exception "System.Data.DataTableCollection.this[string].get returned null."

I'm working on a Exporting DataGridView to CSV File and I have this problem:
When i try with table name ( ds.Tables["tableName"] ) it threw an exception "System.NullReferenceException: 'Object reference not set to an instance of an object.'
System.Data.DataTableCollection.this[string].get returned null.
"
but it is working with the index of table: ds.Tables[0].
In my case I have only the names of the table.
I've tried with ds.Tables["dbo.tableName"] also ds.Tables[(row.Cells["table_name"].Value.ToString())]
this is my code:
{
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
foreach (DataGridViewRow row in TableNamesGrid.Rows)
{
bool isSelected = Convert.ToBoolean(row.Cells["CheckBox"].Value);
if (isSelected)
{
StringBuilder stringBuilder = new StringBuilder();
SqlDataAdapter sqlData = new SqlDataAdapter("SELECT * FROM " + row.Cells[1].Value, sqlConnection);
DataSet ds = new DataSet();
sqlData.Fill(ds);
foreach (DataRow dataRow in ds.Tables[(row.Cells["table_name"].Value.ToString())].Rows)
{
for (int i = 0; i <= ds.Tables[(row.Cells["table_name"].Value.ToString())].Rows.Count; i++)
{
stringBuilder.Append(dataRow[i].ToString() + ",");
}
stringBuilder.Append("\r\n");
}
StreamWriter file = new StreamWriter(#"D:\Projects\AlatiWF\data.csv");
file.WriteLine(stringBuilder.ToString());
file.Close();
}
}
}
}
The SqlDataAdapter is not smart enough to work out that your query is just accessing a single table, so it will always return tables named like this: "Table0", "Table1", "Table2", etc.
Table[0] works because it's accessing the table called "Table0". You can give the tables sensible names after your call to sqlData.Fill(ds) by doing something like:
ds.Tables[0].TableName = "SensibleTableName";
Directly fill the Datatable and without Dataset.
DataTable dt = new DataTable();
sqlData.Fill(dt);
foreach (DataRow dataRow in dt.Rows)
{

Copying Rows from One DataTable to another Displays Nothing

I'm trying to copy rows from one DataTable to another, but only copy rows where the data is yet to be saved to the database table.
The SQL side of this is working fine, it's returning the correct number of columns, however, when I add the rows that were found to my second DataTable and set it as the DataSource for my grid, there is no data displayed, although there is a row that has been added, since the row selector is visible.
What am I doing wrong, and why isn't the data being copied with it?
lftable = New DataTable
Try
For Each dc As DataColumn In lineTable.Columns
lftable.Columns.Add()
Next
Dim ds As New DataSet
For Each row As DataRow In lineTable.Rows
Dim da As New OleDbDataAdapter("SELECT * FROM [Order_Freight] WHERE [Order_Number] = ? AND [Product_Code] <> ?", con)
da.SelectCommand.Parameters.Add("#num", OleDbType.Integer).Value = orderNum
da.SelectCommand.Parameters.Add("#prod", OleDbType.VarChar).Value = row.Item("Product_Code")
da.Fill(ds)
For Each dr As DataRow In ds.Tables(0).Rows
Dim nRow = lftable.Rows.Add()
nRow.ItemArray = dr.ItemArray()
Next
Next
ugProducts.DataSource = lfTable
Screenshot of the grid after assigning it the DataSource
Fill the columns correctly:
For Each dc As DataColumn In lineTable.Columns
lftable.Columns.Add(new DataColumn(dc.ColumnName, dc.DataType));
Next
You can use the overload of the Row.Add function which allows you provide the ItemArray directly:
For Each dr As DataRow In ds.Tables(0).Rows
lftable.Rows.Add(dr.ItemArray)
'nRow.ItemArray = dr.ItemArray() <-- remove
Next
Instead of this code :
' For Each dc As DataColumn In lineTable.Columns
' lftable.Columns.Add() >
' lftable.Columns.Add(dc)
' Next
Use DataTable.Clone() to copy only the columns in a lineTable to lftable
lftable = lineTable.Clone();
And then
For Each dr As DataRow In ds.Tables(0).Rows
lftable.Rows.Add( dr.ItemArray)
Next

How to increase query performance for filling a combobox with 7500 rows in DevExpress?

I have a DevEx GridView and inside that I have GridViewDataComboBoxColumn. I need to fill the Combo-box with 7500 rows. I have used the following query in stored procedure sp_select_AllStocks
Select StockId,StockName from StockMaster;
The below code is used in aspx.cs page to dynamically bind combobox.
DataTable dt_fill_StocksCombo = new DataTable();
dt_fill_StocksCombo = con.select_AllStocks();
((GridViewDataComboBoxColumn)gdStocks.Columns["StockName"]).PropertiesComboBox.IncrementalFilteringMode = DevExpress.Web.ASPxEditors.IncrementalFilteringMode.StartsWith;
((GridViewDataComboBoxColumn)gdStocks.Columns["StockName"]).PropertiesComboBox.DropDownStyle = DevExpress.Web.ASPxEditors.DropDownStyle.DropDown;
((GridViewDataComboBoxColumn)gdStocks.Columns["StockName"]).PropertiesComboBox.DataSource = dt_fill_StocksCombo;
((GridViewDataComboBoxColumn)gdStocks.Columns["StockName"]).PropertiesComboBox.ValueField = "StockId";
((GridViewDataComboBoxColumn)gdStocks.Columns["StockName"]).PropertiesComboBox.TextField = "StockName";
The function definition of select_AllStocks() is as follows.
public DataTable select_AllStocks()
{
Open_Connection();
DataTable dt = new DataTable();
cmd = new SqlCommand("sp_select_AllStocks", con);
cmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter adt = new SqlDataAdapter(cmd);
adt.Fill(dt);
Close_Connection();
return dt;
}
The above method takes approximately 30 seconds to load combobox with 7500 rows.
Is there a way out to increase the performance?
ComboBox supports fetching records from a data base on demand. The ASPxComboBox.ItemsRequestedByFilterCondition and ASPxComboBox.ItemRequestedByValue events are used for this. See the Filtering a Large Data Source demo. Here is a solution for how to use this feature in the Grid: ASPxGridView - Filtering a Large Data Source in a combobox
Public Sub fillCombo(ByVal cbobox As LookUpEdit)
With cbobox
cbobox.Properties.Columns.Clear()
cbobox.Properties.Columns.Add(New LookUpColumnInfo("Code", 10))
cbobox.Properties.Columns.Add(New LookUpColumnInfo("Name", 20))
.Properties.Columns.Item(0).Visible = False
ds = INVBl.FillCustomer()
.Properties.DataSource = ds.Tables(0)
.Properties.DisplayMember = "Name"
.Properties.ValueMember = "Code"
.Properties.ShowFooter = False
.Properties.ShowHeader = False
.Properties.ShowLines = False
End With
End Sub
Public Sub RfillCombo(ByVal cbobox As Repository.RepositoryItemLookUpEdit, ByVal column As String)
With cbobox
cbobox.Properties.Columns.Clear()
cbobox.Properties.Columns.Add(New LookUpColumnInfo("Code", 10))
cbobox.Properties.Columns.Add(New LookUpColumnInfo("Name", 20))
.Properties.Columns.Item(0).Visible = False
ds = INVBl.FillItem(column)
.Properties.DataSource = ds.Tables(0)
.Properties.DisplayMember = "Name"
.Properties.ValueMember = "Code"
.Properties.ShowFooter = False
.Properties.ShowHeader = False
.Properties.ShowLines = False
End With
End Sub

Save data of 2 SqlDataAdapters Sequentially

I am working on some project ( VB.Net & SQL Server ) where I have
Patient & Appointment scenario.
These are the public declarations:
Dim DS As New DataSet
Dim SqlAdap, SqlAdapAppointment As SqlDataAdapter
Dim BindSrc, BindSrcAppointment As New BindingSource
and I set the binding in the Load event:
SqlAdap = New SqlDataAdapter(String.Format("select * from {0}",Patient),SqlConMain)
SqlAdap.Fill(DS,"Patient")
Dim SqlCmd As New SqlCommandBuilder(SqlAdap)
SqlAdap.InsertCommand = SqlCmd.GetInsertCommand
SqlAdap.DeleteCommand = SqlCmd.GetDeleteCommand
SqlAdap.UpdateCommand = SqlCmd.GetUpdateCommand
BindSrc.DataSource = DS
BindSrc.DataMember = "Patient"
BindNavMain.BindingSource = BindSrc
SqlAdapAppointment = New SqlDataAdapter("select * from Appointment", SqlConMain)
SqlAdapAppointment.Fill(DS, "Appointment")
Dim SqlCmdAppointment As New SqlCommandBuilder(SqlAdapAppointment)
SqlAdapAppointment.InsertCommand = SqlCmdAppointment.GetInsertCommand
SqlAdapAppointment.DeleteCommand = SqlCmdAppointment.GetDeleteCommand
SqlAdapAppointment.UpdateCommand = SqlCmdAppointment.GetUpdateCommand
Dim Rel As New DataRelation("FK_Patient_Appointment",
DS.Tables("Patient").Columns("PatientId"),
DS.Tables("Appointment").Columns("PatientId"), False)
Rel.Nested = False
DS.Relations.Add(Rel)
BindSrcAppointment.DataSource = BindSrc
BindSrcAppointment.DataMember = "FK_Patient_Appointment"
BindNavAppointment.BindingSource = BindSrcAppointment
everything works well ..
but if I added new patient using its BindingNavigator, then added an appointment for him using the appointment's BindingNavigator
then I tried to save all of it, it will save the patient data successfully but will throw an error after, because it couldn't save the appointment data that its related to the new patient ID
this is my save code:
BindSrcAppointment.EndEdit()
BindSrc.EndEdit()
Dim TblPatient As DataTable = DS.Tables("Patient").GetChanges()
If TblPatient IsNot Nothing Then
SqlAdap.Update(TblPatient)
DS.Tables("Patient").AcceptChanges()
End If
Dim TblAppointment As DataTable = DS.Tables("Appointment").GetChanges()
If TblAppointment IsNot Nothing Then
SqlAdapAppointment.Update(TblAppointment)
DS.Tables("Appointment").AcceptChanges()
End If
It saves successfully of course if I tried to add appointment to some patient who has data already been saved to the DB
but I want to know if there a way to save data of 2 Adapters at once sequentially?
I use SqlDataAdapter.RowUpdated event to 'catch' the identity of the most recently added record, but it seems that there are a lot of different possibilities:
http://www.mikesdotnetting.com/article/54/getting-the-identity-of-the-most-recently-added-record
So you have to update Patient table, retrieve the most recently added record's autoincrement identity value and then update Appointment table.

How to get identities of inserted data records using SQL bulk copy

I have an ADO.NET DataTable with about 100,000 records. In this table there is a column xyID which has no values in it, because the column is an auto-generated IDENTITY in my SQL Server database.
I need to retrieve the generated IDs for other processes. I am looking for a way to bulk copy this DataTable into the SQL Server database, and within the same "step" to "fill" my DataTable with the generated IDs.
How can I retrieve the identity values of records inserted into a table using the SqlBulkCopy class?
I'm currently doing something like this:
DataTable objects = new DataTable();
DataColumn keyColumn = new DataColumn("name", typeof(string));
DataColumn versionColumn = new DataColumn("version", typeof(int));
versionColumn.DefaultValue = iVersionID;
objects.Columns.Add(keyColumn);
objects.Columns.Add(versionColumn);
foreach (KeyValuePair<string, NamedObject> kvp in Directory)
{
NamedObject o = kvp.Value;
DataRow row = objects.NewRow();
row[0] = o.Name;
objects.Rows.Add(row);
}
using (SqlBulkCopy updater = new SqlBulkCopy(conn,
SqlBulkCopyOptions.TableLock | SqlBulkCopyOptions.UseInternalTransaction, null))
{
updater.DestinationTableName = "object_table";
updater.WriteToServer(objects);
}
string sQuery = #"SELECT id, name FROM object_table WHERE version = #ver";
using (SqlCommand command = new SqlCommand(sQuery, conn))
{
SqlParameter version = new SqlParameter("#ver", SqlDbType.Int, 4);
version.Value = versionID;
command.Parameters.Add(version);
command.CommandTimeout = 600;
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
string key = (string)reader[1];
NamedObject item = Directory[key];
item.ID = (int)reader[0];
}
}
}
Note that our data design enables filtering for all our new objects by using the version ID; every row we're adding will have the same version id, and we've previously removed any rows in the database that already had this version id.
However, my select query is currently timing out in ExecuteReader, even with that 10-minute window. So this is not our final solution.

Resources