Many-to-Many association with a twist - database

I am working on a Spring-MVC application in which I am designing group functionality for doing some common tasks together. So in the application, I have 2 tables, GroupAccount and GroupMembers. GroupMembers has a foreign key relation with GroupAccount. The email-address from GroupMembers is used by Spring-Security for Login. I will post the SQL code at bottom.
Currently with this architecture, A groupMember can be a part of only one GroupAccount. This is not what I want to implement. A groupMember can be a part of multiple GroupAccount. I know you will think many-to-many, but that means replicating the groupAccount or the groupMembers row, but I just want an association. So a single groupMember can be a part of Many groupAccounts and vice-versa, without creating duplicate rows as Spring-Security is involved. I hope I am making it clear. Any suggestions or ideas how to achieve this? If my post is not clear, just tell me, I will make my best effort to explain.
SQL code :
CREATE TABLE GroupAccount (
groupid NUMERIC NOT NULL,
groupName VARCHAR,
adminPassword VARCHAR,
CONSTRAINT groupid PRIMARY KEY (groupid)
);
CREATE TABLE groupmembers (
memberid NUMERIC NOT NULL,
musername VARCHAR,
mpassword VARCHAR,
groupid NUMERIC NOT NULL,
CONSTRAINT memberid PRIMARY KEY (memberid)
);
ALTER TABLE groupmembers ADD CONSTRAINT groupaccount_groupmembers_fk
FOREIGN KEY (groupid)
REFERENCES GroupAccount (groupid)
ON DELETE NO ACTION
ON UPDATE NO ACTION
NOT DEFERRABLE;
Any pointers are welcome. Thank you very much.

Unless I'm missing something, you create a junction table for a many to many relationship.
CREATE TABLE GroupAccount (
groupid NUMERIC NOT NULL,
groupName VARCHAR,
adminPassword VARCHAR,
CONSTRAINT groupid PRIMARY KEY (groupid)
);
CREATE TABLE GroupMembers (
memberid NUMERIC NOT NULL,
musername VARCHAR,
mpassword VARCHAR,
CONSTRAINT memberid PRIMARY KEY (memberid)
);
CREATE TABLE AccountMembers (
groupid NUMERIC NOT NULL,
memberid NUMERIC NOT NULL,
PRIMARY KEY (groupid, memberid),
UNIQUE INDEX (memberid, groupid)
);
I don't think the syntax is correct for AccountMembers, but I hope you get the idea.

Related

inserting into multiple related table at once and return id if exists else insert into multiple tables

I am a newbie to SQL/PostgreSQL. I wanted to insert into multiple related tables at once if certain criteria is satisfied else return the existing id of the table. Let me give an example here, following are my exemplary tables.
CREATE TABLE profile (
id serial NOT NULL,
first_name VARCHAR NOT NULL,
last_name VARCHAR NOT NULL,
PRIMARY KEY (id)
)
CREATE TABLE movies (
movied_id serial NOT NULL,
movie_name VARCHAR NOT NULL,
profile_id INTEGER NOT NULL,
PRIMARY KEY (movie_id),
FOREIGN KEY (profile_id) REFERENCES profile.id
)
CREATE TABLE sports (
sport_id serial NOT NULL,
sport_name VARCHAR NOT NULL,
profile_id INTEGER NOT NULL,
PRIMARY KEY (sport_id),
FOREIGN KEY (profile_id) REFERENCES profile.id
)
CREATE TABLE visited_countries (
country_name VARCHAR NOT NULL,
profile_id INTEGER NOT NULL,
PRIMARY KEY (country_name),
FOREIGN KEY (profile_id) REFERENCES profile.id
)
Here, profile table contains id, first_name, last_name and it is related to movies, sports, visited_countries tables. There exist one to many relation ship between the profile table and all the other tables (Foreign Keys are shown in tables as well). Given this situation:
I want to insert into all these tables at once in the order by getting the unique profile ID first and then use that as FK for all the other tables.
I wanted to check the following before inserting into all of them.
-- first_name, last_nane, and all the movies, sports, visited_countries seen by profile id should be different then only I will insert into all these table at once in the order using the generated profiled ID else just return the profile id.
I have multiple clients writing into the tables at the same time so I wanted to lock and do this transaction in one go.
I read couple of things using CTEs but couldn't get it working correctly. Any suggestion and systematic approach to do this would be helpful.

