How to make a foreign key in T-SQL to null - c#

I have a tableA that have foreign key to tableB:
TableA (id, tableBId, attributeA)
Now I want to insert a row into tableA but set the tableBId to NULL because at that time, I cannot determine it.
Here is my sql query :
insert into TableA values (tableId, attributeA)
tableId I have set to 0 or -1, but always receive an error :
The INSERT statement conflicted with the FOREIGN KEY
I understand this error, because 0 and -1 doesn't belong to any id of TableB. So, my question is : how can I set null to tableBId in this case?

Set the tableBId column to accept nulls
On the C# side, pass DbNull.Value for tableId value, instead of 0 or -1
edit
string sql = "INSERT INTO TableA (tableId, attributeA) values (#tableId, #attributeA)";
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.Add("#tableId", SqlDbType.Int);
cmd.Parameters["#tableId"].Value = tableId > 0 ? tableId : (object)DBNull.Value;
cmd.Parameters.Add("#attributeA", SqlDbType.VarChar);
cmd.Parameters["#attributeA"].Value = tableId;
cmd.ExecuteNonQuery();

You can set the tableId column in the designer to Allow Nulls.

You need to explicitly name the columns in your destination table, like so:
insert into TableA (id, attributeA) values (tableId, attributeA)

your should send the following instruction to your database:
ALTER TABLE TableA ALTER COLUMN tableBId bit null
this will allow null values to be stored in your tableBId field. Default value for newly created records will be Null.
If this script returns an error related to your foreign key, you'll then have to drop the foreign key constraint before running this script. In this case just
drop the constraint:
ALTER TABLE DROP CONSTRAINT myConstraintName
run the script (see before)
reset the constraint:
ALTER TABLE ADD CONSTRAINT myConstraintName foreign key(tableBId) references tableB(id)

Related

EF Core defaultValueSql how to set value from SELECT

I have DB migration where FormTemplateId - foreign key and I need to set default value as Id of the latest record in [dbo].[FormTemplates]:
migrationBuilder.AddColumn<int>(
name: "FormTemplateId",
table: "ContainerMasterTypes",
type: "int",
nullable: false,
defaultValueSql: "SELECT TOP 1 [Id] FROM [dbo].[FormTemplates] ORDER BY [Id] DESC");
and have error:
Subqueries are not allowed in this context. Only scalar expressions are allowed.'
What's wrong? How can I fix that error?
Entity Framework only allows you to do what you can do already in SQL Server. For example, with the following table setup:
create table dbo.FormTemplates (
Id int not null identity(1,1),
Name nvarchar(50) not null
);
insert dbo.FormTemplates (Name) values (N'Foo'), (N'Bar'), (N'Baz');
create table dbo.ContainerMasterTypes (
Id int not null identity(1,1),
Name nvarchar(50) not null
);
Attempting to modify the ContainerMasterTypes table to add the new column with a subquery in the default constraint as you are doing would fail...
alter table dbo.ContainerMasterTypes
add FormTemplateId int not null
constraint DF_ContainerMasterTypes_FormTemplateId
default (SELECT TOP 1 [Id] FROM [dbo].[FormTemplates] ORDER BY [Id] DESC);
... with the error message that you are seeing in .NET:
Msg 1046 Level 15 State 1 Line 3
Subqueries are not allowed in this context. Only scalar expressions are allowed.
To do this in SQL Server you would instead wrap the query in a Scalar User-Defined Function and reference that from the default constraint, i.e.:
create function dbo.LatestFormTemplateId()
returns int as
begin
return (SELECT TOP 1 [Id] FROM [dbo].[FormTemplates] ORDER BY [Id] DESC)
end
go
alter table dbo.ContainerMasterTypes
add FormTemplateId int not null
constraint DF_ContainerMasterTypes_FormTemplateId
default (dbo.LatestFormTemplateId());
Testing that with data...
insert dbo.ContainerMasterTypes (Name, FormTemplateId) values (N'Something', 1);
insert dbo.ContainerMasterTypes (Name) values (N'Else');
select * from dbo.FormTemplates;
select * from dbo.ContainerMasterTypes;
Would yield the results:
Id
Name
1
Foo
2
Bar
3
Baz
Id
Name
FormTemplateId
1
Something
1
2
Else
3
You need to do the same thing from Entity Framework as well, creating a Scalar UDF that wraps your query and then adding your new column with the default constraint referencing the UDF.

