C# + Reading output result from stored procedure + merge into + OUTPUT - c#

I am trying to upload an Excel file in C# to SQL Server database table.
The table looks like this:
Companies
ID(PK) Int AutoIncrement
CompanyName Varchar(256)
Logo Varchar(256)
WebsiteURL Varchar(256)
Description Varchar(256)
I have read the Excel into a DataTable object and passed it to a stored procedure. The stored procedure uses MERGE INTO to insert new records and update existing.
I need to know how many records are inserted and how many are updated.
For this, I used OUTPUT like this:
CREATE PROCEDURE [dbo].[Update_Companies]
#tblCompanies CompanyType READONLY
AS
BEGIN
SET NOCOUNT ON;
DECLARE #summary CompanySummaryType;
MERGE INTO companies c1
USING #tblCompanies c2 ON c1.CompanyName = c2.CompanyName
WHEN MATCHED THEN
UPDATE
SET c1.Logo = c2.Logo,
c1.WebsiteURL = c2.WebsiteURL,
c1.CompanyDescription = c2.CompanyDescription,
WHEN NOT MATCHED THEN
INSERT (CompanyName, Logo, WebsiteURL, Description)
VALUES (c2.CompanyName, c2.Logo, c2.WebsiteURL, c2.Description)
OUTPUT
$action as ActionType,
INSERTED.CompanyName as CompanyName INTO #summary;
END
CompanyType is a user-defined table type containing table columns
CompanySummaryType is a user-defined table type containing two columns:
ActionType Varchar(256),
CompanyName Varchar(256)
The code runs fine and insert or update working perfectly.
I want to read the #summary variable back in my C# code.
Right now, I am using ExecuteNonQuery to execute stored procedure like this:
private void AddRecords(DataTable dataTable)
{
string constr = ConfigurationManager.ConnectionStrings["CMSConnectionString"].ConnectionString;
using (SqlConnection con = new SqlConnection(constr))
{
using (SqlCommand cmd = new SqlCommand("Update_Companies"))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = con;
cmd.Parameters.AddWithValue("#tblCompanies", dataTable);
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
}
}
Also, is there a way by which I can find how many records failed to insert or update?

You can make your #Summary variable an OUTPUT parameter of the procedure.
In your C# code, you can then compare the returned OUTPUT parameter, with the original input parameter, to see which rows were not inserted or updated.

Related

Syntax error with creating a stored procedure in SQL Server Management Studio