Does cakephp implicitly implement on delete cascade for BTM association

I am a beginner with cakephp and I am trying to understand it through the bookmarker app tutorial.
http://book.cakephp.org/3.0/en/quickstart.html
So here is the SQL scheme :
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
created DATETIME,
modified DATETIME
);
CREATE TABLE bookmarks (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
title VARCHAR(50),
description TEXT,
url TEXT,
created DATETIME,
modified DATETIME,
FOREIGN KEY user_key (user_id) REFERENCES users(id)
);
CREATE TABLE tags (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255),
created DATETIME,
modified DATETIME,
UNIQUE KEY (title)
);
CREATE TABLE bookmarks_tags (
bookmark_id INT NOT NULL,
tag_id INT NOT NULL,
PRIMARY KEY (bookmark_id, tag_id),
FOREIGN KEY tag_key(tag_id) REFERENCES tags(id),
FOREIGN KEY bookmark_key(bookmark_id) REFERENCES bookmarks(id)
);
So here is what I did after "bake all" ->
I went and added a user in my app and added a bookmark and tag for the user.
Now I am trying to delete the user which throws a SQL Error saying it violates foreign key constraint as bookmarks refer to a user. Makes sense.
The problem is here - I can go and delete a bookmark without any error, however in my opinion it should throw an error because bookmarks_tags refers to a bookmark and that too violates referential integrity.
So can someone please tell me how this is working internally. It is my first time with CakePHP and MVC in general and I am trying to understand the internal workings of it.
Yes by default CakePHP removes related record from the join table first if the primary record is deleted. It's done so because in most case you don't care about association if primary record itself is deleted.
You can prevent automatic removal of join table records by setting dependent option to false in association config as stated here.

Foreign key to part of primary key's table

I have a database with this tables Conversion and Client I want to create relation between this tables so ID_Send in Conversion Reference to ID in Client and ID_Receive in Conversion Reference to ID in Client
create table Conversion(ID_Send int ,
ID_Receive int ,
[Time] datetime,
[Message] varchar(2048),
primary key(ID_Send,ID_Receive,[Time])
)
create table Client (ID int IDENTITY(1,1) primary key,
[First name] varchar(500) not null,
[Last Name]varchar(500) not null,
[Birth day] datetime,
Gender bit not null,
Country varchar(200)not null,
City varchar(200) ,
[Language] varchar(200)not null,
[Chat name] varchar(500)not null ,
[Password] varchar (500)not null,
--foreign key(ID) REFERENCES Conversion (ID_Send)--there is an error
)
Motazz, there can be only one Primary key in a table like you have in the Client table. to get rid of the error:
1st create the Client table,
2nd replace the code for Conversion with:
create table Conversion(ID_Send int FOREIGN KEY REFERENCES Client(ID),
ID_Receive int FOREIGN KEY REFERENCES Client(ID),
[Time] datetime,
[Message] varchar(2048),
primary key(ID_Send,ID_Receive,[Time])
)
If you have a compound primary key (made up of mulitple columns), all your foreign keys also must use all columns of the PK to reference that table.
After all : how else would you be able to make a clear, deterministic reference from a child table to the parent? Only if you use the columns that uniquely identify one row in the parent table does this work.
The only workaround would be to put a UNIQUE index on the ID_Send and ID_Receive columns in your parent table and then reference that unique index.
But then the question is: if those values are unique - why isn't one of those columns alone your primary key??

Primary and Foreign Key at the same time

