I have a DataTable in C# that I need to insert into a table. The DataTable is completely dynamic (Columns are not pre-defined). I inserted this DataTable row by row using C# code but because it is inefficient I am sending the DataTable bulk into an SQL Stored Procedure. I need the Stored Procedure to loop through the bulk, insert row by row, and return the set of invalid data.
C# code:
SqlConnection sqlConnection = getDBConnection();
SqlCommand command = sqlConnection.CreateCommand();
command.CommandType = System.Data.CommandType.StoredProcedure;
command.CommandText = "[dbo].[saveDataIntoTable]";
SqlParameter parameter = new SqlParameter();
//The parameter for the SP must be of SqlDbType.Structured
parameter.ParameterName = "#Sample";
parameter.SqlDbType = System.Data.SqlDbType.Structured;
parameter.Value = dataTable;
command.Parameters.Add(parameter);
foreach (DataRow row in dataTable.Rows)
{
System.Diagnostics.Debug.WriteLine(row.ItemArray);
if (row.ItemArray[0] == null)
{
dataTable.Rows.Remove(row);
}
}
SqlDataReader dr = command.ExecuteReader();
DataTable dt = new DataTable();
dt.Load(dr);
//handling the dt DataTable here
Stored Procedure:
USE [DATABASE_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[saveDataIntoTable]
(
-- which accepts one table value parameter. It should be noted that the parameter is readonly
#Sample As [dbo].[SampleUserData] Readonly
)
AS
BEGIN
BEGIN TRY
Insert Into USER(USER_ID,EMAIL,PASSWORD,PASSWORD_HINT,PWD_CHANGED_DATE,
CREATED_BY,CREATED_DATE,UPDATED_BY,UPDATED_DATE,STATUS,VERSION,VALIDATE)
Select USER_ID, EMAIL,PASSWORD,PASSWORD_HINT,PWD_CHANGED_DATE,
CREATED_BY,CREATED_DATE,UPDATED_BY,UPDATED_DATE,STATUS,VERSION,VALIDATE From #Sample
END TRY
BEGIN CATCH
Select USER_ID, EMAIL,PASSWORD,PASSWORD_HINT,PWD_CHANGED_DATE,
CREATED_BY,CREATED_DATE,UPDATED_BY,UPDATED_DATE,STATUS,VERSION,VALIDATE From #Sample
END CATCH
END
I used a User-defined Table Type for the Stored Procedure:
-- Create a table data type
CREATE TYPE [dbo].[SampleUserData] As Table
(
--This type has structure similar to the DB table
USER_ID Nvarchar(20) ,
EMAIL Nvarchar(50),
PASSWORD Nvarchar(100),
PASSWORD_HINT Nvarchar(20),
PWD_CHANGED_DATE date,
CREATED_BY Nvarchar(20),
CREATED_DATE date,
UPDATED_BY Nvarchar(20),
UPDATED_DATE date,
STATUS Nvarchar(20),
VERSION Int,
VALIDATE Nvarchar(10)
);
Right now, my Stored Procedure inserts the whole bulk of data at once. When an exception occurs, it returns the whole DataSet (I do not know how to separate the rows).
PS: If there is any other method for the above scenario which is easier, please let me know.
Thanks.
This will do the same as your stored procedure one line at a time:
This was written for SQL Server 2005
BEGIN
--I'm only using your first three columns in this example
DECLARE #USER_ID as Nvarchar(20);
DECLARE #Email as Nvarchar(20);
DECLARE #Password as Nvarchar(20);
DECLARE #SampleCursor as CURSOR;
SET #SampleCursor = CURSOR FOR
SELECT USER_ID, EMAIL, PASSWORD
FROM #Sample;
OPEN #SampleCursor;
--Can't insert directly into table from table variable so save as scalar variable first
FETCH NEXT FROM #SampleCursor INTO #USER_ID, #Email, #Password;
WHILE ##FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM #SampleCursor INTO #USER_ID, #Email, #Password;
BEGIN TRY
--then insert scalar variables into table
INSERT INTO USER (USER_ID, Email, Password) VALUES( #USER_ID, #Email, #Password)
END TRY
BEGIN CATCH
SELECT #USER_ID, #Email, #Password
END CATCH
END
CLOSE #SampleCursor;
DEALLOCATE #SampleCursor;
END
This might work. Just insert your source table one line at a time using TOP(1). The solution below creates a table temporarily so your source table isn't deleted.
--create copy of source table
SELECT * INTO TempTbl FROM Source_Table
--loop through temp table
WHILE EXISTS (SELECT * FROM TempTbl)
BEGIN
--insert first line of temp table into destination table
BEGIN TRY
INSERT INTO [USER] SELECT TOP (1) * FROM TempTbl
END TRY
BEGIN CATCH
SELECT TOP(1) FROM TempTbl
END CATCH
--remove inserted line from temp table
DELETE TOP (1) FROM TempTbl
END
DROP TABLE TempTbl
Update.
This works for me:
CREATE PROCEDURE SOProc
-- Add the parameters for the stored procedure here
#Source_Table_Name sysname = ''
AS
BEGIN
EXEC(
'SELECT * INTO TempTbl FROM ' + #Source_Table_Name)
WHILE EXISTS (SELECT * FROM TempTbl)
BEGIN
BEGIN TRY
INSERT INTO User_Table SELECT TOP (1) * FROM TempTbl
END TRY
BEGIN CATCH
SELECT TOP(1) * FROM TempTbl
END CATCH
DELETE TOP (1) FROM TempTbl
END
DROP TABLE TempTbl
END
GO
Why not use a where clause to validate the data
-- insert valid data
insert into [USER] (USER_ID,EMAIL,PASSWORD,PASSWORD_HINT,PWD_CHANGED_DATE,
CREATED_BY,CREATED_DATE,UPDATED_BY,UPDATED_DATE,STATUS,VERSION,VALIDATE)
select USER_ID, EMAIL,PASSWORD,PASSWORD_HINT,PWD_CHANGED_DATE,
CREATED_BY,CREATED_DATE,UPDATED_BY,UPDATED_DATE,STATUS,VERSION,VALIDATE
from #Sample
where USER_ID is not null
-- select invalid data
select USER_ID, EMAIL,PASSWORD,PASSWORD_HINT,PWD_CHANGED_DATE,
CREATED_BY,CREATED_DATE,UPDATED_BY,UPDATED_DATE,STATUS,VERSION,VALIDATE
from #Sample
where USER_ID is null
An example of a more detailed validation check
select USER_ID, EMAIL,PASSWORD,PASSWORD_HINT,PWD_CHANGED_DATE,
CREATED_BY,CREATED_DATE,UPDATED_BY,UPDATED_DATE,STATUS,VERSION,VALIDATE
from #Sample S
where USER_ID is not null
and EMAIL is not null
and PASSWORD is not null -- etc
-- check record is not duplicate
and not exists (select 1 from [USER] U where U.USER_ID = S.USER_ID)
and isnumeric(USER_ID)
Related
I am executing a stored procedure in a C# asmx webservice.
the stored procedure is as follows:
#userName NVARCHAR(50),
#password NVARCHAR(50),
#defaultTabApp NVARCHAR(20) OUT
AS
Declare #defaultTabApptemp NVARCHAR(20)
set #defaultTabApptemp ='NoAccess'
BEGIN TRANSACTION
if EXISTS(SELECT Top 1 [userId],[userName] FROM [dbo].[users] WHERE [userName]=#userName AND [password]=#password AND [AppAccess]=N'Yes')
Begin
set #defaultTabApptemp = (select Top 1 [dbo].[users].[defaultTabApp] FROM [dbo].[users] WHERE [userName]=#userName AND [password]=#password AND [AppAccess]=N'Yes')
end
select #defaultTabApp = #defaultTabApptemp
COMMIT TRANSACTION
return #defaultTabApp
my c# code is:
[WebMethod]
public string Login(string userName, string userPass)
{
string result;
SqlConnection conn = new SqlConnection(new DBConnection().ConnectionString);
try
{
SqlCommand cmd = new SqlCommand("_getSpecificUserLogin", conn);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
if (conn.State == System.Data.ConnectionState.Closed)
{
conn.Open();
}
cmd.Parameters.AddWithValue("#userName", userName);
cmd.Parameters.AddWithValue("#password", userPass);
cmd.Parameters.Add("#defaultTabApp", SqlDbType.NVarChar,20);
cmd.Parameters["#defaultTabApp"].Direction = ParameterDirection.Output;
int i = cmd.ExecuteNonQuery();
result = Convert.ToString(cmd.Parameters["#defaultTabApp"].Value);
}
finally
{
conn.Close();
}
return result;
}
i'm getting the exception found in the title on this line:int i = cmd.ExecuteNonQuery(); I tried to change it to executescalar but had the same problem. I have no int types in my stored procedure. what is the problem exactly? thanks in advance.
The bare bones of your login procedure should look something like the following.
Note, you do not need a transaction, nor do you need to check if anything exists first - that's implied by the fact it assigns a value to your output variable.
Your code doesn't handle the case where the password is incorrect, or if there are no enabled apps for the user.
In reality, you would validate the user first and assign them some sort of ticket to indicate they have logged in successfully and would not be repeatedly checking their password; when it comes time to get their default app, they are already authenticated.
You only need a top 1 if you can have a single user with more than one appAccess='Yes', in which case you are missing the ordering criteria by which it will select the correct one - without a specific order clause, the value is essentially random.
The correct syntax for an output parameter is Output
I would also hope that if this is a public facing application that the password is not plain text and is stored in the database as a hash of the user's password.
create procedure UserLogin
#userName nvarchar(50),
#password nvarchar(50), -- This should be a HASH of the user's password, performed by the application
#defaultTabApp nvarchar(20) output
as
set nocount on
set #defaultTabApp='Default failure message'
select top 1 #defaultTabApp=defaultTabApp
from dbo.Users
where AppAccess=N'Yes'
and Username=#userName and [Password]=#password
order by <<criteria if there are more than 1 matching rows>>
Go
RETURN can only return int. You already used an OUT parameter, you should remove return #defaultTabApp statement at the end of your stored procedure.
Replace
Declare #defaultTabApptemp NVARCHAR(20)
set #defaultTabApptemp ='NoAccess'
BEGIN TRANSACTION
if EXISTS(SELECT Top 1 [userId],[userName] FROM [dbo].[users] WHERE [userName]=#userName AND [password]=#password AND [AppAccess]=N'Yes')
Begin
set #defaultTabApptemp = (select Top 1 [dbo].[users].[defaultTabApp] FROM [dbo].[users] WHERE [userName]=#userName AND [password]=#password AND [AppAccess]=N'Yes')
end
select #defaultTabApp = #defaultTabApptemp
COMMIT TRANSACTION
return #defaultTabApp
with
Declare #defaultTabApptemp NVARCHAR(20)
select Top 1 #defaultTabApptemp=[dbo].[users].[defaultTabApp] FROM [dbo].[users]
WHERE [userName]=#userName AND [password]=#password AND [AppAccess]=N'Yes')
if #defaultTabApptemp is null SET #defaultTabApptemp ='NoAccess'
set #defaultTabApp = #defaultTabApptemp
I am trying to display report in mvc web application using rdlc. I have created data set and data table. but now report viewer always reloading continuously. when i try to figure out the problem i found this issue.
Here C# Code
private DataTable GetData(DateTime sdate, DateTime edate,string user,string sp_name)
{
DataTable oDataTable = new DataTable();
string ConStr = System.Configuration.ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
using (SqlConnection con = new SqlConnection(ConStr))
{
SqlCommand cmd = new SqlCommand("GetBalanceSheet_CurrentUser", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#SDate", SqlDbType.DateTime).Value = sdate;
cmd.Parameters.Add("#EDate", SqlDbType.DateTime).Value = edate;
cmd.Parameters.Add("#Uid", SqlDbType.VarChar).Value = user;
SqlDataAdapter oSqlDataAdapter = new SqlDataAdapter(cmd);
oSqlDataAdapter.Fill(oDataTable);
}
return oDataTable;
}
Here Sql Stored Procedure
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- GetBalanceSheet_CurrentUser '2017-09-12' ,'2017-11-12' ,'ab0eb318-de5f-4f13-a80b-7a95f678ad8a'
ALTER PROCEDURE [dbo].[GetBalanceSheet_CurrentUser]
#SDate DateTime,#EDate DateTime,#Uid VarChar(MAX)
AS
IF OBJECT_ID ('tempdb..#TEMP1') IS NOT NULL
DROP TABLE #TEMP1
IF OBJECT_ID ('tempdb..#TEMP2') IS NOT NULL
DROP TABLE #TEMP2
DECLARE #UserId VarChar(MAX)
SELECT #UserId = #Uid
SELECT ROW_NUMBER() OVER(ORDER BY Date) RNo,Description,Date,Amount,ForUser,CreatedUser,Status,ApprovedBy
INTO #TEMP1
FROM (
SELECT PEDM.ProjectExpenditureDescription AS Description , PEDM.ExpendedDate As Date,PEDM.ProjectExpenditureCost As Amount, ANU1.UserName As ForUser,ANU1.UserName As CreatedUser,PEDM.ApprovedStatus As Status,ANU2.UserName As ApprovedBy
FROM ProjectExpenditureDetailsModel PEDM
JOIN AspNetUsers ANU1
ON ANU1.Id = PEDM.CreatedUser
JOIN AspNetUsers ANU2
ON ANU2.Id = PEDM.ApprovedBy
WHERE PEDM.IsActive = 1
AND PEDM.IsDelete = 0
AND PEDM.Rejected = 0
AND PEDM.CreatedUser = #UserId
UNION ALL
SELECT OMDM.ObtainedMoneyDescription As Description,OMDM.ObtainedDate As Date,(OMDm.ObtainedMoneyAmount *-1) As Amount, ANU3.UserName As ForUser,ANU4.UserName As CreatedUser,OMDM.ApprovedStatus,ANU5.UserName As ApprovedBy
FROM ObtainedMoneyDetailsModel OMDM
JOIN AspNetUsers ANU3
ON ANU3.Id = OMDM.ForUser
JOIN AspNetUsers ANU4
ON ANU4.Id = OMDM.CreatedUser
JOIN AspNetUsers ANU5
ON ANU5.Id = OMDM.ApprovedBy
WHERE OMDM.IsActive = 1
AND OMDM.IsDelete = 0
AND OMDM.Rejected = 0
AND OMDM.ForUser = #UserId
)A
ORDER BY Date
SELECT RNo,Description,Convert (varchar(20),Date,103) AS ConvertedDate,Date,Amount,SUM(Amount) OVER (ORDER BY RNo) AS Balance,ForUser,CreatedUser,Status,ApprovedBy
INTO #TEMP2
FROM #TEMP1
SELECT RNo,Description,ConvertedDate,Date,Amount,Balance,ForUser,CreatedUser,Status,ApprovedBy
FROM #TEMP2
WHERE Date Between #SDate AND #EDate
When I execute program sql profiler indicates it hits successfully. but when i return result to data table its always getting empty.
I have executed sp manually using same parameters. it is showing 10
records.but when i call through my c# code its hitting but sql data
adapter always empty like {}
Do you execute your sp manually under the same account?
Your procedure uses datetime parameters but the literals for this parameters that you are passing in are language dependent.
Instead, you should pass them in language independent format yyyymmdd (without any separator)
Now, your login language is different from the default login of your application, this cause the date literals to be interpreted differently.
Here is an example to show you the problem. First execute it as it is, then comment set language us_english and uncomment set language British;
declare #t table (dt datetime);
insert into #t values ('20171001'), ('20171101'); -- added 1st oct + 1st nov
--set language British;
set language us_english;
declare #SDate datetime ='2017-09-12', #EDate datetime = '2017-11-12';
select *
from #t
WHERE dt Between #SDate AND #EDate;
I'm trying to create a new stored procedure programatically using the following code:
using (MySqlConnection conn = new MySqlConnection(connectionString))
{
conn.Open();
using (MySqlTransaction trans = conn.BeginTransaction())
{
using (MySqlCommand command = conn.CreateCommand())
{
command.CommandText = query;
command.ExecuteNonQuery();
}
trans.Commit();
}
}
And the following text as the create statement copied from Mysql workbench:
static string query = #"
delimiter $$
CREATE PROCEDURE `GetParentIds`(IN `tempTableName` VARCHAR(255), IN `id` int)
BEGIN
DECLARE parId INT;
DECLARE curId INT;
DROP TEMPORARY TABLE IF EXISTS tempTableName;
CREATE TEMPORARY TABLE tempTableName (
node_id INT NOT NULL PRIMARY KEY
);
set curId := id;
get_parents_loop: LOOP
set parId := null;
set parId = (select ParentID from {TableName} where ID = curId);
IF parId is NULL THEN
LEAVE get_parents_loop;
END IF;
INSERT INTO tempTableName(node_id) Values (parId);
set curId := parId;
END LOOP get_parents_loop;
SELECT *
FROM tempTableName;
END$$";
This procedure is passed an ID of an object which has a parentID and it gets all of the parentIDs of all the parents of the given object and returns them. The problem comes when I try to run it and I get the following message:
"You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'delimiter $$
CREATE PROCEDURE GetParentIds(IN tempTableName VARCHAR(255),' at line 1"
Any and all Ideas are welcome!
* EDIT **
Thanks to all the answers below, this is what finally worked:
CREATE PROCEDURE GetParentIds(IN tempTableName VARCHAR(255), IN id int)
BEGIN
DECLARE parId INT;
DECLARE curId INT;
DROP TEMPORARY TABLE IF EXISTS tempTableName;
CREATE TEMPORARY TABLE tempTableName (node_id INT NOT NULL PRIMARY KEY );
set curId := id;
get_parents_loop: LOOP
set parId := null;
set parId = (select ParentID from TDOs where TDOID = curId);
IF parId is NULL THEN
LEAVE get_parents_loop;
END IF;
INSERT INTO tempTableName(node_id) Values (parId);
set curId := parId;
END LOOP get_parents_loop;
SELECT *
FROM tempTableName;
END;
Remove DELIMITER $$ at the beginning and $$ after last END
DELIMITER is a mysql client command that enables you to change its statement terminator temporarily while you define a stored routine.
If you are defining a stored routine from within a programming interface that does not use the semicolon as a statement terminator, semicolons within stored routine definitions do not present any special issues.
I have a table in SQL Server with 3 columns Id (int identity), email (nvarchar(50)), password (nvarchar(50)). Now I want to write a query where I can insert email and password and that time, I want to return the identity element for id.
For e.g I insert abc#dal.ca and password then the identity element value should be returned.
I wrote it as:
#email nvarchar(50), #password nvarchar(50), #id int
insert into addEmail(email, password)
values(#email,#password)
return #id
Is this proper ?? How should I do ? How should I check whether this is working properly or not ? If I select
dbo.sp_addEmailReturnId abc#dal.ca, demo
and click on execute, it shows
Incorrect syntax near '.'.
I am unable to find the error. I am just trying to insert email id and password so that could be inserted and i would get the identity element which is automatically incremented by 1 with every new row.
In code part for asp.net, how would I retrieve the id. ?
try
insert into addEmail(email,password)
OUTPUT INSERTED.ID
values(#email,#password)
Try this query; it will fetch you the id
insert into addEmail(email, password) values(#email,#password) Select ##IDENTITY;
Do like this
private int getEmail(string email, string password)
{
string cs = ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
using (SqlConnection conn = new SqlConnection(cs))
{
conn.Open();
SqlCommand cmd = new SqlCommand("sp_addEmailReturnid", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#email", email);
cmd.Parameters.AddWithValue("#password",password);
int nUploadId = Convert.ToInt32(cmd1.ExecuteScalar()); } // Updated Part
NOTE
At the end of your stored proc i.e after insert statement add
Select Scope_Identity()
Edit
Your proc would be sometihng like this
ALTER proc [dbo].[sp_addEmailReturnid]
#email VARCHAR(500),
#password VARCHAR(500)
AS
BEGIN
// Your insert statement here
Select Scope_Identity()
End
Use Scope_Identity
insert into addEmail(email,password) values(#email,#password)
SELECT SCOPE_IDENTITY()
I am using ASP.NET 4.0, C# and SQL Server 2008 R2. I am getting UserName from the user in a webpage for stored in the SQL table User_Info2. Using SQL Server 2008 feature "computed column" I am generating the Vendor_ID automatically for every Insert using the stored procedure. In button click, after I insert the record I want to display the message with Vendor_ID, so please anyone tell me how to get the Vendor_ID column from the stored procedure ?
CREATE TABLE User_Info2
( SNo int Identity (2000,1) ,
Vendor_ID AS 'VEN' + CAST(SNo as varchar(16)) PERSISTED PRIMARY KEY,
UserName VARCHAR(30) NOT NULL
)
Stored procedure
ALTER PROCEDURE [dbo].[usp_User_Info2] #UserName VARCHAR(30)
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO User_Info2 (UserName) VALUES (#UserName)
END
C# Code :
protected void BtnUserNext_Click(object sender, EventArgs e)
{
SqlConnection SqlCon = new SqlConnection(GetConnectionString());
SqlCommand cmd = new SqlCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "usp_User_Info2";
cmd.Parameters.Add("#UserName", SqlDbType.VarChar).Value = txtUserName.Text.Trim();
cmd.Connection = SqlCon;
try
{
SqlCon.Open();
cmd.ExecuteScalar();
}
finally
{
string url = "../CompanyBasicInfo.aspx?Parameter=" + Server.UrlEncode ("+ Vendor_ID +");
ClientScript.RegisterStartupScript(this.GetType(),"callfunction",
"alert('Login created successfully for "+ Vendor_ID +"');
window.location.href = '" + url + "';", true);
SqlCon.Close();
}
}
You can output the inserted value using the OUTPUT clause, and then read it when you execute the stored procedure:
ALTER PROCEDURE [dbo].[usp_User_Info2] #UserName VARCHAR(30)
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO User_Info2 (UserName)
OUTPUT Inserted.Vendor_ID
VALUES (#UserName)
END
and in your C# calling code:
object spResult;
string vendorID;
try
{
SqlCon.Open();
spResult = cmd.ExecuteScalar();
if(spResult != null) // check to make sure you got something back!
{
vendorID = spResult.ToString();
}
}
You can try this
ALTER PROCEDURE [dbo].[usp_User_Info2] #UserName VARCHAR(30)
AS
BEGIN
SET NOCOUNT ON;
declare #Id int
INSERT INTO User_Info2 (UserName) VALUES (#UserName)
SET #Id = Scope_Identity()
SELECT Vendor_ID From User_Info2 WHERE SNo = #Id
END
C#
try
{
SqlCon.Open();
string VendorID = cmd.ExecuteScalar() as string;
}
Also u can do like this
select top(1) Vendor_ID from User_Info2 order by SNo desc
You can get currently insert row with the help of SCOPE_IDENTITY()
SET #SNo = SCOPE_IDENTITY()
And below insert query you can execute select query on the #SNo
So it would be:
ALTER PROCEDURE [dbo].[usp_User_Info2]
(
#UserName VARCHAR(30),
#SNo int,
#Vendor_ID VARCHAR(50) OUTPUT
)
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO User_Info2 (UserName) VALUES (#UserName)
SET #SNo = Scope_Identity()
select #Vendor_ID as Vendor_ID from User_Info2 where SNo = #SNo
END
EDIT:
SqlParameter[] param= new SqlParameter[1];
param[0] = new SqlParameter("#Vendor_ID", 0);
param[0].Direction = ParameterDirection.Output;
// Here there will be a Stored Procedure Call
int VendorID = Convert.ToInt32(param[0].Value);
So now you will #Vendor_ID which is an Output variable.