Currently, I am working on an inventory system that will allow the client to customize their own SQL Server tables.
The user will name their column then specify the data type they would like to use. After they are satisfied with their table they click on the submit table button to create their table. C# then will create a unique table for the user by running a SQL Server stored procedure.
SqlConnection connection = new SqlConnection(Constants.conn);
connection.Open();
string query = "EXEC CreateTable #TableName='"+ UserAccountInfo.Username + "InventoryTable'";
SqlCommand command = new SqlCommand(query, connection);
command.ExecuteReader();
Then using a foreach loop, iterating through each column added (class UserTable is my custom WPF UserControl) by looking at the Stack panel "StackTable" Children, and assigning the properties of the Usertable "Name" and "DataType" to the List of String Arrays. Then I iterate throughout the list and execute the second stored procedure that alters the table previously created.
I believe all of this to work!! My question and problem lies in my stored procedure.
List<string[]> list = new List<string[]>();
foreach (UserTable ut in StackTable.Children)
{
string dataType = "";
switch (ut.DataType.Text)
{
case "Text": dataType = "Varchar(100)"; break;
case "Number": dataType = "INT"; break;
case "Boolean": dataType = "Varchar(5)"; break;
}
string[] vs = {"[" + ut.Name.Text + "]", dataType};
list.Add(vs);
}
foreach (string[] s in list)
{
string query2 = "EXEC SelectAllCustomers #Name = '" + s[0] + "', #DataType = '" + s[1] + "';";
SqlCommand command2 = new SqlCommand(query, connection);
command2.ExecuteReader();
}
My DATABASE Schema consists of the base database called InventoryDatabase, inside a table called LoginInfo where I store the user's information, and the rest of the tables are going to be the tables we are creating above. PROBLEM IS, my stored procedures are throwing syntax errors.
You'll have to use dynamic SQL. Here's an example to get you started:
create or alter procedure CreateTable #TableName varchar(100)
as
begin
declare #sql nvarchar(max) =
concat('create table ',quotename(#TableName),' AccountId int primary key identity(1,1)');
exec (#sql);
end
Note that the name is passed through the quotename function to protect against SQL Injection.
Option 1: prepare the SQL script as a string which can able to Create or alter the table in your C# code and then execute prepared string using ExecuteNonQuery method.
Option 2: by using dynamic SQL concept, prepare the create/alter table script with in a stored procedure and then execute

Inserting and updating records to database at the same time [duplicate]

I'm looking to do something simulair toward here: How do I insert multiple rows WITHOUT repeating the "INSERT INTO dbo.Blah" part of the statement?
except that in addition towards doing this in one query (faster then several dozen) I also want to do this parameterized as the input comes from the web.
Currently I have
foreach(string data in Scraper){
SqlConnection conn = new SqlConnection(WebConfigurationManager.AppSettings["ConnectionInfo"].ToString());
string query = "INSERT INTO DATABASE('web',#data)";
SqlCommand sqlCommand= new SqlCommand(query, conn);
sqlCommand.Parameters.AddWithValue("#data", data);
Command.executeNonQuery();
conn.close();
}
Which is a bit slugish (note the real example has a lot more colums but that would make things more confusing).
Since you are using c# and sql server 2008, you can use a table valued parameter to insert multiple rows to your database.
Here is a short description on how to do this:
First, you need to create a user defined table type:
CREATE TYPE MyTableType AS TABLE
(
Col1 int,
Col2 varchar(20)
)
GO
Then, you need to create a stored procedure that will accept this table type as a parameter
CREATE PROCEDURE MyProcedure
(
#MyTable dbo.MyTableType READONLY -- NOTE: table valued parameters must be Readonly!
)
AS
INSERT INTO MyTable (Col1, Col2)
SELECT Col1, Col2
FROM #MyTable
GO
Finally, execute this stored procedure from your c# code:
DataTable dt = new DataTable();
dt.Columns.Add("Col1", typeof(int));
dt.Columns.Add("Col2", typeof(string));
// Fill your data table here
using (var con = new SqlConnection("ConnectionString"))
{
using(var cmd = new SqlCommand("MyProcedure", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#MyTable", SqlDbType.Structured).Value = dt;
con.Open();
cmd.ExecuteNonQuery();
}
}
You can make use of the SQL syntax:
INSERT INTO YOUR_TABLE (dataColumn) VALUES (data1),(data2),(data3)
So loop over your rows you wanna insert and append ",(datax)" to your query and also add the corresponding parameter.
Perhaps it helps.

MS Access - C# - Retrieve the latest inserted guid

Is there a way to retrieve the latest inserted guid in access with C#?
I tried this: Created a table Cars with a field Id of type autonumber, replicationID and a field Name varchar(250).
var command = myConnection.CreateCommand();
command.Connection.Open();
command.CommandText = "INSERT INTO Cars(Name) VALUES ('Pagani')";
command.ExecuteNonQuery();
command = context.Database.Connection.CreateCommand();
command.CommandText = "SELECT ##Identity";
Console.WriteLine(command.ExecuteScalar());
command.Connection.Close();
The issue which I am getting is:
Console.WriteLine(command.ExecuteScalar());
always shows 0
EDIT
To create the table you can use this statement over the C# OleDb connection (I think that from MS Access query does not work)
CREATE TABLE [Cars] (
[Id] guid not null DEFAULT GenGUID(),
[Name] text null
);
ALTER TABLE [Cars] ADD CONSTRAINT [PK_Cars_6515ede4] PRIMARY KEY ([Id])
I know this is not exactly what you are asking for, but let me suggest an alternative solution which might solve your underlying problem.
Create the GUID in C# and pass it to your insert:
var newGuid = Guid.NewGuid();
var command = myConnection.CreateCommand();
command.Connection.Open();
command.CommandText = "INSERT INTO Cars(Id, Name) VALUES (?, 'Pagani')";
command.Parameters.AddWithValue("#Id", newGuid); // Note: OleDb ignores the parameter name.
command.ExecuteNonQuery();
Console.WriteLine(newGuid);
GUIDs are unique. It really doesn't matter whether it is generated by your application or by the Access database driver.
This option is in all respects superior to reading the GUID afterwards:
You only need one database access.
It's less code.
It's easier.
And you can still omit the GUID in your INSERT in cases where you don't need to know the GUID - no need to change existing code.
If SELECT ##IDENTITY does not work for "ReplicationID" AutoNumber fields then the most likely way to retrieve such a value for a new record is to use an Access DAO Recordset insert, like this:
// required COM reference:
// Microsoft Office 14.0 Access Database Engine Object Library
var dbe = new Microsoft.Office.Interop.Access.Dao.DBEngine();
Microsoft.Office.Interop.Access.Dao.Database db = dbe.OpenDatabase(
#"C:\Users\Public\Database1.accdb");
Microsoft.Office.Interop.Access.Dao.Recordset rst = db.OpenRecordset(
"SELECT [Id], [Name] FROM [Cars] WHERE FALSE",
Microsoft.Office.Interop.Access.Dao.RecordsetTypeEnum.dbOpenDynaset);
rst.AddNew();
// new records are immediately assigned an AutoNumber value ...
string newReplId = rst.Fields["Id"].Value; // ... so retrieve it
// the returned string is of the form
// {guid {1D741E80-6847-4CB2-9D96-35F460AEFB19}}
// so remove the leading and trailing decorators
newReplId = newReplId.Substring(7, newReplId.Length - 9);
// add other field values as needed
rst.Fields["Name"].Value = "Pagani";
// commit the new record
rst.Update();
db.Close();
Console.WriteLine("New record added with [Id] = {0}", newReplId);
which produces
New record added with [Id] = 1D741E80-6847-4CB2-9D96-35F460AEFB19
You can try like this using the OUTPUT :
INSERT INTO myTable(myGUID)
OUTPUT INSERTED.myGUID
VALUES(GenGUID())
You can try like this:
string str1 = "INSERT INTO Cars(Name) VALUES ('Pagani')";
string str2 = "Select ##Identity";
int ID;
using (OleDbConnection conn = new OleDbConnection(connect))
{
using (OleDbCommand cmd = new OleDbCommand(str1, conn))
{
conn.Open();
cmd.ExecuteNonQuery();
cmd.CommandText = str2;
ID = (int)cmd.ExecuteScalar();
}
}

Displaying data from SQL database in C# WinForm controls (textBox, comboBox, label)

I have a performance problem with displaying data from an SQL database in my application. The problem is that I have a large number of parameters that I need to display (customers personal data, his current statistics etc.).
So far I've used either SqlCommand.ExecuteScalar (for single parameters), or DataTable.Rows[].ItemArray.GetValue() (for multiple parameters - I fill the DataTable with SqlDataAdapter whose query withdraws the necessary data from the database) and assigned their values to the appropriate control. Assuming that command is an SqlCommand type:
For single parameter
command.CommandText = "SELECT Parameter1 FROM MyTable WHERE Condition = Value";
textBox1.Text = command.ExecuteScalar().ToString();
For multiple parameters (SDA is a SqlDataAdapter):
command.CommandText="SELECT Parameter1 - ParameterN FROM MyTable WHERE Condition = Value";
SDA.SelectCommand = command;
SDA.Fill(MyDataTable);
textBox1.Text = MyDataTable.Rows[0].ItemArray.GetValue(0).ToString();
comboBox1.Text = MyDataTable.Rows[0].ItemArray.GetValue(1).ToString();
/*
I repeat similar lines of code for each parameter and display it in the appropriate control.
*/
This approach works correctly but when I have a large number of parameters (20+), it works very slowly.
Is there a more efficient way to display these amounts of data, and how would I implement it?
Thank you
Probably, with the second example, a SqlDataReader will perform better because you read the values just one time, while with a DataAdapter, you need to load the DataTable and then loop over the rows of the table (Effectively reading data two times).
command.CommandText="SELECT Field1,...,FieldN FROM MyTable WHERE Condition = Value";
SqlDataReader reader = command.ExecuteReader();
while(reader.Read())
{
// Of course this works correctly just if your query returns one row....
textBox1.Text = reader.GetString(0);
comboBox1.Text = reader.GetString(n);
}
You could also try with the Field<T> extension for the DataRow
command.CommandText="SELECT Field1,...,FieldN FROM MyTable WHERE Condition = Value";
SqlDataAdapter SDA = new SqlDataAdapter(command);
SDA.Fill(MyDataTable);
textBox1.Text = MyDataTable.Rows[0].Field<string>("Field1");
comboBox1.Text = MyDataTable.Rows[0].Field<string>("FieldN");
However, I think that the real performance gain would be in the query that you submit to the database engine and in the correct working of indexes on your tables.
Try to retrieve the minimun number of rows possible, search on indexed fields and/or change to a stored procedure.
here i had write sample stored procedure in wich you can get idea...
you can pass as amny parameter as you can in xml format and insert into temp table...
now you have table with value Name/value pair means Paramater name /value....
now you can do your furteher work...
/*
EXEC wa_TempGetDaya '<SampleXML>
<tblXML><AccountID>3</AccountID><Code>11</Code><Description>Leptospiral infect NEC</Description></tblXML>
</SampleXML>'
*/
CREATE PROCEDURE wa_TempGetDaya
(
#ParaXML NVARCHAR(MAX)
)
AS
SET NOCOUNT ON
BEGIN
DECLARE #AccountID INT
DECLARE #MyXML XML
SET #MyXML = #ParaXML
IF OBJECT_ID('tempdb..#TempData') IS NOT NULL
DROP TABLE #TempData
SELECT * INTO #TempData
FROM (
SELECT
Parse.value('(AccountID)[1]', 'INT') AS 'AccountID',
Parse.value('(Code)[1]', 'Varchar(100)') AS 'Code',
Parse.value('(Description)[1]', 'varchar(1000)') AS 'Description'
FROM
#MyXML.nodes('/SampleXML/tblXML') AS YourData(Parse)
) AS tbl
declare #para1 varchar(20)
declare #para2 varchar(20)
declare #para3 varchar(20)
SELECT #para1 =AccountID ,#para2 =Code,#para3 =Description from #TempICD
END

Just one column from a stored procedure

I have this stored procedure in my SQL Server;
CREATE PROC GetChild
#Child_ID int
AS
SELECT * FROM Children WHERE Child_ID = #Child_ID
I am calling this stored procedure from C#.
I would like to know, if it is possible to call just one column from this table instead of the whole record from C#.?
Assuming you mean return one column, if this is what your stored procedure looks like then no. It will always return all columns back to the client.
You can simply ignore the returned columns that you do not need. Or you can change the stored procedure to only return one column. But as is, it always returns all of them.
You have only have three choices.
Rewrite the Stored procedure to just return the columns you want.
e.g. SELECT foo from Children Where Child_id = #Child_ID
Use a DataReader and just get the columns you want from that
Using a reader directly
while (reader.Read())
`Console.WriteLine("{0}", reader.GetInt32(0));`
Using the Linq extension methods which allows you to filter and sort the results as well as getting just the columns you want.
var List = rdr.Cast<IDataRecord>()
.Select(s => s.GetInt32(0)).ToList();
Abandon the stored procedure and write Select statements against the table. See Pranay's answer
just write below query
select columnname from Children where Child_ID = #Child_ID
columnname- is name of the column you want to retrive
Code for you
SqlConnection mySqlConnection =new SqlConnection("server=(local)\\SQLEXPRESS;database=MyDatabase;Integrated Security=SSPI;");
SqlCommand mySqlCommand = mySqlConnection.CreateCommand();
mySqlCommand.CommandText ="select columnname from Children where Child_ID = #Child_ID";
mySqlCommand .Parameters.Add("#Child_ID", SqlDbType.Int);
mySqlCommand .Parameters["#Child_ID"].Value = idvalue;
mySqlConnection.Open();
SqlDataReader mySqlDataReader = mySqlCommand.ExecuteReader(CommandBehavior.SingleRow);
while (mySqlDataReader.Read()){
Console.WriteLine("mySqlDataReader[\" columnname\"] = " +
mySqlDataReader["columnname"]);
}
mySqlDataReader.Close();
mySqlConnection.Close();
Use a SqlDataReader for this:
SqlConnection DbConn = new SqlConnection(YourConnStringHere);
SqlCommand ExecStoredProc = new SqlCommand();
ExecStoredProc.CommandType = CommandType.StoredProcedure;
ExecStoredProc.CommandText = "GetChild";
ExecStoredProc.Connection = DbConn;
ExecStoredProc.Parameters.AddWithValue("#ChildID", YourChildId);
using (DbConn)
{
DbConn.Open();
using (SqlDataReader sdr = ExecStoredProc.ExecuteReader())
{
while(sdr.Read())
// reference your column name like this:
// sdr.GetString(sdr.GetOrdinal("YourColumnName"));
}
}
You can reference any column returned by the SqlDataReader.Read() method. Likewise, if you are looking for an integer value:
int someInt = sdr.GetInt32(sdr.GetOrdinal("YourColumnName"));
From this thread ( Insert results of a stored procedure into a temporary table ), you might want to try OPENROWSET.
First, configure your DB,
sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO
sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO
then, depending on your connection :
SELECT yourcolumnname
FROM
OPENROWSET(
'SQLNCLI',
'server=yourservername;database=yourdbname;uid=youruserid;pwd=youruserpwd',
'EXEC [GetChild] yourchildid'
)
or
SELECT yourcolumnname
FROM
OPENROWSET(
'SQLNCLI',
'server=yourservername;database=yourdbname;Trusted_Connection=yes',
'EXEC [GetChild] yourchildid')
I wouldn't use this solution when retrieving only one line. Performance would be really bad.
For retrieving a great number of lines, this should do the job.

Categories

Resources