Is it possible to make a polymorphic relationship in Yii2? - sql

There are 4 tables - it can be simplified as follows (PostgresQL database)
CREATE TABLE PRODUCTS(
ID INT NOT NULL,
NAME VARCHAR (20) NOT NULL,
TYPE_PRODUCT INT NOT NULL,
PRIMARY KEY (ID)
);
CREATE TABLE FOODS (
ID INT NOT NULL,
NAME VARCHAR (20) NOT NULL,
STATUS VARCHAR (50)
PRIMARY KEY (ID)
);
CREATE TABLE CLOTHES (
ID INT NOT NULL,
NAME VARCHAR (20) NOT NULL,
STATUS VARCHAR (50)
PRIMARY KEY (ID)
);
CREATE TABLE CLEANERS (
ID INT NOT NULL,
NAME VARCHAR (20) NOT NULL,
STATUS VARCHAR (50)
PRIMARY KEY (ID)
);
The meaning is this - the goods arrive at the warehouse and are registered in the first table. After acceptance and registration, the manager sends the goods, depending on the type, to the departments - Food, Clothing, Cleaning products. A product record is created in the corresponding table. That is, it is copied from the first table. Here I presented it in a simplified way, in fact, there are a lot of fields in the tables and there are different fields in each department. Therefore, apparently such a structure was made. But there is a status field in these tables. Is it possible to make it possible to display the calculated STATUS field from 2, 3, 4 tables when displaying a list of goods from the first main table.
I'm thinking of adding the EXTERNAL_ID field to the main table (where to write the id from the table where the record was copied) and using a polymorphic relationship (depending on TYPE_PRODUCT) pull out this Status field.
How can this be done in Yii2? On Laravel, this is easy to do - there are built-in tools. You only need to pull out this field when displaying the list (sorting) - no restrictions are required. This field will not be changed from the main table
I accept that all this could be put into one table or the status field could be made into the main table. But there is a problem with access rights. Managers in departments can neither view nor edit the master table. All this has been used for a very long time and no one will allow much change in the structure. You can only add a field to the main table

It is possible and really easy to implement.
Since you have your Product model you can define in it a relations that calls that specific model.
Product:
...
//Function to get a status of the Foods
public function getFoods()
{
return $this->hasOne(Foods::className(), ['foods_id' => 'id']);
}
//And so on on the rest of the tables
Now when you get your product in the table you can do as simple as that:
//$product Product model
$product->foods->status // It will return your food status.

Related

Why would a database architect choose to de-normalize referenced child tables

