Use DataTable Update() Method to Update Multiple SQL Tables? - c#

I'm trying to update my SQL backend with data that has changed in my webform.
That data is initially fetched from SQL like so
string cs = ConfigurationManager.ConnectionStrings["ConnectionString"].ToString();
SqlConnection connection = new SqlConnection(cs);
SqlCommand command = new SqlCommand("my.storedprocedure", cs);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("#customerID", custid));
command.CommandTimeout = 300;
connection.Open();
SqlDataAdapter sda = new SqlDataAdapter(command);
connection.Close();
sda.FillSchema(myDataTable, SchemaType.Source);
sda.Fill(myDataTable);
The stored procedure makes a selection
SELECT c.*
,cs.*
,p.*
,sl.*
,drl.*
FROM tableC as c
INNER JOIN tableCS as cs ON c.[custID] = cs.[custID]
INNER JOIN tableSL as sl ON cs.[custStatusID] = sl.[custStatusID]
LEFT OUTER JOIN tableP as p ON c.custID = p.custID
LEFT OUTER JOIN tableDRL as drl ON cs.custID = drl.custID
WHERE c.[custID] = #custID AND c.[EndDate] IS NULL AND cs.[EndDate] IS NULL
I then manipulate the data table that is filled with this stored procedure, call a dt.NewRow(), add values to that new row, add the row into the data table. Set fields in the previous row, etc.
My question is, when I get ready to call a SqlDataAdapter.Update(myDataTable) will the data adapter go ahead and make the update to all the tables I joined too? Based on what's in my data table?

The short answer is no - a single adapter is for a single table. You need to use a DataSet if you are using ADO.NET.
Using a DataSet:
Populating a DataSet from a DataAdapter
ADO.NET - Updating Multiple DataTables
Fill DataSet with multiple Tables and update them with DataAdapter
Using TableAdapterManager:
TableAdapterManager Overview

Related

Selecting from multiple tables in SQL Server in C#

System.Data.SqlClient.SqlException: 'Ambiguous column name
'SchoolID'.'
I need to select a SchoolName == SchoolID and an AcademicYear from two combo-boxes that are found in two database tables School-Info and School_AcademicYear
Also SchoolID in School_AcademicYear is Foreign Key and its Primary Key in School_Info, I am using inner join to join these two tables but an error is occuring
Ambiguous column name 'SchoolID'
con.Open();
adp = new SqlDataAdapter("SELECT AcademicYearID,AcademicYear,SchoolID FROM School_AcademicYear INNER JOIN School_Info ON School_AcademicYear.AcademicYearID = School_Info.SchoolID where School_AcademicYear.AcademicYearID = '" + AcademicYearID + "'", con);
dt = new DataTable();
adp.Fill(dt);
dataGridViewSchoolNMergeAcYear.DataSource = dt;
con.Close();
If you join two tables that contain columns with the same name and you refer to one of these columns in the SELECT list, then you need to specify from which table are you getting the values. So to solve this problem let's start using some alias for the table names. Using the alias in front of the column's name correctly identify the columns source table.
While at it, I have also changed your string concatenation to a parameterized query. It is a lot better because it avoids parsing errors and a well known security problem called Sql Injection
using(SqlConnection con = new SqlConnection(.......))
{
string cmdText = #"SELECT a.AcademicYearID,a.AcademicYear,i.SchoolID
FROM School_AcademicYear a INNER JOIN School_Info i
ON a.AcademicYearID = i.SchoolID
WHERE a.AcademicYearID = #id";
con.Open();
adp = new SqlDataAdapter(cmdText, con);
adp.SelectCommand.Parameters.Add("#id", SqlDbType.Int).Value = AcademicYearID;
dt = new DataTable();
adp.Fill(dt);
dataGridViewSchoolNMergeAcYear.DataSource = dt;
}
To be complete this answer introduces also the using statement around the disposable connection object. In this way the connection is closed and disposed when the code exits the using block. Note that I suppose that AcademicYearID is a number and not a string so, the parameter is of type SqlDbType.Int instead of NVarChar.
You have multiple columns in those tables with name SchoolID.
You have to specify the column name, because sql cannot know which one you want. Example: School_Info.SchoolID
adp = new SqlDataAdapter(`
SELECT AcademicYearID,AcademicYear,School_Info.SchoolID
FROM School_AcademicYear
INNER JOIN School_Info ON School_AcademicYear.AcademicYearID = School_Info.SchoolID
where School_AcademicYear.AcademicYearID = '` + AcademicYearID + "'", con);

