Why does liquibase references by tableName stop working? - database

I always reference a table foreign key by specifying the table name if the primary key is just "id". For example this would always work:
- column:
name: user_id
type: nvarchar(36)
constraints:
nullable: false
references: user
foreignKeyName: fk_users_user
Just referencing the production table would be enough to map the foreignKey to the correct field. However, it seems that it doesn't work for a specific case.
I have an addColumn changeSet as follows:
- changeSet:
id: SDAC-X
author: benjamin.bajic#antcolony.io
changes:
- addColumn:
columns:
- column:
name: conversation_id
type: nvarchar(36)
constraints:
nullable: false
references: conversation
foreignKeyName: fk_message_conversation
tableName: message
And for some reason this does not work. I get the UnexpectedLiquibaseException: Don't know how to find table and column names from conversation error. I know that I can fix this by specifying the column name and it does work, but I'm curious as to why it does not work in this one scenario? I reference the very same column when creating a new column in a different table in the same fashion and it runs just fine, like so:
- createTable:
tableName: conversation_message
columns:
- column:
name: id
type: nvarchar(36)
constraints:
primaryKey: true
nullable: false
- column:
name: conversation_id
type: nvarchar(36)
constraints:
nullable: false
references: conversation
foreignKeyName: fk_conversation_message_conversation
Does anyone have an idea as to why this happens?

Related

Designing a system in which user can give access to tasks