Why would a DBA choose to have a large, heavily referenced lookup table instead of several small, dedicated lookup tables with only one or two tables referencing each one. For example:
CREATE TABLE value_group (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
group_name VARCHAR(30) NOT NULL
);
CREATE TABLE value_group_value (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
value_group_id INT NOT NULL,
value_id INT NOT NULL,
FOREIGN KEY (value_group_id) REFERENCES value_group(id)
);
CREATE TABLE value (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
value_text VARCHAR(30) NOT NULL
);
Example groups would be something along the lines of:
'State Abbreviation' with the corresponding values being a list of all the U.S. state abbreviations.
'Name Prefix' with the corresponding values being a list of strings such as 'Mr.', 'Mrs.', 'Dr.', etc.
In my experience normalizing these value tables into tables for each value_group would make changes easier, provides clarity, and queries perform faster:
CREATE TABLE state_abbrv (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
abbreviation CHAR NOT NULL
);
CREATE TABLE name_prefix (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
prefix VARCHAR NOT NULL
);
With n tables like that for n groups in the value_group table. Each of these new tables could then be directly referenced from another table or using some intermediary table depending on the desired relationship.
What factors would influence a DBA to use the described the first setup over the second?
In my experience, the primary advantages of a single, standardized "table of tables" structure for lookups are code reuse, simplified documentation (if you're in the 1% of folks who document your database, that is) and you can add new lookup tables without changing the database structure.
And if I had a dollar for every time I saw something in a database that made me wonder "what was the DBA thinking?", I could retire to the Bahamas.

How do I realize this 3 simple things in SQL?

i am an absolute SQL beginner and i already did search a lot with google but didnt find what i needed. So how do i realize in SQL(translated from a ER-Model):
An Entity having an Atribute that can have mulitple Entrys(I already found the ARRAY contstraint but i am unsure about that)
An Entity having an Atribute that consists itself of a few more Atributes(Picture: http://static3.creately.com/blog/wp-content/uploads/2012/03/Attributes-ER-Diagrams.jpeg)
Something like a isA Relation. And especially the total/partial and the disjunct characteristics.
Thanks already
In Postgres you have all three of this:
create table one
(
id integer primary key,
tags text[] -- can store multiple tags in a single column
);
A single column with multiple attributes can be done through a record type:
create type address as (number integer, street varchar(100), city varchar(100));
create table customer
(
id integer primary key,
name varchar(100) not null,
billing_address address
);
An isA relation can be done using inheritance
create table paying_customer
(
paid_at timestamp not null,
paid_amount decimal(14,2) not null
)
inherits (customer);
A paying customer has all attributes of a customer plus the time when the invoice was paid.

How to design relation between tables employee,client and phone Number?

I have a relational database with a Client table, containing id, name, and address, with many phone numbers
and I have an Employee table, also containing id, name, address, etc., and also with many phone numbers.
Is it more logical to create one "Phone Number" table and link the Clients and Employees, or to create two separate "Phone Number" tables, one for Clients and one for Employees?
If I choose to create one table, can I use one foreign key for both the Client and Employee or do I have to make two foreign keys?
If I choose to make one foreign key, will I have to make the Client ids start at 1 and increment by 5, and Employee ids start at 2 and increment by 5 so the two ids will not be the same?
If I create two foreign keys will one have a value and the other allow nulls?
The solution which I would go with would be:
CREATE TABLE Employees (
employee_id INT NOT NULL,
first_name VARCHAR(30) NOT NULL,
...
CONSTRAINT PK_Employees PRIMARY KEY (employee_id)
)
CREATE TABLE Customers (
customer_id INT NOT NULL,
customer_name VARCHAR(50) NOT NULL,
...
CONSTRAINT PK_Customers PRIMARY KEY (customer_id)
)
-- This is basic, only supports U.S. numbers, and would need to be changed to
-- support international phone numbers
CREATE TABLE Phone_Numbers (
phone_number_id INT NOT NULL,
area_code CHAR(3) NOT NULL,
prefix CHAR(3) NOT NULL,
line_number CHAR(4) NOT NULL,
extension VARCHAR(10) NULL,
CONSTRAINT PK_Phone_Numbers PRIMARY KEY (phone_number_id),
CONSTRAINT UI_Phone_Numbers UNIQUE (area_code, prefix, line_number, extension)
)
CREATE TABLE Employee_Phone_Numbers (
employee_id INT NOT NULL,
phone_number_id INT NOT NULL,
CONSTRAINT PK_Employee_Phone_Numbers PRIMARY KEY (employee_id, phone_number_id)
)
CREATE TABLE Customer_Phone_Numbers (
customer_id INT NOT NULL,
phone_number_id INT NOT NULL,
CONSTRAINT PK_Customer_Phone_Numbers PRIMARY KEY (customer_id, phone_number_id)
)
Of course, the model might changed based on a lot of different things. Can an employee also be a customer? If two employees share a phone number how will you handle it on the front end when the phone number for one employee is changed? Will it change the number for the other employee as well? Warn the user and ask what they want to do?
Those last few questions don't necessarily affect how the data is ultimately modeled, but will certainly affect how the front-end is coded and what kind of stored procedures you might need to support it.
"The Right Way", allowing you to use foreign keys for everything, would be to have a fourth table phoneNumberOwner(id) and have fields client.phoneNumberOwnerId and employee.phoneNumberOwnerId; thus, each client and each employee has its own record in the phoneNumberOwner table. Then, your phoneNumbers table becomes (phoneNumberOwnerId, phoneNumber), allowing you to attach multiple phone numbers to each phoneNumberOwner record.
Maybe you can somehow justify it, but to my way of thinking it is not logical to have employees and clients in the same table. It seems you wan to do this only so that your foreign keys (in the telephone-number table) all point to the same table. This is not a good reason for combining employees and clients.
Use three tables: employees, clients, and telephone-number. In the telephone table, you can have a field that indicates employee or client. As an aside, I don't see why telephone number needs to be a foreign key: that only adds complexity with very little benefit, imo.
Unless there are special business requirements I would expect a telephone number to be an attribute of an employee or client entity and not an entity in its own right.
If it were considered an entity in its own right it would be 'all key' i.e. its identifier is the compound of its attributes and has no attributes other than its identifier. If the sub-attributes aren't stored apart then it only has one attribute i.e. the telephone number itself! Therefore, it isn't usually 'interesting' enough to be an entity in its own right and a telephone numbers table, whether superclass or subclass, is usually overkill (as I say, barring special business requirements).

SQL database design for storing different types of "Person"

I need to create a table in a relational database using SQL for persons having the columns Name, LastName and so on.
I'll have three different kinds of People: Seller, Buyer and Customer.
Every person has other information/attributes.
Do I need to create a table for each different type of Person or can a single table be used for all three types?
If I used a single table, what if one type of "Person", say Seller, has different attributes from another Person type?
I would create one table Person , with personId as primary key that will contain common properties for all types.(Seller , Buyer , Customer)
Then I would create PersonTypes, a small reference table , that will declare codes for the different types .
Then for each type I would create a separate table with reference to Person table and
PersonType table that includes all the unique properties.
You can create 1 or two tables. Of course you can create 3 tables for each user role. It depend's on what you would like to achieve. Common solution to your question is: create two tables, one for users and one for their roles (examples for mysql:
Create table `person_role` (
id int not null,
roleName varchar(255) not null,
PRIMARY KEY(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
Create table `person`(
id int not null.
name varchar(255) not null,
lastName varchar(255) not null,
role int not null,
PRIMARY KEY(`id`),
CONSTRAINT FOREIGN KEY(`role`) REFERENCES person_role(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
Can a seller ever be a buyer or a customer? If they can, put it all in the same table so that you don't have separate copies of the same data.

How do you store business activities in a SQL database?

The goal is to store activities such as inserting, updating, and deleting business records.
One solution I'm considering is to use one table per record to be tracked. Here is a simplified example:
CREATE TABLE ActivityTypes
(
TypeId int IDENTITY(1,1) NOT NULL,
TypeName nvarchar(50) NOT NULL,
CONSTRAINT PK_ActivityTypes PRIMARY KEY (TypeId),
CONSTRAINT UK_ActivityTypes UNIQUE (TypeName)
)
INSERT INTO ActivityTypes (TypeName) VALUES ('WidgetRotated');
INSERT INTO ActivityTypes (TypeName) VALUES ('WidgetFlipped');
INSERT INTO ActivityTypes (TypeName) VALUES ('DingBatPushed');
INSERT INTO ActivityTypes (TypeName) VALUES ('ButtonAddedToDingBat');
CREATE TABLE Activities
(
ActivityId int IDENTITY(1,1) NOT NULL,
TypeId int NOT NULL,
AccountId int NOT NULL,
TimeStamp datetime NOT NULL,
CONSTRAINT PK_Activities PRIMARY KEY (ActivityId),
CONSTRAINT FK_Activities_ActivityTypes FOREIGN KEY (TypeId)
REFERENCES ActivityTypes (TypeId),
CONSTRAINT FK_Activities_Accounts FOREIGN KEY (AccountId)
REFERENCES Accounts (AccountId)
)
CREATE TABLE WidgetActivities
(
ActivityId int NOT NULL,
WidgetId int NOT NULL,
CONSTRAINT PK_WidgetActivities PRIMARY KEY (ActivityId),
CONSTRAINT FK_WidgetActivities_Activities FOREIGN KEY (ActivityId)
REFERENCES Activities (ActivityId),
CONSTRAINT FK_WidgetActivities_Widgets FOREIGN KEY (WidgetId)
REFERENCES Widgets (WidgetId)
)
CREATE TABLE DingBatActivities
(
ActivityId int NOT NULL,
DingBatId int NOT NULL,
ButtonId int,
CONSTRAINT PK_DingBatActivities PRIMARY KEY (ActivityId),
CONSTRAINT FK_DingBatActivities_Activities FOREIGN KEY (ActivityId)
REFERENCES Activities (ActivityId),
CONSTRAINT FK_DingBatActivities_DingBats FOREIGN KEY (DingBatId)
REFERENCES DingBats (DingBatId)
CONSTRAINT FK_DingBatActivities_Buttons FOREIGN KEY (ButtonId)
REFERENCES Buttons (ButtonId)
)
This solution seems good for fetching all activities given a widget or dingbat record id, however it doesn't seem so good for fetching all activities and then trying to determine to which record they refer.
That is, in this example, all the account names and timestamps are stored in a separate table, so it's easy to create reports focused on users and focused on time intervals without the need to know what the activity is in particular.
However, if you did want to report on the activities by type in particular, this solution would require determining to which type of activity the general activity table refers.
I could put all my activity types in one table, however the ID's would not be able to be constrained by a foreign key, instead the table name might be used as an id, which would lead me to use dynamic queries.
Note in the example that a DingBatActivity has an optional button Id. If the button name were to have been edited after being added to the dingbat, the activity would be able to refer to the button and know its name, so if a report listed all activities by dingbat and by button by name, the button name change would automatically be reflected in the activity description.
Looking for some other ideas and how those ideas compromise between programming effort, data integrity, performance, and reporting flexibility.
The way that I usually architect a solution to this problem is similar to inheritance in objects. If you have "activities" that are taking place on certain entities and you want to track those activities then the entities involved almost certainly have something in common. There's your base table. From there you can create subtables off of the base table to track things specific to that subtype. For example, you might have:
CREATE TABLE Objects -- Bad table name, should be more specific
(
object_id INT NOT NULL,
name VARCHAR(20) NOT NULL,
CONSTRAINT PK_Application_Objects PRIMARY KEY CLUSTERED (application_id)
)
CREATE TABLE Widgets
(
object_id INT NOT NULL,
height DECIMAL(5, 2) NOT NULL,
width DECIMAL(5, 2) NOT NULL,
CONSTRAINT PK_Widgets PRIMARY KEY CLUSTERED (object_id),
CONSTRAINT FK_Widgets_Objects
FOREIGN KEY (object_id) REFERENCES Objects (object_id)
)
CREATE TABLE Dingbats
(
object_id INT NOT NULL,
label VARCHAR(50) NOT NULL,
CONSTRAINT PK_Dingbats PRIMARY KEY CLUSTERED (object_id),
CONSTRAINT FK_Dingbats_Objects
FOREIGN KEY (object_id) REFERENCES Objects (object_id)
)
Now for your activities:
CREATE TABLE Object_Activities
(
activity_id INT NOT NULL,
object_id INT NOT NULL,
activity_type INT NOT NULL,
activity_time DATETIME NOT NULL,
account_id INT NOT NULL,
CONSTRAINT PK_Object_Activities PRIMARY KEY CLUSTERED (activity_id),
CONSTRAINT FK_Object_Activities_Objects
FOREIGN KEY (object_id) REFERENCES Objects (object_id),
CONSTRAINT FK_Object_Activities_Activity_Types
FOREIGN KEY (activity_type) REFERENCES Activity_Types (activity_type),
)
CREATE TABLE Dingbat_Activities
(
activity_id INT NOT NULL,
button_id INT NOT NULL,
CONSTRAINT PK_Dingbat_Activities PRIMARY KEY CLUSTERED (activity_id),
CONSTRAINT FK_Dingbat_Activities_Object_Activities
FOREIGN KEY (activity_id) REFERENCES Object_Activities (activity_id),
CONSTRAINT FK_Dingbat_Activities_Buttons
FOREIGN KEY (button_id) REFERENCES Object_Activities (button_id),
)
You can add a type code to the base activity if you want to for the type of object which it is affecting or you can just determine that by looking for existence in a subtable.
Here's the big caveat though: Make sure that the objects/activities really do have something in common which relates them and requires you to go down this path. You don't want to store disjointed, unrelated data in the same table. For example, you could use this method to create a table that holds both bank account transactions and celestial events, but that wouldn't be a good idea. At the base level they need to have something in common.
Also, I assumed that all of your activities were related to an account, which is why it's in the base table. Anything in common to ALL activities goes in the base table. Things relevant to only a subtype go in those tables. You could even go multiple levels deep, but don't get carried away. The same goes for the objects (again, bad name here, but I'm not sure what you're actually dealing with). If all of your objects have a color then you can put it in the Objects table. If not, then it would go into sub tables.
I'm going to go out on a limb and take a few wild guesses about what you're really trying to accomplish.
You say you're trying to track 'store activities' I'm going to assume you have the following activities:
Purchase new item
Sell item
Write off item
Hire employee
Pay employee
Fire employee
Update employee record
Ok, for these activities, you need a few different tables: one for inventory, one for departments, and one for employees
The inventory table could have the following information:
inventory:
item_id (pk)
description (varchar)
number_in_stock (number)
cost_wholesale (number)
retail_price (number)
dept_id (fk)
department:
dept_id (pk)
description (varchar)
employee
emp_id (pk)
first_name (varchar)
last_name (varchar)
salary (number)
hire_date (date)
fire_date (date)
So, when you buy new items, you will either update the number_in_stock in inventory table, or create a new row if it is an item you've never had before. When you sell an item, you decriment the number_in_stock for that item (also for when you write off an item).
When you hire a new employee, you add a record from them to the employees table. When you pay them, you grab their salary from the salary column. When you fire them, you fill in that column for their record (and stop paying them).
In all of this, the doing is not done by the database. SQL should be used for keeping track of information. It's fine to write procedures for doing these updates (a new invoice procedure that updates all the items from an invoice record). But you don't need a table to do stuff. In fact, a table can't do anything.
When designing a database, the question you need to ask is not "what do I need to do?" it is "What information do I need to keep track of?"
New answer, based on an different interpretation of the question.
Are you just trying to keep a list of what has happened? If you just need a ordered list of past events, you just need 1 table for it:
action_list
action_list_id (pk)
action_desc (varchar)
event_log:
event_log_id (pk)
event_time (timestamp)
action_list_id (fk)
new_action_added (fk)
action_details_or_description (varchar)
In this, the action_list would be something like:
1 'WidgetRotated'
2 'WidgetFlipped'
3 'DingBatPushed'
4 'AddNewAction'
5 'DeleteExistingAction'
The event_log would be a list of what activities happened, and when. One of your actions would be "add new action" and would require the 'new_action_added' column to be filled in on the event table anytime the action taken is "add new action".
You can create actions for update, remove, add, etc.
EDIT:
I added the action_details_or_description column to event. In this way, you can give further information about an action. For example, if you have a "product changes color" action, the description could be "Red" for the new color.
More broadly, you'll want to think through and map out all the different types of actions you'll be taking ahead of time, so you can set up your table(s) in a way that can accurately contain the data you want to put into them.
How about the SQL logs?
The last time I needed a database transaction logger I used an Instead Of trigger in the database so that it would instead of just updating the record, the database would insert a new record into the log table. This technique meant that I needed an additional table to hold the log for each table in my database and the log table had an additional column with a time stamp. Using this technique you can even store the pre and post update state of the record if you want to.