Building a SQL UPDATE command by knowing the SELECT command? - c#

Let's say that's my select query:
SELECT
CNDSC.NAME,
CNEA.ATRBT AS ATR,
ISNULL(CNEXTRA.CNVAL,'') AS CNVAL,
ISNULL(CNEXTRA.INRDR,'') AS INRDR
FROM
CNDSC
INNER JOIN CNEA
ON CNEA.ELEMS LIKE '%'+CAST(CNDSC.FNCELEM AS VARCHAR)+'%' AND
NOT CNEA.ELEMS LIKE '%1'+CAST(CNDSC.FNCELEM AS VARCHAR)+'%'
LEFT OUTER JOIN CNEXTRA
ON CNEXTRA.ATR LIKE CNEA.ATRBT AND
CNEXTRA.NAME LIKE #con
WHERE
CNDSC.NAME LIKE #con;
I am using C# to bind the result of that query on a datagrid. But when I try using the "auto-update" command of the SQLDataAdapter, I get an exception due to the use of more than one table in my select.
How would the UPDATE command look like, if I wanted to UPDATE the CNEXTRA.CNVAL table? And how could I ensure, if the CNVAL is empty that I will have to use the INSERT command?
Thank you for any help.

Depending on your version of SQL server you can use MERGE instead of UPDATE.
Something like:
DECLARE #CNVAL varchar(100) = 'test'
DECLARE #ATRBT varchar(100) = 'some attribute'
DECLARE #CON varchar(100) = 'the name'
MERGE into CNEXTRA as target
USING ( VALUES( #CON, #ATRBT, #CNVAL))
AS source([Name], ATRBT, CNVAL)
ON (target.ATRBT = source.ATRBT)
AND (target.[Name] = source.[Name])
WHEN MATCHED THEN
UPDATE SET
CNVAL = source.CNVAL
WHEN NOT MATCHED THEN
INSERT([Name], ATRBT, CNVAL)
VALUES(source.[Name], source.ATRBT, source.CNVAL)
;
A MERGE statement will perform an update if the record exists or an insert if it doesn't.

I haven't tested this code but something like this should work... (but you'll need to write your own insert, update and delete queries)
public static DataSet UpdateSqlRows(
string connectionString,
string selectQuery,
string insertQuery,
string updateQuery,
string deleteQuery,
DataSet dataSet)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlDataAdapter adapter = new SqlDataAdapter())
{
adapter.SelectCommand = new SqlCommand(selectQuery, connection);
connection.Open();
SqlCommandBuilder builder = new SqlCommandBuilder(adapter);
// Assign your own Insert/Update/Delete commands
adapter.InsertCommand = new SqlCommand( insertQuery );
adapter.UpdateCommand = new SqlCommand( updateQuery );
adapter.DeleteCommand = new SqlCommand( deleteQuery );
//Without the SqlCommandBuilder this line would fail
adapter.Update(dataSet);
return dataSet;
}
}
}

Related

How should I get the values from the select query of the stored procedure in c#