I am designing a small system in which there will be two models ( currently ).
User
Task
Users can create tasks. And can invite another user to a task and give him access as a read-only or read-write. I am able to create the models for each of them but I am not able to figure out the relationships related to user access.
here are my models
// user.entity.ts
#Entity()
#Unique(['email'])
export class User extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
email: string;
#Column()
first_name: string;
#Column()
last_name: string;
#Exclude()
#Column()
password: string;
#OneToMany(() => Task, (task) => task.user, { eager: true })
task: Task[];
}
// task.entity.ts
#Entity()
export class Task extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
content: string;
#Exclude()
#ManyToOne(() => User, (user) => user.task, { eager: false })
user: User;
#Column()
userId: number;
}
I am pretty new to databases and models. I thought of creating an entity that would have task_id, user_id, access_role as columns, but I am not able to move forward from that.
What you describe a M:M relationship between User:Task. These type relationships are typically resolved with a intersection table - your thought of another entity is dead on. In this case perhaps User_Task table. Intersection tables often contain attributes related to that combination. Here that would be the authorization the user has on the task. So here something like: (assuming user_id is PK to User table and task_id is PK to Task table and each are integers: ( Sorry I have to give just SQL as I do not know your obfuscation layer - TypeORM?)
create table user_task
( user_id integer
, task_id integer
, access text constraint access_ck check (access in ('RO', 'RW'))
, constraint user_task_pk
primary key (user_id, task_id)
, constraint user_task_2_user_fk
foreign key (user_id)
references users(user_id)
, constraint user_task_2_task_fk
foreign key (task_id)
references tasks(task_id)
);
The above describes the minimal case, now for the hard part. That involves your design:
Consider Task_1 only:
Can a user invite themself? How does the first user get assigned to a
task?
When user_a invites user_b, does user_a have any further influence or effect
on user_b to task relationship. If so what?
Can user_b now invite user_c? Anytime, or dependent on access.
What happens when user_a invites user_c and later user_b invites
user_c?
What happens in the above when the access is different.
I am sure there could be many others. The answer to above could change the table columns.

I want to have a unique field which should not be unique when combining together with another field

e.g.:
Let's say I have a table containing three fields, id, user_id & id_proof_number. Then,
Accepted:
id: 1,
user_id: 1,
id_proof_number: 123456789012,
Accepted:
id: 2,
user_id: 1,
id_proof_number: 123456789012,
Not Accepted:
id: 3,
user_id: 2,
id_proof_number: 123456789012
Can someone tell me how to do this?
In your case you have user_id and id_proof_number that should be unique by itself but not refeering both.
In your example you should implement something like this:
ALTER TABLE dbo.User
ADD CONSTRAINT uniqueFields UNIQUE (user_id, id_proof_number)
In accord with the Primary Key definition, id should be unique indipendently by other fields.

Liquibase: How do I set the autoincrement seed and increment in SQL Server

I have a table:
CREATE TABLE dbo.Courses
(
courseID BIGINT IDENTITY(-1,-1) NOT NULL,
courseName NVARCHAR(100) NOT NULL
);
I am trying to convert this to liquibase
- createTable:
tableName: Courses
columns:
- column:
name: courseID
type: bigint
autoIncrement: true
- column:
name: courseName
type: nvarchar(100)
constraints:
nullable: false
The liquibase code generates
CREATE TABLE Courses
(
courseID BIGINT IDENTITY (1, 1),
courseName NVARCHAR(100) NOT NULL
)
GO
I tried not setting autoIncrement: true and then adding addAutoIncrement after the create table but that returned:
ERROR liquibase.integration.commandline.Main - Unexpected error running Liquibase: Validation Failed:
1 changes have validation failures
addAutoIncrement is not supported on mssql, baselineTables.yml::Courses::user
Next I tried to change it manually using T-SQL:
ALTER TABLE Courses
ALTER COLUMN CourseID IDENTITY (-1, -1)
But I get a SQL Server error.
Any idea how I can get liquibase to set an identity seed and increment to anything but (1,1)?

Multi level database foreign keys

I have the following structure in my database:
Area:
----------------
Id: (PK, bigint)
AreaName: (varchar)
Post:
----------------
Id: (PK, bigint)
AreaId: (FK, bigint)
Title: (varchar)
Text: (varchar)
Comment:
----------------
Id: (PK, bigint)
PostId: (FK, bigint)
Text: (varchar)
Since I will have many scenarios where I need to query comments by Area, my question is this -
Is it better to JOIN through my Post table to get to Area for Comments, or would it be better to modify my Comment table like so:
Comment:
----------------
Id: (PK, bigint)
AreaId: (FK, bigint)
PostId: (FK, bigint)
Text: (varchar)
What are the best practices with "cascading" foreign keys instead of joining for purposes of querying? My gut tells me that this is a bad idea, but I can't deny that it would make a lot of my queries easier.
Should I be constructing a View that does this? Also, is there a term for this "cascading" foreign key concept? I had a hard time coming up with information on this despite feeling like it would be a common question.
The following question asks something similar:
Three level database - foreign keys
The answers point out that this is unnecessary (agreed), but not why (or if) it's a bad idea.
It is a very common and perfectly correct practice to cascade FKs through multiple levels of a hierarchy modeling a "containment" or "belongs to" type relationship. But you don't just add FK columns, you use compound PKs on the child tables:
Area:
----------------
AreaId: (PK, bigint)
AreaName: (varchar)
Post:
----------------
AreaId: (PK,FK, bigint)
PostId: (PK, bigint)
Title: (varchar)
Text: (varchar)
Comment:
----------------
AreaId: (PK,FK, bigint)
PostId: (PK,FK, bigint)
CommentId: (PK, bigint)
Text: (varchar)
And each table has one FK, not two. So Comment has a two-column FK referencing Post.
The only real complaint against this model is that you have to join on multiple columns, but that's just complaining about having to type. This is most efficient model, as related rows are stored together in the PK index, and the PK index supports efficient lookups and traversals of the relationships. In the single-column key model, you must have secondary indexes supporting the FKs.
Modification is not necessary. Mostly people use multiple foreign keys to create easy queries and it is definitely not a bad idea according to me.

how to configure Model Options

I want to reverse engineering a DB I create for the CakePHP framework.
So the naming convention is this one:
Tables are always named in plural.
If users belongsTo or belongsToMany users, relationships are declared like:
users owners
id
owner_id --> id
If sites hasAndBelongsToMany users, relationships are declared like
sites sites_users users
id
id <-- site_id
user_id --> id
So I would like to configure Model Options in MySQLworkbench in order to create diagram.
So I tried to set the following options but it doesn't work:
Column Defaults:
PK Column name: id PK Column Type: INT
Column name: %col Column Type: VARCHAR(45)
Foreign Key/Relationship Defaults:
FK name: %dtable%_id Column name: %dtable%_id
ON UPDATE: NO ACTION ON DELETE: NO ACTION
Associative Column name: %stable%s_%dtable%s
Is it the right way to do what I want?

Resources