Unable to add new user claim due to constraint conflict of primary key

I have a net core web app net core 3.1 using Oracle database. The tables are generated using MS.EntityFrameworkCore.Tools ('add-migration' & 'update-database' commands).
The auto-generated tables specifically, AspNetUserClaims & AspNetRoleClaims both uses Oracle's auto identity generation.
The issue I have is with the given code, I am just not able to add a new user claim due to constraint conflict.
OracleException: ORA-00001: unique constraint (SYSTEM.PK_AspNetUserClaims) violated.
SQL for the table:
CREATE TABLE "SYSTEM"."AspNetUserClaims"
(
"Id" NUMBER(10,0) GENERATED BY DEFAULT AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE NOKEEP NOSCALE NOT NULL ENABLE,
"UserId" NVARCHAR2(450) NOT NULL ENABLE,
"ClaimType" NVARCHAR2(2000),
"ClaimValue" VARCHAR2(3000 CHAR),
CONSTRAINT "PK_AspNetUserClaims" PRIMARY KEY ("Id")
......
)
C# code:
IdentityUserClaim<string> userClaim = new IdentityUserClaim<string> { UserId = user.Id, ClaimType = "WT", ClaimValue = JsonConvert.SerializeObject(jsonWebToken) };
//// var last = await _context.UserClaims.LastOrDefaultAsync(); // dumb workaround
//// userClaim.Id = last == null ? 1 : last.Id + 1;
await _context.UserClaims.AddAsync(userClaim);
GENERATED ALWAYS, attempting to insert a value will result in error, but id is an integer with value 0 as default.
GENERATED DEFAULT, generated if no value is provided, same as above, an integer is 0 as default.
GENERATED BY DEFAULT ON NULL, integer is not nullable.
How can I proceed to create a user claim from here on?
Please ignore the mixed case, system schema, quotations or what not.
Generated by default, while providing its value manually:
SQL> create table test(id number generated by default as identity primary key, name varchar2(10));
Table created.
SQL> insert into test (id, name) values (1, 'Little');
1 row created.
SQL> insert into test (id, name) values (1, 'Foot');
insert into test (id, name) values (1, 'Foot')
*
ERROR at line 1:
ORA-00001: unique constraint (DP_4005.SYS_C001685565) violated
If you omit ID:
SQL> insert into test (name) values ('Foot');
insert into test (name) values ('Foot')
*
ERROR at line 1:
ORA-00001: unique constraint (DP_4005.SYS_C001685571) violated
Generated always; ID value provided manually:
SQL> create table test(id number generated always as identity primary key, name varchar2(10));
Table created.
SQL> insert into test (id, name) values (1, 'Little');
insert into test (id, name) values (1, 'Little')
*
ERROR at line 1:
ORA-32795: cannot insert into a generated always identity column
So - don't provide the ID, let Oracle handle it:
SQL> insert into test (name) values ('Little');
1 row created.
SQL> insert into test (name) values ('Foot');
1 row created.
SQL> select * From test;
ID NAME
---------- ----------
1 Little
2 Foot
Conclusion? Let database handle columns which are to be generated; don't provide those values manually.
Apart from that, the first line you posted:
CREATE TABLE "SYSTEM"."AspNetUserClaims"
has 2 "errors" you'd want to avoid:
SYSTEM, as well as SYS, are special users in an Oracle database, they own it and should not be used except by DBAs while maintaining the database. Creating your own objects in any of those schemas is/can be a HUGE mistake. If you do something wrong, you might destroy the database.
double quotes (Caius Jardas already commented): don't use them in Oracle. You can use any letter case you want, but object names will default to uppercase.
See a few examples:
SQL> create table TesT (nAME varCHAr2(10));
Table created.
SQL> select table_name, column_name from user_tab_columns where table_name = 'TEST';
TABLE_NAME COLUMN_NAME
-------------------- --------------------
TEST NAME
SQL> select NamE from TEst;
no rows selected
But, with double quotes, you have to match letter case every time (and, of course, use double quotes every time):
SQL> create table "TesT" ("nAME" varchar2(10));
Table created.
SQL> select table_name, column_name from user_tab_columns where table_name = 'TesT';
TABLE_NAME COLUMN_NAME
-------------------- --------------------
TesT nAME
SQL> select * From test;
select * From test
*
ERROR at line 1:
ORA-00942: table or view does not exist
SQL> select name from "test";
select name from "test"
*
ERROR at line 1:
ORA-00942: table or view does not exist
SQL> select name from "TesT";
select name from "TesT"
*
ERROR at line 1:
ORA-00904: "NAME": invalid identifier
SQL> select "nAME" from "TesT";
no rows selected
SQL>
So - forget about double quotes while in Oracle.

