I'm trying to work on a code that allows me to only insert records that have a different ID; if the same ID already exists in the database then the record shouldn't be inserted.
My SQL code is this:
SqlCommand cmd = new SqlCommand("insert into User(UserId,Name,Profession) values(#UserId,#Name,#Profession) WHERE NOT EXISTS (Select UserId From User where UserId = #UserId)", con);
But it doesn't work and when I try this, nothing gets added to the database.
How do I correct this query?
Thank you,
Instead of :
insert into User(UserId,Name,Profession)
values(#UserId,#Name,#Profession)
WHERE NOT EXISTS (Select UserId From User where UserId = #UserId)
use this query :
insert into User(UserId,Name,Profession)
select #UserId, #Name, #Profession
where not exists (Select UserId From User where UserId = #UserId)
Explanation : AFAIK the insert command can't combine a VALUES and a WHERE clause. But instead of directly supplying the values (using the VALUES clause), you can define a SELECT to indicate the values to insert. And on that SELECT you can now use filters, joins, etc. ..., so you can define there your filter checking that the row doesn't still exists.
Try this:
SqlCommand cmd = new SqlCommand("IF NOT EXISTS (Select UserId From User where UserId = #UserId) insert into User(UserId,Name,Profession) values(#UserId,#Name,#Profession) ", con);
This is SQL query to achieve the same.
IF NOT EXISTS (Select UserId From User where UserId = #UserId
insert into User(UserId,Name,Profession) values(#UserId,#Name,#Profession)
You can use a MERGE statement :
https://learn.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql
Or using CTE : https://learn.microsoft.com/en-us/sql/t-sql/queries/with-common-table-expression-transact-sql
Related
I'm using a stored procedure with this sql:
INSERT INTO [dbTblUsers]([strUsername], [strPassword])
VALUES (#p1,#p2);
SELECT ##IDENTITY;
And calling the procedure:
// Insert new user
nId = daUsers.InsertQuery(textBoxUsername.Text, textBoxPassword.Text);
// Insert new Twitter OAuth
daTwitterOAuth.Insert(nId, textBoxConsumerKey.Text, textBoxConsumerSecret.Text, textBoxToken.Text, textBoxTokenSecret.Text);
How do I cast the object ##IDENTITY int the int nId?
Like this?
nId = (int)daUsers.InsertQuery(textBoxUsername.Text, textBoxPassword.Text);
Firstly, don't use ##IDENTTY - use SCOPE_IDENTITY(); the first can give you unexpected answers if there are any triggers involved. Secondly; both of these return decimals; cast it at the call-site, IMO:
SELECT CAST(SCOPE_IDENTITY() as int)
I have a table DEPT, which holds 2 columns - ID, NAME.
A search form is presented with the IDs from the DEPT table and the user can chose any number of IDs and submit the form, to get the related NAMEs.
Clarification/Inputs:
I don't want to build a dynamic query - its not manageable.
I prefer a stored procedure using table-valued parameters
Any other solutions to proceed?
NOTE:
This example is simple with 1 table - in real life, I have to deal with more than 6 tables!
Thanks for any suggestions
CREATE TYPE dbo.DeptList
AS TABLE
(
ID INT
);
GO
CREATE PROCEDURE dbo.RetrieveDepartments
#dept_list AS dbo.DeptList READONLY
AS
BEGIN
SET NOCOUNT ON;
SELECT Name FROM dbo.table1 WHERE ID IN (SELECT ID FROM #dept)
UNION ALL
SELECT Name FROM dbo.table2 WHERE ID IN (SELECT ID FROM #dept)
-- ...
END
GO
Now in your C# code, create a DataTable, fill it in with the IDs, and pass it in to the stored procedure. Assuming you already have a list called tempList and the IDs are stored in id:
DataTable tvp = new DataTable();
tvp.Columns.Add(new DataColumn("ID"));
foreach(var item in tempList)
{
tvp.Rows.Add(item.id);
}
using (connObject)
{
SqlCommand cmd = new SqlCommand("StoredProcedure", connObject);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter tvparam = cmd.Parameters.AddWithValue("#dept_list", tvp);
tvparam.SqlDbType = SqlDbType.Structured;
...
}
You can also use a split function. Many exist, this is the one I like if you can guarantee that the input is safe (no <, >, & etc.):
CREATE FUNCTION dbo.SplitInts_XML
(
#List VARCHAR(MAX),
#Delimiter CHAR(1)
)
RETURNS TABLE
AS
RETURN
(
SELECT Item = y.i.value('(./text())[1]', 'int')
FROM
(
SELECT x = CONVERT(XML, '<i>'
+ REPLACE(#List, #Delimiter, '</i><i>') + '</i>').query('.')
) AS a
CROSS APPLY x.nodes('i') AS y(i)
);
GO
Now your procedure can be:
CREATE PROCEDURE dbo.RetrieveDepartments
#dept_list VARCHAR(MAX)
AS
BEGIN
SET NOCOUNT ON;
;WITH d AS (SELECT ID = Item FROM dbo.SplitInts(#dept_list, ','))
SELECT Name FROM dbo.table1 WHERE ID IN (SELECT ID FROM d)
UNION ALL
SELECT Name FROM dbo.table2 WHERE ID IN (SELECT ID FROM d)
-- ...
END
GO
I need to turn this query into an update statement. I will have to update the values from fields. Everything is already in place but the update statement.
Here is the select version of the query:
SELECT i.GoLiveDate, i.FirstBonusRun, i.TechFName, i.TechLName, i.TechEmail, i.TechPhone, i.WebISPFName, i.WebISPLName,
i.WebISPEmail, i.WebISPPhone, i.FullFillFName, i.FullFillLName, i.FullFillEmail, i.FullFillPhone, d.FName,
d.LName, d.HomePhone, d.Email
FROM NC_Information i
INNER JOIN Distributor d
ON d.DistID = i.ClientID
WHERE clientID = #value
Is it possible to update two different tables from within the same query?
Here is the code I have so far:
public void Update (int ClientID)
{
using ( var conn = new SqlConnection( GeneralFunctions.GetConnectionString() ) )
using ( var cmd = conn.CreateCommand() )
{
conn.Open();
cmd.CommandText =
#"SELECT i.GoLiveDate, i.FirstBonusRun, i.TechFName, i.TechLName, i.TechEmail, i.TechPhone, i.WebISPFName, i.WebISPLName,
i.WebISPEmail, i.WebISPPhone, i.FullFillFName, i.FullFillLName, i.FullFillEmail, i.FullFillPhone, d.FName,
d.LName, d.HomePhone, d.Email
FROM NC_Information i
INNER JOIN Distributor d
ON d.DistID = i.ClientID
WHERE clientID = #value";
cmd.Parameters.AddWithValue( "#value", ClientID );
cmd.ExecuteNonQuery();
}
}
You can't update multiple tables in one statement, but you can use a transaction to make sure that the updates are contingent upon one another:
BEGIN TRANSACTION
UPDATE SomeTable
SET SomeColumn = 'Foo'
WHERE SomeID = 123
UPDATE AnotherTable
SET AnotherColumn = 'Bar'
WHERE AnotherID = 456
COMMIT
I think, you cannot directly do the update on two tables. But you can Optimize the query.
How?
OUTPUT keyword in Insert/Update/Delete Statement
The first Update Statement's Select Data(filtered data) can be reused using the below mentioned example.
CREATE TABLE #table1
(
id INT,
employee VARCHAR(32)
)
go
INSERT INTO #table1 VALUES
(1, 'name1')
,(2, 'name2')
,(3, 'name3')
,(4, 'name4');
GO
DECLARE #GuestTable TABLE
(
id INT,
employee VARCHAR(32)
);
update #table1
Set id = 33
OUTPUT inserted.* INTO #GuestTable
Where id = 3
The Data in the '#GuestTable' Table is filtered data and can be
reused.
select * from #GuestTable
drop table #table1
Alternatively, you can create a dataset with two datatables, and let the tableadaptermanager manage the updates.
I am working on a web application where there are many tables but two will suffice to illustrate my problem:
User
Order
Let us say that the User table has a primary key "UserID", which is a foreign key in the Order table called "CreatedBy_UserID".
Before deleting a User, I would like to check if the Order table has a record created by the soon-to-be deleted user.
I know that a SqlException occurs if I try to delete the user but let us say that I want to check beforehand that the Order table does not have any records created by this user? Is there any SQL code which I could run which will check all foreign keys of a table if that row is being referenced?
This for me is generally useful code as I could remove the option for deletion altogether if it can be detected that the user exists in these other tables.
I don't want a simple query (SELECT COUNT(*) FROM Order WHERE CreatedBy_UserID == #userID) because this will not work if I create another foreign key to the Order table. Instead I want something that will traverse all foreign keys.
Can this be done?
Below is code for an sp that I've used in the past to perform this task (please excuse the indenting):
create proc dbo.usp_ForeignKeyCheck(
#tableName varchar(100),
#columnName varchar(100),
#idValue int
) as begin
set nocount on
declare fksCursor cursor fast_forward for
select tc.table_name, ccu.column_name
from
information_schema.table_constraints tc join
information_schema.constraint_column_usage ccu on tc.constraint_name = ccu.constraint_name join
information_schema.referential_constraints rc on tc.constraint_name = rc.constraint_name join
information_schema.table_constraints tc2 on rc.unique_constraint_name = tc2.constraint_name join
information_schema.constraint_column_usage ccu2 on tc2.constraint_name = ccu2.constraint_name
where tc.constraint_type = 'Foreign Key' and tc2.table_name = #tableName and ccu2.column_name = #columnName
order by tc.table_name
declare
#fkTableName varchar(100),
#fkColumnName varchar(100),
#fkFound bit,
#params nvarchar(100),
#sql nvarchar(500)
open fksCursor
fetch next from fksCursor
into #fkTableName, #fkColumnName
set #fkFound = 0
set #params=N'#fkFound bit output'
while ##fetch_status = 0 and coalesce(#fkFound,0) <> 1 begin
select #sql = 'set #fkFound = (select top 1 1 from [' + #fkTableName + '] where [' + #fkColumnName + '] = ' + cast(#idValue as varchar(10)) + ')'
print #sql
exec sp_executesql #sql,#params,#fkFound output
fetch next from fksCursor
into #fkTableName, #fkColumnName
end
close fksCursor
deallocate fksCursor
select coalesce(#fkFound,0)
return 0
end
This will select a value of 1 if a row has any foreign key references.
The call you would need would be:
exec usp_ForeignKeyCheck('User','UserID',23)
There is no clean way to iterate through all FK columns where multiple exist. You'd have to build some dynamic SQL to query the system tables and test each in turn.
Personally, I wouldn't do this. I know what FKs I have: I'll test each in turn
...
IF EXISTS (SELECT * FROM Order WHERE CreatedBy_UserID == #userID)
RAISERROR ('User created Orders ', 16, 1)
IF EXISTS (SELECT * FROM Order WHERE PackedBy_UserID == #userID)
RAISERROR ('User packed Orders', 16, 1)
...
You wouldn't dynamically iterate through each property of some user object and generically test each one would you? You'd have code for each property
This code will give you a list of the foreign keys which are defined for a specifit table:
select distinct name from sys.objects
where object_id in ( select constraint_object_id from sys.foreign_key_columns as fk
where fk.Parent_object_id = (select object_id from sys.tables
where name = 'tablename') )
You can use transaction to check it.
I know it seems like stone ax, but it working fast and stable.
private bool TestUser(string connectionString, int userID)
{
var result = true;
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
var command = connection.CreateCommand();
var transaction = connection.BeginTransaction();
command.Connection = connection;
command.Transaction = transaction;
try
{
command.CommandText = "DELETE User WHERE UserID = " + userID.ToString();
command.ExecuteNonQuery();
transaction.Rollback();
}
catch
{
result = false;
}
}
return result;
}
i am trying to insert a statement contains WHERE from two different tables :
the table i want to insert into is dbo.order
the other two tables are :
dbo.users. user_id.
dbo.packages. package_id.
another order field "notes".
the statement i tried is
insert into dbo.order
(customer_id,package_id,notes)
Select user_id,Package_ID
from
dbo.users,dbo.packages
where
username = 'bader' AND package_name = 'beginner','notes value here';
any suggestions ?
There's no obvious join here so you'll get the cartesian product of Bader orders and beginner packages. Not sure what the notes value should be. If its a literal you can just include it in the select clause.
insert into dbo.order
(customer_id,package_id,notes)
Select
user_id,Package_ID , 'notes value here'
from
dbo.users,dbo.packages
where
username = 'bader' AND package_name = 'beginner';
insert into dbo.order
(customer_id,package_id,notes)
Select user_id, Package_ID, 'notes value here'
from
dbo.users, dbo.packages
where
username = 'bader' AND package_name = 'beginner';