Set what datatable will show to the GridView1 because i'm using sqlCommand LEFT OUTER JOIN

here's my SqlCommand
SqlCommand cmd = new SqlCommand("SELECT a.*,b.ID,b.Firstname,b.Middlename,
b.Lastname,b.Friendswith,b.Amount from [User_TBL_DB] a
LEFT OUTER JOIN [Friends_TBL_DB] b ON a.ID=b.Friendswith", conn);
basically i call 2 datatable and compare them. So no problem with that
now I want to show my data using GridView but what I want is to select [User_TBL_DB] only and not the [Friends_TBL_DB]
Table database.
example:
after I set the SqlCommandI will set the
SqlDataAdapter da = new SqlDataAdapter(cmd);
in here it will select both [User_TBL_DB] and [Friends_TBL_DB] then I will set
da.Fill(ds);
and the next thing that I will do is to call it to the GridView
GridView1.DataSource = ds;
Where will I filter the data ? Is it posible to call one datatable using that SqlCommand? Because I don't want to call it one more time. I want to open my sqlcommand once.
this is the current output of it ^_^
i tried to use the group by but it's not working
If the query is only used for binding GridView, do not select the [Friends_TBL_DB] table's column. Try something like,
SqlCommand cmd = new SqlCommand("SELECT a.* from [User_TBL_DB] a
LEFT OUTER JOIN [Friends_TBL_DB] b ON a.ID = b.Friendswith", conn);
I think you should try something like this
Create procedure dbo.GetSomething
as
begin
SELECT a.*,b.ID,b.Firstname,b.Middlename,
b.Lastname,b.Friendswith,b.Amount from [User_TBL_DB] a
LEFT OUTER JOIN [Friends_TBL_DB] b ON a.ID=b.Friendswith;
select * from [User_TBL_DB];
end;
SqlCommand cmd = new SqlCommand("dbo.GetSomething", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlDataReader rdr = cmd.ExecuteReader();
You will get two dataset.You can assign your second table as your datasource to gridview.
I'm a litle confuse. When you do LEFT OUTER JOIN you aren't comparing them, you are combining them in a cartesian product where a.ID=b.Friendswith and also all the rows in the table [User_TBL_DB] who ID doesn't have a value equal in b.FriendsWith So, if you put "SELECT a.*" you get exactly the same result that select the table [User_TBL_DB] without the LEFT OUTER JOIN
Ok, I got it.
Wath you need to do is add your rows in a for each loop and verify the data of [User_TBL_DB] you are trying to add is not repeat, then use a nested foreach loop to fill a second GridView with the fields from [Friends_TBL_DB] and adding all this to the main GridView like this:
...
da.Fill(ds);
foreach(DataRow r in ds[0].Rows)
{
List<DataRow> l = (from DataRow row in GridView1 where row.Item["Id"] == r.Item["Id"]).toList();
if(l==0)
{
GridView GridView2 = new GridView();
foreach(DataRow r2 in ds[0])
if(r.Item["Id"]==r2.Item["Id"])
GridView2.Rows.Add(r2.Item["bFirstName"], r2.Item["etc..."]);
GridView1.Rows.Add(r.Item["Id"], r.Item["etc..."], GridView2);
}
}

Insert and select within same sp?

Here I came up with situation where I want insert records in temp temple and again want to dispaly that record to user.
I have created one sp in that sp created temp table, added record to that table and select record from temp table.How to show record to user in interface?
ExecuteNOnquery is used for inserting record and ExecuteReader is is used for selecting record.
Withing same sp,I have insert and select.So how to do that in code behind?
You should use ExecuteReader.
All it does - sends string of CommandText to server, executes it, and then builds SqlDataReader to read from.
So if your CommandText is a call to stored procedure - this procedure will be executed (so it will insert your data and select it back) and returned data will be available in SqlDataReader.
See MSDN for reference to ExecuteReader
You could do something like this
using(var con = new SqlConnection(conStr))
{
var cmd = new SqlCommand("MY_SP",con);
cmd.CommandType = CommandType.StoredProcedure;
var da = new SqlDataAdapter(cmd);
var ds = new DataSet();
da.Fill(ds);
if(ds.Tables.Count > 0)
{
foreach(var dr in ds.Tables[0].Rows)
{
// Do stuffs with dr
}
}
}

Giving table friendly name in SQL query using MS SQL server

I am using ADO.NET to execute store procedure. The store procedure is having multiple select statement. When I access DataSet, the tables are named as Table1, Table2 and so on. I want to give user friend name to each of these table. I do not want to use Table variable or temp tables in my SQL query. DO I have any other alternatives?
I am using following code to get the dataset
SqlCommand cmd = new SqlCommand();
SqlDataAdapter da = new SqlDataAdapter();
DataSet ds = new DataSet();
try
{
con.ConnectionString = ConfigurationManager.ConnectionStrings["connString"].ConnectionString;
con.Open();
cmd = new SqlCommand("sp_GetData", con);
cmd.Parameters.Add(new SqlParameter("#ParamOne", param));
cmd.CommandType = CommandType.StoredProcedure;
da.SelectCommand = cmd;
da.Fill(ds);
}
I dont want to do this either
da.TableMappings.Add("Table", "MyTable1");
da.TableMappings.Add("Table1", "MyTable2");
da.TableMappings.Add("Table2", "MyTable3");
or this
ds.Tables[0].TableName = "NametbA";
ds.Tables[1].TableName = "NametbB";
Preferably I want to specify the name in the SQL query. The reason I want to use this approach is because, I will pass this dataset as it to a function which will write the table name into a file.
Please provide your suggestions.
Thank you
It is unfortunately not possible to set it automatically. You will have to provide it to the code somehow.
One option would be to change the structure of your results to have twice as many result sets where the odd one is the name and the even is the data:
-- Table name
SELECT 'nameoftable' AS TableName
-- Data
SELECT * FROM ...
c# code (consider it to be psudo code):
myDataSet.Tables[1].TableName = myDataSet.Tables[0]["TableName"].ToString();
Table names in the ADO.Net dataset object are entirely .Net, C# (or vb.net) specific. They have nothing to do with the table names in the SQL query or in the database. Change them in your C# code, by simply writing
myDataSet.Tables[0].TableName 'WhateverYouWant";

Stored procedure with table type parameter returns data but SqlDataAdapter will not fill

I'm passing a data table to stored procedure (using a table type) to update multiple records at once. SQL profiler shows the stored procedure executing and the records are updated; however, I'm also returning a data set to a SqlDataAdapter. When I execute this in SSMS, I get the results. When I run this in code, rows are not being received.
Any ideas? I'm clueless on why the SqlDataAdapter isn't being filled. Oddly enough, I can get a tables count, but no rows for any tables.
CREATE PROCEDURE [dbo].[SGT_UpdateSGT]
#SGT_GuidelinesTbl As [dbo].[SGT_GuidelinesTbl] Readonly
AS
BEGIN
SET NOCOUNT ON;
update g set Disposition=sgt.Disposition, DispositionDate=GETDATE() from SGTGuidelineRequests g inner join #SGT_GuidelinesTbl sgt on g.pkid=sgt.pkid
select u.ForeName, u.Email, g.*
from #SGT_GuidelinesTbl sgt
inner join SGTGuidelineRequests g on g.pkid=sgt.pkid
inner join Users u on u.NBID=g.SubmittedBy
END
GO
c# code. The session is being filled
DataSet dataSet = new DataSet();
DataTable queue = (DataTable)Session["SGTKeepDeleteQueue"];
using (SqlConnection sqlConnection = new SqlConnection(SQLConn.CWFMO()))
{
sqlConnection.Open();
SqlCommand sqlCommand = new SqlCommand("SGT_UpdateSGT", sqlConnection);
sqlCommand.CommandType = CommandType.StoredProcedure;
SqlParameter guidelinesTblParam = new SqlParameter();
guidelinesTblParam.ParameterName = "#SGT_GuidelinesTbl";
guidelinesTblParam.SqlDbType = SqlDbType.Structured;
guidelinesTblParam.Value = queue;
sqlCommand.Parameters.Add(guidelinesTblParam);
SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(sqlCommand);
sqlDataAdapter.Fill(dataSet);
}
And this is what SQL Profiler receives. When I execute this, I get at least 1 row of data.
declare #p1 dbo.SGT_GuidelinesTbl
insert into #p1 values(1,N'Approved',N'8/23/2012 12:00:00 AM')
exec SGT_UpdateSGT #SGT_GuidelinesTbl=#p1
I changed the name of the SqlDataAdapter and it works. I'm not sure why, I didn't name it that anywhere else in the file. Cleaning the solution didn't solve this either. At least it works.

Categories

Resources