I want the date and the name from the select query which if I run as normal query I get the results but i when I try to get the results in C# all I get is count=0. Can anyone tell me what wrong am I doing?
Here is the C# code
private List<CertificationSummary> GetLastAccessData (string taskOwner)
{
List<CertificationSummary> lastAccessedResult = new List<CertificationSummary>();
string connectionString = SqlPlusHelper.GetConnectionStringByName("MetricRepositoryDefault");
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlParameter[] sqlParams = new SqlParameter[1];
sqlParams[0] = new SqlParameter("#taskOwner", SqlDbType.NVarChar);
sqlParams[0].Value = taskOwner;
connection.Open();
SqlCommand cmd = connection.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "GetLastAccessedCertificationData";
cmd.Parameters.AddRange(sqlParams);
cmd.ExecuteNonQuery();
}
return lastAccessedResult;
}
And here is the stored procedure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[GetLastAccessedCertificationData]
(#taskOwner nvarchar(255))
AS
BEGIN
DECLARE #name nvarchar(100)
DECLARE #lastAccessedDate [datetime]
SELECT #name = Name
FROM CertificationReviewCycles
INNER JOIN UserReviewCycleAccess ON CertificationReviewCycles.CertificationReviewCycleID = UserReviewCycleAccess.LastAccessedReviewCycleID
WHERE USERID = #taskOwner
SELECT #lastAccessedDate = LastAccessedDate
FROM UserReviewCycleAccess
WHERE UserID = #taskOwner
CREATE TABLE #tempTable
(
name [nvarchar](255) NULL,
[LastAccessedDate] [datetime] NULL,
)
INSERT INTO #tempTable VALUES (#name, #lastAccessedDate)
SELECT TOP(1) name, LastAccessedDate
FROM #tempTable
END
GO
You are returning lastAccessedResult which is has just been set to new List<CertificationSummary>(). This list has no items, so it has a count of 0.
Use ExecuteReader instead of ExecuteNonQuery and you can then read the data returned and store them into your lastAccessedResult list.
Read here for more info.
ExecuteNonQuery will not return results, and should only be used when you don't expect rows back. This is common for UPDATE statements.
Since you're interested in reading the rows returned by the stored procedure, use ExecuteReader, e.g var reader = cmd.ExecuteReader();
See here for more:
https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqldatareader?view=dotnet-plat-ext-3.1
You're using ExecuteNonQuery, which discards any grids from the query. You need to use ExecuteReader to consume grids, but it is a lot of mess and ceremony - the API is verbose. Frankly, I'd recommend a tool like "Dapper" (freely available on NuGet), then this becomes just
private List<CertificationSummary> GetLastAccessData (string taskOwner)
{
string connectionString = SqlPlusHelper.GetConnectionStringByName("MetricRepositoryDefault");
using var connection = new SqlConnection(connectionString);
return connection.Query<CertificationSummary>(
"GetLastAccessedCertificationData",
new { taskOwner }, // <== parameters
commandType: CommandType.StoredProcedure).AsList();
}

insert value from TextBox into sql

I'm getting this error message: Cannot insert the value NULL into column 'id', table ''; column does not allow nulls. INSERT fails. thanks in advance
protected void AddItem(object sender, EventArgs e)
{
string insertCmd = "INSERT INTO Picture (Album, id) VALUES (#Album, #id)";
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["strConn"].ConnectionString))
{
conn.Open();
SqlCommand myCommand = new SqlCommand(insertCmd, conn);
// Create parameters for the SqlCommand object
// initialize with input-form field values
myCommand.Parameters.AddWithValue("#Album", txtAlbum.Text);
myCommand.Parameters.Add("#id", SqlDbType.Int).Direction = ParameterDirection.Output;
myCommand.ExecuteNonQuery();
int id = (int)myCommand.Parameters["#id"].Value;
}
}
I suppose that ID is an IDENTITY column. Its value is generated automatically by the database engine and you want to know what value has been assigned to your record.
Then you should change your query to
string insertCmd = #"INSERT INTO Picture (Album) VALUES (#Album);
SELECT SCOPE_IDENTITY()";
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["strConn"].ConnectionString))
{
conn.Open();
SqlCommand myCommand = new SqlCommand(insertCmd, conn);
myCommand.Parameters.AddWithValue("#Album", txtAlbum.Text);
int newID = Convert.ToInt32(myCommand.ExecuteScalar());
}
The query text now contains a second instruction SELECT SCOPE_IDENTITY() separated from the first command by a semicolon. SCOPE_IDENTITY returns the last IDENTITY value generated for you by the database engine in the current scope.
Now the command is run using the ExecuteScalar to get back the single value returned by the last statement present in the query text without using any output parameter
I would think that ID is identity. You don't have to add this value. I would try the following code and check the database if you get automatically an ID.
string insertCmd = "INSERT INTO Picture (Album) VALUES (#Album)";
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["strConn"].ConnectionString))
{
conn.Open();
SqlCommand myCommand = new SqlCommand(insertCmd, conn);
// Create parameters for the SqlCommand object
// initialize with input-form field values
myCommand.Parameters.AddWithValue("#Album", txtAlbum.Text);
myCommand.ExecuteNonQuery();
}
I case you want to set the id yourself(withoud automatic increment from the db), you should change the schema of the database removing identity from ID as shown below:
I hope this helps
If you need to stay this column empty you can try to replace to ' '(blank). This will work if you column is not "Key"
Or try to use:
substitute a value when a null value is encountered
NVL( string1, replace_with )
You can do this using stored procedure. Below is the script for Create stored procedure.
CREATE PROCEDURE [dbo].[InsertIntoPicture]
#Album varchar(500)=null,
#id int=0 output
AS
BEGIN
insert INTO Picture(Album)VALUES(#Album)
SET #id=##IDENTITY
END
Below is the code for call stored procedure with C# .
string insertCmd = "InsertIntoPicture";
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["strConn"].ConnectionString))
{
conn.Open();
SqlCommand myCommand = new SqlCommand(insertCmd, conn);
myCommand.CommandType = CommandType.StoredProcedure;
myCommand.Parameters.AddWithValue("#Album", txtAlbum.Text);
myCommand.Parameters.Add("#id", SqlDbType.Int).Direction = ParameterDirection.Output;
myCommand.ExecuteNonQuery();
int id = (int)myCommand.Parameters["#id"].Value;
}
Using above code you can insert a date from TextBox and also get last inserted record ID as an output variable as per your requirement.
Thanks .

