Multi SQL statements executed via ADO.NET command object - c#

I need to update a record in a table but it is possible that the record does not exist, so I would need to insert the record.
Below is a SQL statement that accomplished my goal by trying to update the record first and if it doesn't exist it performs an insert. What I am wondering if it can be executed directly via ADO.NET command object or does it need to go into a stored procedure.
UPDATE MyTable SET ReservationDate = #ReservationDate WHERE CustomerNumber = XXXX;
IF ##ROWCOUNT=0
INSERT INTO MyTable (CustomerNumber , ReservationDate)
VALUES (#CustomerNumber , #ReservationDate)
If I could execute it via the command object without a stored procedure it would mean one less dependency for deployment time (i.e. deploying the stored procedure).

The MERGE command in T-SQL works for just this scenario
string cmdText = #"MERGE MyTable T
USING (SELECT #CustomerNumber As CustomerNumber) as S
ON T.CustomerNumber = S.CustomerNumber
WHEN MATCHED UPDATE SET ReservationDate = #ReservationDate
WHEN NOT MATCHED INSERT INTO (CustomerNumber , ReservationDate)
VALUES (#CustomerNumber , #ReservationDate)";
It is just one string of text wrapped for readability in more lines thanks to the verbatim character #
With MERGE you start defining your TARGET table (T), then you build a pseudotable called SOURCE (S) with the parameter that contains the value for the primary key field in TARGET. Now the two tables are JOINED ON the field CustomerNumber. The product of this join could be MATCHED or NOT MATCHED depending of the previous presence of the record in the TARGET table. The remainder of the query is probably self explanatory, just notice that in the two actions (UPDATE and INSERT) there is no need to repeat the MyTable name (it is the TARGET)
By the way, yes you could pass multiple commands to the SqlCommand separated by a semicolon

Related

Is there any way to set Database name as global name in sql server?

I have two databases as mentioned below:
[QCR_DEV]
[QCR_DEV_LOG]
All application data are stored in [QCR_DEV]. On each table of [QCR_DEV], there is a trigger that insert the details of insertion and update of [QCR_DEV] table into [QCR_DEV_LOG] database.
Suppose i have a table [party] in [QCR_DEV] database. Whenever i insert,update or delete some record in the table. There will be one insertion in table [party_log] which exists in [QCR_DEV_LOG] database. In short i am keeping the log or action performed on tables of [QCR_DEV] into [QCR_DEV_LOG] database.
When we connect to database through application, it connect to database somehow using connection-string. In my stored procedure, i did not use database name like:
Select * From [QCR_DEV].[party];
I am using like this:
Select * From [party];
This is because, in feature if i need to change database name then i will only need to change connection-string.
Now come to the point, i need to get data from [QCR_DEV_LOG] database. I am writing a stored procedure in which i need to get data from both databases like:
Select * From [QCR_DEV_LOG][party_log]
INNER JOIN [person] on [person].person_id = [QCR_DEV_LOG][party_log].person_id
where party_id = 1
This stored procedure is in [QCR_DEV] database. I need to get data from both databases. For this i need to mention the database name in query. I don't want this. Is there any way to set database name globally and use this name in my queries so that if in future i need to change database name, i only change from where it sets globally. Is there any way to do this?
I would second Jeroen Mostert comment and use synonyms:
CREATE SYNONYM [party_log] FOR [QCR_DEV_LOG].[dbo].[party_log];
And when the target database is renamed, this query would generate a migration script:
SELECT 'DROP SYNONYM [' + name + ']; CREATE SYNONYM [' + name + '] FOR ' + REPLACE(base_object_name, '[OldLogDbName].', '[NewLogDbName].') + ';'
FROM sys.synonyms
WHERE base_object_name LIKE '[OldLogDbName].%';
You could do this in the DEV database:
CREATE VIEW [dbo].[party_log]
AS
SELECT * FROM [QCR_DEV_LOG].[dbo].[party_log]
Then you can write SELECT-queries as if the [party_log] table exists in the DEV database.
Any WHERE.. or JOIN..ON.. clauses should get applied before the combined query is executed.
If the LOG database ever gets moved or renamed, then you'd only need to update the view (or a couple of views, but probably never a lot).
If you expect regular changes, or if you need to use this on multiple servers then you could use dynamic SQL:
IF OBJECT_ID('[dbo].[party_log]') IS NOT NULL DROP VIEW [dbo].[party_log]
-- etc, repeat to DROP other views
DECLARE #logdb VARCHAR(80) = 'QCR_DEV_LOG'
EXEC ('CREATE VIEW [dbo].[party_log] AS SELECT * FROM [' + #logdb + '].[dbo][party_log]')
-- etc, repeat to create other views

Parameters not replacing properly in NpgsqlCommand

I'm trying to replace parameters in a string to execute in an Npgsql query.
The problem is, when it replaces the parameter by its value in the string it adds unnecessary parentheses and so the query returns an error.
NAME_SCHEMA_DB and NAME_ADMIN_DB are string constants and
ExecuteCommand just takes an NpgsqlCommand and executes it.
This is my code:
String qdropSchema = #"DROP SCHEMA IF EXISTS #name_schem CASCADE";
String qCreateSchema = #"CREATE SCHEMA #name_schem AUTHORIZATION #name_admin";
DbCommand commandeDrop = new NpgsqlCommand(qdropSchema);
commandDrop.Parameters.Add(new NpgsqlParameter("#name_schem", NAME_SCHEMA_DB));
DbCommand commandCreate = new NpgsqlCommand(qCreateSchema);
commandCreate.Parameters.Add(new NpgsqlParameter("#name_schem", NAME_SCHEMA_DB));
commandCreate.Parameters.Add(new NpgsqlParameter("#name_admin", NAME_ADMIN_DB));
ExecuteCommand(commandDrop);
ExecuteCommand(commandCreate);
This is what the SQL query it tries to run when it reaches ExecuteCommand(commandDrop)
DROP SCHEMA IF EXISTS (('test_schemaName')) CASCADE;
I 'm not sure why it adds the extra parentheses and single quotes. Normally, I'd want the query it runs to be
DROP SCHEMA IF EXISTS test_schemaName CASCADE;
SQL parameters are generally only valid for values (e.g. the values of fields) - not field names and table names etc. While it's annoying, you'll probably need to embed these names directly into the SQL.
You should be very careful doing that, of course - anywhere that it might be from user input, you should use a whitelist of some form.

Correct query to pull the last modified time for a table on SQL server?

My goal is to pull the last modified date and time for a table in SQL server (MS SQL SERVER 2008 R2), when I say last modified date and time, I specifically meant the changes of values for the records of that table. For example, value added, deleted, or updated. Not changes such as structural change for the table.
Assuming my DB name is MyDB.
Assuming my table name is MyTable.
So I used the following query and it did work every time I changed a value for a record in the table, and reflects the correct time for the change:
SELECT last_user_update from sys.dm_db_index_usage_stats where database_id = DB_ID('MyDB') and object_id = object_id('MyDB.dbo.Mytable')
My question now - Is this query the correct way to meet my goal? Because I sort of came up with this query by trail and error so I need some confirmation. Also, does this query also reflect other changes for the table such as structural changes? If so, is there a better query that is cleaner and only reflects value changes within the table?
MSDN States: The user_updates counter indicates the level of maintenance on the index caused by insert, update, or delete operations on the underlying table or view.
https://msdn.microsoft.com/en-us/library/ms188755.aspx
So its probably OK.
However you could probably put a LastModifiedDateTime field on the DB and set it during an operation and then select the MAX value for this.
EDIT: As per comments:
CREATE TRIGGER LastUpdateTrigger
ON sourceTable
AFTER INSERT, UPDATE, DELETE
AS
IF NOT EXISTS(SELECT * FROM destTable WHERE TableName = 'sourceTable')
BEGIN
INSERT INTO destTable (LastUpdateDateTime, TableName)
VALUES (GETDATE(), 'sourceTable')
END
ELSE
BEGIN
UPDATE destTable SET LastUpdateDateTime = GETDATE()
WHERE Tablename = 'sourceTable'
END
Also put table name in destTable to track updates accross tables in same database.

Check for new/existing values and update in sql

I have a SQL table with Project Column value as 'Project1,Project2,Project3'
I need to update this row if they select a different value like 'Project4' from the telerik dropdown list as 'Project1,Project2,Project3,Project4'
I get the value from the dropdown same as 'Project1,Project2,Project3',so I will send this as a paramter to SQL.
Suppose if they select 'Project5,Project1'...Project1 should not be added as its already there.
Can some one suggest how do I check for new and existing values and update accordingly.
My simple update is not working for this scenario.Kind of struck.
Thanks
You can create a stored procedure and use merge to insert or update as necessary like the example below
DECLARE #nameField VarChar(50) = 'some data'
MERGE dbo.MyTable t
USING (SELECT #nameField [field]) s
ON t.myData = s.field
WHEN MATCHED THEN
UPDATE
SET t.myData = #nameField
WHEN NOT MATCHED THEN
INSERT (myData)
VALUES (#nameField);
If you want to limit redundant updates, e.g. if updating Project Column with the exact same data and block such updates, then you'll need to create an update trigger to check and block the update.

C# database update

I'm stuck on a little problem concerning database.
Once a month I get a XML file with customer information (Name, address, city,etc.). My primary key is a customer number which is provided in the XML file.
I have no trouble inserting the information in the database;
var cmd = new SqlCommand("insert into [customer_info]
(customer_nr, firstname, lastname, address_1, address_2, address_3.......)");
//some code
cmd.ExecuteNonQuery();
Now, I would like to update my table or just fill it with new information. How can I achieve this?
I've tried using TableAdapter but it does not work.
And I'm only permitted to add one XML because I can only have one customer_nr as primary key.
So basically how do I update or fill my table with new information?
Thanks.
One way would be to bulk insert the data into a new staging table in the database (you could use SqlBulkCopy for this for optimal insert speed). Once it's in there, you could then index the customer_nr field and then run 2 statements:
-- UPDATE existing customers
UPDATE ci
SET ci.firstname = s.firstname,
ci.lastname = s.lastname,
... etc
FROM StagingTable s
INNER JOIN Customer_Info ci ON s.customer_nr = ci.customer_nr
-- INSERT new customers
INSERT Customer_Info (customer_nr, firstname, lastname, ....)
SELECT s.customer_nr, s.firstname, s.lastname, ....
FROM StagingTable s
LEFT JOIN Customer_Info ci ON s.customer_nr = ci.customer_nr
WHERE ci.customer_nr IS NULL
Finally, drop your staging table.
Alternatively, instead of the 2 statements, you could just use the MERGE statement if you are using SQL Server 2008 or later, which allows you to do INSERTs and UPDATEs via a single statement.
If I understand your question correctly - if the customer already exists you want to update their information, and if they don't already exist you want to insert a new row.
I have a lot of problems with hard-coded SQL commands in your code, so I would firstly be very tempted to refactor what you have done. However, to achieve what you want, you will need to execute a SELECT on the primary key, if it returns any results you should execute an UPDATE else you should execute an INSERT.
It would be best to do this in something like a Stored Procedure - you can pass the information to the stored procedure at then it can make a decision on whether to UPDATE or INSERT - this would also reduce the overhead of making several calls for your code to the database (A stored procedure would be much quicker)
AdaTheDev has indeed given the good suggestion.
But in case, you must insert/update from .NET code then you can
Create a stored procedure that will handle insert/update i.e. instead of using a direct insert query as command text, you make a call to stored proc. The SP will check if row exists or not and then update (or insert).
User TableAdapter - but this would be tedious. First you have to setup both insert & update commands. Then you have to query the database to get the existing customer numbers and then update the corresponding rows in the datatable making the Rowstate as Updated. I would rather not go this way.

Categories