Return the row, when Unique key voliation happens

Is it possible to return the record, that causes the unique key violation in MSSQL, When inserting data?
Try this schema
select * from
(
--query used for your insert
) f1
where exists
(
select * from tablewhereyouwantinsert f2
where f1.key1=f2.key1 and f1.key2=f2.key2 ---- keys used into your unique key violation
)
You can use MERGE to conditionally insert or retrieve a row from the database using a single statement.
Unfortunately, to get the retrieval action, we do have to touch the existing row, I'm assuming that's acceptable and that you'll be able to construct a low impact "No-Op" UPDATE as below:
create table T (ID int not null primary key, Col1 varchar(3) not null)
insert into T(ID,Col1) values (1,'abc')
;merge T t
using (values (1,'def')) s(ID,Col1)
on t.ID = s.ID
when matched then update set Col1 = t.Col1
when not matched then insert (ID,Col1) values (s.ID,s.Col1)
output inserted.*,$action;
This produces:
ID Col1 $action
----------- ---- ----------
1 abc UPDATE
Including the $action column helps you know that this was an existing row rather than the insert of (1,def) succeeding.

Error inserting row to the sql server from linqtosql with the existance of a trigger

I created a trigger for the table below in the sql server,
Buildings: ID decimal(24, 0) PK, Name varchar(255)
The trigger is
CREATE TRIGGER [dbo].[TRG_BLD]
ON [dbo].[Building]
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO Building (Name)
SELECT Name
FROM inserted
END
All it does, only inserting the row into the table (it does more, but I'm trying to simplify my case).
When I'm inserting a row from the sql server, everything is fine,
but when I'm inserting through LinqToSql,
Building b = new Building();
b.Name = "building A";
DC.Buildings.InsertOnSubmit(b);
DC.SubmitChanges();
an exception occurs on 'SubmitChanges' saying :
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Data.Linq.dll
Additional information: The null value cannot be assigned to a member with type System.Decimal which is a non-nullable value type.
IF OBJECT_ID('dbo.Building', 'U') IS NOT NULL
DROP TABLE dbo.Building
GO
CREATE TABLE dbo.Building
(
ID INT IDENTITY(1,1) PRIMARY KEY,
Name VARCHAR(255)
)
GO
CREATE UNIQUE NONCLUSTERED INDEX ix ON dbo.Building (Name) WITH(IGNORE_DUP_KEY=ON)
GO
CREATE TRIGGER dbo.TRG_BLD
ON dbo.Building
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO dbo.Building (Name)
SELECT Name
FROM INSERTED
END
GO
INSERT INTO dbo.Building (Name)
VALUES ('a'), ('a'), ('b'), ('c'), (NULL), (NULL)
GO
SELECT *
FROM dbo.Building
output -
ID Name
----------- ------------
1 a
3 b
4 c
5 NULL

Update one database table value from another based on recently updated records

I have a scenario where i have to update record in database table from another database table based on recently updated record.
if record is new insert statement will fire
if record is updated Update statement will fire
here the problem is we don't know number of table return by query
as well as column name.
here is code
DECLARE #RowsToProcess int
DECLARE #CurrentRow int
declare #tablenames varchar(100)
DECLARE #sampleTable TABLE(RowID int not null primary key identity(1,1), tablename varchar(100),last_user_update datetime)
insert into #sampleTable SELECT [TableName] = OBJECT_NAME(object_id),last_user_update
FROM sys.dm_db_index_usage_stats
WHERE database_id = DB_ID('DATABASE')
select * from #sampleTable
SET #RowsToProcess=##ROWCOUNT
print #RowsToProcess
SET #CurrentRow=0
WHILE #CurrentRow<#RowsToProcess
BEGIN
SET #CurrentRow=#CurrentRow+1
SELECT #tablenames= tablename from #sampleTable
WHERE RowID=#CurrentRow
print #tablenames
EXEC('INSERT INTO '+ 'SM_' + #tablenames +' SELECT * FROM '+#tablenames + 'Where flag = NULL' )
END
In SQL Server, you can use triggers. SQL Server triggers can fire on insert, on update, on delete or instead of insert, etc.
You may get started using this lesson

Categories