SQL Server - Update table and return the Updated rows

I have a SQL Server database which has a lot of information inside.
I want to select top 50 rows in a single query (which I did, with no problem) but then I want to update a column from false to true, so next time I select I wont select the same, my code looks like this:
string Command = "UPDATE HubCommands SET [Alreadytaken] = 'true' FROM (SELECT TOP 50 [CommandId],[DeviceId],[Commandtext], [HashCommand],[UserId] FROM HubCommands) I WHERE [HubId] = '18353fe9-82fd-4ac2-a078-51c199d9072b'";
using (SqlConnection myConnection = new SqlConnection(SqlConnection))
{
using (SqlDataAdapter myDataAdapter = new SqlDataAdapter(Command, myConnection))
{
DataTable dtResult = new DataTable();
myDataAdapter.Fill(dtResult);
foreach (DataRow row in dtResult.Rows)
{
Guid CommandId, DeviceId, UserId;
Guid.TryParse(row["CommandId"].ToString(), out CommandId);
Guid.TryParse(row["DeviceId"].ToString(), out DeviceId);
Guid.TryParse(row["UserId"].ToString(), out UserId);
Console.WriteLine("CommandId" + CommandId);
}
}
}
This code does work, and it updates what I ask it to update, but I don't get nothing in the data table, its like it is always updating but not selecting.
If I do a normal select it does work and give information.
Does anyone have any idea how to update and get some data back, in a single query?
So your question is:
How can I update a table in SQL Server using C# and return the truly updated
rows as a DataTable ?
First You have multiple issues in your query.
You should use 1 and 0, not true or false. SQL-Server has a bit datatype and not a Boolean.
Second, this is how you should've constructed your query:
DECLARE #IDs TABLE
(
[CommandId] uniqueidentifier
);
INSERT INTO #IDs
SELECT [CommandId] FROM HubCommands
WHERE [HubId] = '18353fe9-82fd-4ac2-a078-51c199d9072b' AND [Alreadytaken] = 0;
UPDATE HubCommands
SET [Alreadytaken] = 1
WHERE CommandId IN
(
SELECT [CommandId] FROM #IDs
);
SELECT * FROM HubCommands
WHERE CommandId IN
(
SELECT [CommandId] FROM #IDs
);
Wrap all the above in a single string and use SqlDataReader. No need for an Adapter in you case (Since we're mixing commands unlike what the adapter usually does):
var sqlCommand = new SqlCommand(Command, myConnection);
SqlDataReader dataReader = sqlCommand.ExecuteReader();
DataTable dtResult = new DataTable();
dtResult.Load(dataReader);
I highly advise you to create a stored procedure accepting HubId as a parameter that does all the above work. It is neater and better for maintenance.

Cannot insert value via SqlDataAdapter

I am learning how to work with SQL in C#, and I got in troubles with using SqlDataAdapter. I have tried to use direct queries via SqlCommand class and everything works fine, but when I rewrote my code to use SqlDataAdapter I have no changes in my table. There is my code:
SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ADO"]
.ConnectionString);
connection.Open();
SqlDataAdapter daUser = new SqlDataAdapter("SELECT * FROM Books", connection);
SqlCommand insert = new SqlCommand();
insert.Connection = connection;
insert.CommandText = "INSERT INTO Books (name, author) VALUES (#name, #author);";
SqlParameterCollection pc = insert.Parameters;
pc.Add("#name", SqlDbType.VarChar, 20, "test123");
pc.Add("#author", SqlDbType.VarChar, 20, "test322");
daUser.InsertCommand = insert;
DataSet ds = new DataSet();
daUser.Fill(ds, "Books");
daUser.Update(ds, "Books");
Table Books was created with this SQL query in SQL Server Management Studio:
CREATE TABLE Books
(
id int PRIMARY KEY IDENTITY(1,1),
name varchar(MAX) NOT NULL,
author varchar(MAX) NOT NULL
)
INSERT INTO Books(name, author)
VALUES('1984', 'George Orwell'), ('Fathers and sons', 'Dostoevski')
Looks like I am missing something to do, that why my code have no effect on table.
SqlDataAdapter.Update will call its InsertCommand only for the rows of datatable having RowState = DataRowState.Added.
This rowstate is automatically assigned to the datarows being added to rows collection using DataTable.Add method (until next call to AcceptChanges method). Also you can use DataRow.SetAdded method to force this state assignment.
Since you're not modifying/adding anything in you datatable after you've populated it with select command, it has nothing to insert.
Change your code to something like
daUser.Fill(ds, "Books");
var newBook = daUser.Tables[0].NewRow();
newBook["name"] = "New Book";
newBook["author"] = "Author Name";
daUser.Tables[0].Rows.Add(newBook);
daUser.Update(ds, "Books");
and in this case it should be new row added to the database table.
See MSDN for reference.
Just to clarify the previous answer, which is correct, you want to call ExecuteNonQuery() on the command not the dataAdapter.
SqlCommand insert = new SqlCommand();
insert.Connection = connection;
insert.CommandText = "INSERT INTO Books (name, author) VALUES (#name,
#author);";
SqlParameterCollection pc = insert.Parameters;
pc.Add("#name", SqlDbType.VarChar, 20, "test123");
pc.Add("#author",
SqlDbType.VarChar, 20, "test322");
// you do not need this line if you execute the insert on the command object.
// daUser.InsertCommand = insert;
//Add this line instead:
insert.ExecuteNonQuery();
Joey

Is there a way to insert a C# string into a SQL varbinary column?

I am using SqlConnection and SqlCommand in C# to insert rows of strings (via Parameters.AddWithValue) into SQL Server. One of the columns I need to insert into is a Varbinary. Is there any way to do the conversion in either C# or SQL Server?
According to this answer, you can use the CONVERT function to insert a string into a VARBINARY column. Make sure you use the proper encoding, as discussed in that answer.
insert Table_2 (Test) values( CONVERT(varbinary(30), N'this is a test') )
select * from Table_2
select CONVERT(nvarchar(30), test) from Table_2
So the C# code would look something like
// Get from config
string connectionString = "";
using (var conn = new SqlConnection(connectionString))
{
string sql = "insert Table_2 (Test) values( CONVERT(varbinary(30), #nvarcharParam) )";
using (var cmd = new SqlCommand(sql, conn))
{
var param = cmd.Parameters.AddWithValue("nvarcharParam", "This is a test");
param.DbType = DbType.String;
cmd.ExecuteNonQuery();
}
}

Categories