NHibernate Mapping a Table Were the Primary Key is Also a Foreign Key - sql

I have 2 tables as follows:
create table Users
(
UserId int primary key identity not null
)
create table UserExternalKeys
(
UserIdRef int primary key not null,
ExternalKey varchar(50) unique not null
)
alter table UserExternalKeys
add constraint fk_UsersExternalKeys_Users
foreign key (UserIdRef)
references Users (UserId)
Each user can have a 0 or 1 external keys. Things are setup this way because adding a nullable unique column to SQL Server does not allow for more than 1 null value.
Based on Ayende's post, it seems like this could be handled using a <one-to-one> mapping. However, this would require the UserExternalKeys table to have its own primary key.
The new schema would look something like this:
create table Users
(
UserId int primary key identity not null,
ExternalKeyRef int null
)
create table UserExternalKeys
(
UserExternalKeyId int primary key identity not null,
ExternalKey varchar(50) unique not null
)
alter table Users
add constraint fk_Users_UsersExternalKeys
foreign key (ExternalKeyRef)
references UserExternalKeys (UserExternalKeyId)
I think this would work, but it feels like I would only be adding the UserExternalKeyId column to appease NHibernate.
Any suggestions?

If a user can have 0 or 1 external keys why not design the tables as:
create table Users
(
UserId int primary key identity not null
ExternalKey varchar(50) null
)
and use one of the known workarounds for this problem. If you're using SQL Server 2008 you can use a filtered index. If you're using an earlier version you can use a trigger, an indexed view (2005), or the nullbuster workaround.
You could also keep your original schema and map the relationship as one-to-many from Users to UserExternalKeys. Map the collection as a private member and expose access to it through a property:
private IList<UserExternalKeys> _externalKeys;
public string ExternalKeys
{
get
{
if (_externalKeys.Count() == 1)
{
return _externalKeys.ElementAt(0).ExternalKey;
}
else
{
// return null or empty string if count = 0, throw exception if > 1
}
}
set
{
if (_externalKeys.Count() == 0) { // add key and set value }
else { // set value if count = 1, throw exception if > 1 }
}
}

Related

How to update the nested tables in sql using gorm?

Here the code is written in Go. I am using two tables where one table has a foreign key that refers to the other table's primary key. Let's say I have a database as following struct defined:
type User struct{
ID uint `gorm:"primary_key;column:id"`
Name string `gorm:"column:name"`
Place place
PlaceID
}
type Place struct{
ID uint `gorm:"primary_key;column:id"`
Name string `gorm:"column:name"`
Pincode uint `gorm:"column:pincode"`
}
And the sql schema is:
create table place(
id int(20) NOT NULL AUTO_INCREMENT,
name varchar(100) NOT NULL,
pincode uint(20) NOT NULL,
PRIMARY KEY (id),
)
create table user(
id int(20) NOT NULL AUTO_INCREMENT,
name varchar(100) NOT NULL,
place_id uint(20) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (place_id) REFERENCES place(id)
)
Now while inserting in user by gorm as:
place := Place{Name:"new delhi",Pincode:1234}
user := User{Name: "sam", Age: 15, Place: place}
err = db.Debug().Create(&user).Error
//It inserts to both user and place table in mysql
//now while updating to name in user table as Samuel and place as
//following
place := Place{Name:"mumbai",Pincode:1234}
err = db.Debug().Model(&User{}).Where("id =?",
1,).Update(&user{Name:"Samuel",Place:place}).Error
It updates the row in user table but creates a new row in place table.But it should update the matching row in place table and not create a new one
Is there any way to do it? Here I am not using auto migrate function to create db tables.
The answer to your question should be sought in a relations or Association Mode.
The example below shows how to add new associations for many to many, has many, replace current associations for has one, belongs to
db.Model(&user).Association("Place").Append(Place{Name:"mumbai",Pincode:1234})
Or you can replace current associations with new ones:
db.Model(&user).Association("Place").Replace(Place{Name:"mumbai",Pincode:1234},Place{Name:"new delhi",Pincode:1234})
Probably It's creating a new row because you didn't set the ID on Place{Name:"mumbai",Pincode:1234}.

User permission hierarchy in DB

I am trying to add a permission hierarchy for users e.g. child, parent, admin
My idea is to create a generic user table with usernames and passwords and accessibility which would be from 0 to 2 , 0 - child 1 - parent 2 - admin, but I do not know how to connect the user table to parent/child table since they have different variables.
Access picture of my database right now
To be clear parent/child wouldn't have username/password like in this picture anymore with user table.
Update:
DB using only one table for users
This table would keep the fields that are only for parents empty if this is a child etc. I would want feedback if the variable 'accessibility' makes sense that would be value from 0 to 2 which would allow me in code to check if it is a parent child or admin
What you want is a kind-of table inheritance that prevents two derived entities from sharing the same supertype instance.
Databases like Access and MS SQL Server do not support table-inheritance like how PostgreSQL does, but you can fake it:
TABLE Users (
UserId int PRIMARY KEY IDENTITY(1,1),
UserName nvarchar(50) NOT NULL,
PasswordHash binary(32) NOT NULL, -- assuming sha256
PasswordSalt binary(8) NOT NULL
)
TABLE Parents (
UserId int NOT NULL PRIMARY KEY,
-- insert parent-specific fields here
FOREIGN KEY UserId REFERENCES Users ( UserId )
)
TABLE Children (
UserId int NOT NULL PRIMARY KEY,
-- insert child-specific fields here
FOREIGN KEY UserId REFERENCES Users ( UserId )
)
This schema means that a Parent or Children entity cannot exist without a single specific User entity also existing. However because Access does not support true table-inheritance it cannot easily constrain UserId values such that only 1 Parent or Children row can have that value (i.e. there can be a Parent AND a Child that share the same UserId value).
Fortunately there's a hack that is mathematically correct (as far as relational-algebra is concerned) but which unfortunately breaks aspects of OOP in that the superclass (User) is now aware of it subclasses - but this might be desirable is certain circumstances...
...anyway. the trick is to add an enum value to User's primary key (so it's a composite-key) which identifies a singular subclass, then add a constant (enforced via CHECK CONSTRAINT) composite-key component to match in each "derived" table, like so (using pseudo-SQL - the relational-algebra is portable, but concepts like enums and check-constraints don't necessarily port to MS Access very well):
ENUM UserType ( HumanParent = 1, HumanChild = 2, Other = 3 )
TABLE Users (
UserId int IDENTITY(1,1),
UserType UserType NOT NULL,
UserName nvarchar(50) NOT NULL,
PasswordHash binary(32) NOT NULL, -- assuming sha256
PasswordSalt binary(8) NOT NULL
PRIMARY KEY ( UserId, UserType )
)
TABLE Parents (
UserId int NOT NULL,
UserType UserType NOT NULL,
-- insert parent-specific fields here
PRIMARY KEY ( UserId, UserType )
FOREIGN KEY ( UserId, UserType ) REFERENCES Users ( UserId, UserType )
CHECK CONSTRAINT UserType = 1
)
TABLE Children (
UserId int NOT NULL,
UserType UserType NOT NULL,
-- insert child-specific fields here
PRIMARY KEY ( UserId, UserType )
FOREIGN KEY ( UserId, UserType ) REFERENCES Users ( UserId, UserType )
CHECK CONSTRAINT UserType = 2
)
So at the cost of slight inefficiency (i.e. the extra storage needed for the UserType columns - and computational expense of evaluating the CHECK constraints) you gain guarantees of the correctness of your data.
Now have fun porting that to Access :)

SQL Server : create a foreign key with a condition

I'm designing a new database for a company, trying to keep strict constraints with foreign keys etc for integrity. I have a table [Member] which holds companies on the system. This table has a column of [internalContact] for the user in our company who deals with this member which has a foreign linked to the users table by user id.
What I would like to know is if it is possible to assign a condition to the foreign key, since the users table contains internal and external users. ie. for the field to only accept a user id where the user type is 5. Can this be done, or can I only control this in my application code?
Thanks
You can use a check constraint for this.
(The code is untested some syntax errors will be in there)
CREATE TABLE Member
(
P_Id int NOT NULL,
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Address varchar(255),
City varchar(255),
InternalContactId
CONSTRAINT chk_Person CHECK (isInternalUser(internalContactId) > 0)
)
ALTER TABLE Member
ADD FOREIGN KEY (InternalContacId)
REFERENCES Persons(P_Id)
Then just create a function isInternalUser that returns 1 if user in ok to be an internal contact
CREATE FUNCTION isInternalUser ( #userId int(10) )
RETURNS int
AS
BEGIN
DECLARE #tmp int
SELECT #tmp = count(*)
FROM users
WHERE userId = #UserId and <check to see if user is internal>
RETURN(#CtrPrice)
END
GO

Entity Framework to Auto Increment Non Identity Column / Concurrency Issue

I have two tables:
CREATE TABLE [dbo].[Customers] (
[CustomerId] INT IDENTITY (1, 1) NOT NULL,
CONSTRAINT [PK_dbo.Customers] PRIMARY KEY CLUSTERED ([CustomerId] ASC)
);
CREATE TABLE [dbo].[Campaigns] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[CustomerId] INT NULL,
[CampaignId] INT NULL,
CONSTRAINT [PK_dbo.Campaigns] PRIMARY KEY CLUSTERED ([Id] ASC)
);
Campaigns.CampaignId is unique to every CustomerId; therefore, it cannot be an identity. So from my web app, I need to auto increment the CampaignId upon Campaign creation. In the past, I've had to obtain a lock in a single transaction to obtain the next highest and issue an insert. How would I accomplish the same thing in EF without having to worry about or effectively manage concurrency?
In the Campaign controller, I have this (UserContext is a static helper class that retrieves the user's current CustomerId and db is my DbContext):
public ActionResult Create(Campaign campaign)
{
if (ModelState.IsValid)
{
int customerId = UserContext.customerId;
int maxCampaignId = db.Campaigns.Where(c => c.CustomerId == customerId).Max(c => c.CampaignId);
campaign.CampaignId = maxCampaignId + 1;
db.Campaigns.Add(campaign);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(campaign);
}
But wouldn't this risk duplicate values per CustomerId in a high concurrency environment?
EDIT:
I forgot to mention that a guid was not an option. The Ids have to be integers.
EDIT 2:
I forgot to mention that there is also a Users table which can have the same CustomerId. Users can create multiple Campaigns with the same CustomerId which causes the potential for concurrency issues.
You may want to look into a HiLo pattern, or just use Guid.NewGuid() instead of incrementing.
See: HiLO for the Entity Framework
What's the Hi/Lo algorithm?

Primary key consists of a foreign key and a identity and should reset identity under a condition

create table Linq_TB
{
url_id int NOTNULL,
Pg_Name nvarchar(50) NOTNULL,
URL nvarchar(50) NUTNULL,
CONSTRAINT Linq_id PRIMARY KEY (url_id,DBCC Checkident(Linq_TB,RESEED,0) case url_id not in(select URL_Id from URL_TB ))
}
I want to make a table which it's primary key is Linq_id and gets it's value from both the url_id and identity with start from 1 and increments 1 by 1. url_id is a foreign key. For example if url_id is 1, linq_id's will be 11, 12, 13,... and I also want to reset linq_id identity when the url_id changes.
What should the query be? The query above doesn't work, why?
Thanks in advance
Well, a constraint contains conditions and not code to be executed. You should consider using a stored procedure for your task and also a homegrown method of assigning IDs.
However, it is not a common practice to have your primary keys 'pretty' or formatted, as there is no real benefit (except maybe for debugging purposes maybe).
I do not recommend executing DBCC whenever your url_ID changes. This has a great negative impact on performance.
Why don't you leave the IDs like they are?
You can do this with the following table and trigger definitions:
CREATE TABLE Linq_TB
(
url_id INT NOT NULL,
Linq_id INT NOT NULL,
Pg_Name NVARCHAR(50) NOT NULL,
URL NVARCHAR(50) NOT NULL,
CONSTRAINT PK_Link_TB PRIMARY KEY (url_id, Linq_id),
CONSTRAINT FK_URL_TB_URL_ID FOREIGN KEY (url_id) REFERENCES URL_TB (url_id)
)
GO
CREATE TRIGGER tr_Linq_TB_InsertUpdate
ON Linq_TB
INSTEAD OF INSERT
AS
INSERT INTO Linq_TB
SELECT i.url_id,
ISNULL(tb.Linq_id, 0)
+ row_number() over (partition by i.url_id order by (select 1)),
i.Pg_Name, i.URL
FROM inserted i
LEFT OUTER JOIN
(
SELECT url_id, MAX(Linq_ID) Linq_id
FROM Linq_TB
GROUP BY url_id
) tb ON i.url_id = tb.url_id
GO
The CREATE TABLE defines your columns and constraints. And, the trigger creates the logic to generate a sequence value in your Linq_id column for each url_id.
Note that the logic in the trigger is not complete. A couple of issues are not addressed: 1) If the url_id changes for a row, the trigger doesn't update the Link_id, and 2) deleting rows will lead to gaps in the Linq_TB column sequence.

Categories