Would it be possible in SQL Server 2008 to have a table created with 2 columns that are at the same time primary and foreign keys? If yes, how would such a code look like? I've searched and came up with nothing.
Sure, no problem:
CREATE TABLE dbo.[User]
(
Id int NOT NULL IDENTITY PRIMARY KEY,
Name nvarchar(1024) NOT NULL
);
CREATE TABLE [Group]
(
Id int NOT NULL IDENTITY PRIMARY KEY,
Name nvarchar(1024) NOT NULL
);
CREATE TABLE [UserToGroup]
(
UserId int NOT NULL,
GroupId int NOT NULL,
PRIMARY KEY CLUSTERED ( UserId, GroupId ),
FOREIGN KEY ( UserId ) REFERENCES [User] ( Id ) ON UPDATE NO ACTION ON DELETE CASCADE,
FOREIGN KEY ( GroupId ) REFERENCES [Group] ( Id ) ON UPDATE NO ACTION ON DELETE CASCADE
);
This is quite commonly used to model many-to-many relations.
These are totally different constructs.
A Primary Key is used to enforce uniqueness within a table, and be a unique identifier for a certain record.
A Foreign Key is used for referential integrity, to make sure that a value exists in another table.
The Foreign key needs to reference the primary key in another table.
If you want to have a foreign key that is also unique, you could make a FK constraint and add a unique index/constraint to that same field.
For reference purposes, SQL Server allows a FK to refer to a UNIQUE CONSTRAINT as well as to a PRIMARY KEY field.
It is probably not a good idea since often you want to allow duplicate foreign keys in the table. Even if you don't now, in the future, you might, so best not to do this. See Is it fine to have foreign key as primary key?
Just a quick note - from Microsoft pages (http://msdn.microsoft.com/en-us/library/ms189049.aspx)...
"A foreign key constraint does not have to be linked only to a primary key constraint in another table; it can also be defined to reference the columns of a UNIQUE constraint in another table."
Not used often, but useful in some circumstances.

Foreign Key to multiple tables

I've got 3 relevant tables in my database.
CREATE TABLE dbo.Group
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.User
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.Ticket
(
ID int NOT NULL,
Owner int NOT NULL,
Subject varchar(50) NULL
)
Users belong to multiple groups. This is done via a many to many relationship, but irrelevant in this case. A ticket can be owned by either a group or a user, via the dbo.Ticket.Owner field.
What would be the MOST CORRECT way describe this relationship between a ticket and optionally a user or a group?
I'm thinking that I should add a flag in the ticket table that says what type owns it.
You have a few options, all varying in "correctness" and ease of use. As always, the right design depends on your needs.
You could simply create two columns in Ticket, OwnedByUserId and OwnedByGroupId, and have nullable Foreign Keys to each table.
You could create M:M reference tables enabling both ticket:user and ticket:group relationships. Perhaps in future you will want to allow a single ticket to be owned by multiple users or groups? This design does not enforce that a ticket must be owned by a single entity only.
You could create a default group for every user and have tickets simply owned by either a true Group or a User's default Group.
Or (my choice) model an entity that acts as a base for both Users and Groups, and have tickets owned by that entity.
Heres a rough example using your posted schema:
create table dbo.PartyType
(
PartyTypeId tinyint primary key,
PartyTypeName varchar(10)
)
insert into dbo.PartyType
values(1, 'User'), (2, 'Group');
create table dbo.Party
(
PartyId int identity(1,1) primary key,
PartyTypeId tinyint references dbo.PartyType(PartyTypeId),
unique (PartyId, PartyTypeId)
)
CREATE TABLE dbo.[Group]
(
ID int primary key,
Name varchar(50) NOT NULL,
PartyTypeId as cast(2 as tinyint) persisted,
foreign key (ID, PartyTypeId) references Party(PartyId, PartyTypeID)
)
CREATE TABLE dbo.[User]
(
ID int primary key,
Name varchar(50) NOT NULL,
PartyTypeId as cast(1 as tinyint) persisted,
foreign key (ID, PartyTypeId) references Party(PartyID, PartyTypeID)
)
CREATE TABLE dbo.Ticket
(
ID int primary key,
[Owner] int NOT NULL references dbo.Party(PartyId),
[Subject] varchar(50) NULL
)
The first option in #Nathan Skerl's list is what was implemented in a project I once worked with, where a similar relationship was established between three tables. (One of them referenced two others, one at a time.)
So, the referencing table had two foreign key columns, and also it had a constraint to guarantee that exactly one table (not both, not neither) was referenced by a single row.
Here's how it could look when applied to your tables:
CREATE TABLE dbo.[Group]
(
ID int NOT NULL CONSTRAINT PK_Group PRIMARY KEY,
Name varchar(50) NOT NULL
);
CREATE TABLE dbo.[User]
(
ID int NOT NULL CONSTRAINT PK_User PRIMARY KEY,
Name varchar(50) NOT NULL
);
CREATE TABLE dbo.Ticket
(
ID int NOT NULL CONSTRAINT PK_Ticket PRIMARY KEY,
OwnerGroup int NULL
CONSTRAINT FK_Ticket_Group FOREIGN KEY REFERENCES dbo.[Group] (ID),
OwnerUser int NULL
CONSTRAINT FK_Ticket_User FOREIGN KEY REFERENCES dbo.[User] (ID),
Subject varchar(50) NULL,
CONSTRAINT CK_Ticket_GroupUser CHECK (
CASE WHEN OwnerGroup IS NULL THEN 0 ELSE 1 END +
CASE WHEN OwnerUser IS NULL THEN 0 ELSE 1 END = 1
)
);
As you can see, the Ticket table has two columns, OwnerGroup and OwnerUser, both of which are nullable foreign keys. (The respective columns in the other two tables are made primary keys accordingly.) The CK_Ticket_GroupUser check constraint ensures that only one of the two foreign key columns contains a reference (the other being NULL, that's why both have to be nullable).
(The primary key on Ticket.ID is not necessary for this particular implementation, but it definitely wouldn't harm to have one in a table like this.)
Another approach is to create an association table that contains columns for each potential resource type. In your example, each of the two existing owner types has their own table (which means you have something to reference). If this will always be the case you can have something like this:
CREATE TABLE dbo.Group
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.User
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.Ticket
(
ID int NOT NULL,
Owner_ID int NOT NULL,
Subject varchar(50) NULL
)
CREATE TABLE dbo.Owner
(
ID int NOT NULL,
User_ID int NULL,
Group_ID int NULL,
{{AdditionalEntity_ID}} int NOT NULL
)
With this solution, you would continue to add new columns as you add new entities to the database and you would delete and recreate the foreign key constraint pattern shown by #Nathan Skerl. This solution is very similar to #Nathan Skerl but looks different (up to preference).
If you are not going to have a new Table for each new Owner type then maybe it would be good to include an owner_type instead of a foreign key column for each potential Owner:
CREATE TABLE dbo.Group
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.User
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.Ticket
(
ID int NOT NULL,
Owner_ID int NOT NULL,
Owner_Type string NOT NULL, -- In our example, this would be "User" or "Group"
Subject varchar(50) NULL
)
With the above method, you could add as many Owner Types as you want. Owner_ID would not have a foreign key constraint but would be used as a reference to the other tables. The downside is that you would have to look at the table to see what the owner types there are since it isn't immediately obvious based upon the schema. I would only suggest this if you don't know the owner types beforehand and they won't be linking to other tables. If you do know the owner types beforehand, I would go with a solution like #Nathan Skerl.
Sorry if I got some SQL wrong, I just threw this together.
Yet another option is to have, in Ticket, one column specifying the owning entity type (User or Group), second column with referenced User or Group id and NOT to use Foreign Keys but instead rely on a Trigger to enforce referential integrity.
Two advantages I see here over Nathan's excellent model (above):
More immediate clarity and simplicity.
Simpler queries to write.
you can also use an enum to identify whether Owner is user or group like this:
CREATE TABLE dbo.Group
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.User
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TYPE Enum_OwnerType AS ENUM ('Group', 'User');
CREATE TABLE dbo.Ticket
(
ID int NOT NULL,
Owner int NOT NULL,
OwnerType Enum_OwnerType NOT NULL,
Subject varchar(50) NULL
)
Maybe it's no better than any of proposed solutions, it might not offer any advantage. In fact, I think that this might require altering Enum_OwnerType and even ticket in order to change OwnerType, I guess... I hope it's useful anyway.
I have many cases like this and I just use polymorphic ability like below:
example
I have turnovers table that have this columns id, amount, user_id and I need to know the refrence of every records, So I just add two Fields table_id and table_type and my final turnovers table is like id, amount, user_id,table_id, table_type.
if new record is about order record inserted like this
[1,25000,2,22,order]
and if new record is about increment credit like this
[1,25000,2,23,credit]
note
if using M:M tables its take so much time two retrieve the records
and my way
Cons is turnovers table records number is grows up
Pons is more flexible in new records and readable and search ability
nathan_jr's 4th option (model an entity that acts as a base for both Users and Groups, and have tickets owned by that entity) doesn't enforce referential integrity on PartyId. You'd have to do that on the application layer which invites all sorts of trouble. Can't really call it an antipattern when django's genericforeignkey implements the same solution, but no doubt you can design something more robust and performant using your framework's orm (using something like django's Multi-table inheritance)
CREATE TABLE dbo.OwnerType
(
ID int NOT NULL,
Name varchar(50) NULL
)
insert into OwnerType (Name) values ('User');
insert into OwnerType (Name) values ('Group');
I think that would be the most general way to represent what you want instead of using a flag.

Resources