How do I realize this 3 simple things in SQL? - 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.

Related

Is it possible to make a polymorphic relationship in Yii2?

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.

How to enforce a unique constraint across multiple tables?

I have created the following two tables to map out students and teachers :
CREATE TABLE students(
student_id SERIAL PRIMARY KEY,
first_name NOT NULL VARCHAR(50),
last_name NOT NULL VARCHAR(50),
phone VARCHAR(15) UNIQUE NOT NULL CHECK (phone NOT LIKE '%[^0-9]%'),
email VARCHAR(30) UNIQUE NOT NULL CHECK (email NOT LIKE '%#%'),
graduationYear SMALLINT CHECK (graduationYear > 1900)
);
CREATE TABLE teachers(
teacher_id SERIAL PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
departament VARCHAR(40) NOT NULL,
email VARCHAR(30) UNIQUE NOT NULL CHECK (email NOT LIKE '%#%'),
phone VARCHAR(15) UNIQUE NOT NULL CHECK (phone NOT LIKE '%[^0-9]%')
);
As you can see, both tables have a column for phone and email. I want these two to be unique to each individual.
How can I introduce a constraint that will check if the phone number/email introduced, for example, in the students table doesn't already exist in the teachers table?
Is there any kind of keyword that works like UNIQUE but on multiple tables or should I take another approach?
Edit: as #a_horse_with_no_name pointed out, LIKE doesn't support regular expressions. I should have used SIMILAR TO.
I would create a single table person that contains all attributes that are common to both including a type column that identifies teachers and students. Then you can create unique constraints (or indexes) on the phone and email columns.
To store the "type specific" attributes (graduation year, department) you can either have nullable columns in the person table and only put in values depending on the type. If you do not expect to have more "type specific" attributes apart from those two, this is probably the easiest solution
If you expect more "type specific" attributes, additional tables (student and teacher) with containing those can also be used. This is the traditional way of modelling inheritance in a relational database. As Postgres supports table inheritance, you could also create the teacher and student tables to inherit from the person table.

SQLite database with multi-valued properties

I want to create a SQLITE database for storing objects. The objects have properties with multiple values for which I have created separate tables.
CREATE TABLE objs
(
id INTEGER,
name TEXT
);
CREATE TABLE prop1
(
id INTEGER,
value TEXT,
FOREIGN KEY(id) REFERENCES objs(id)
);
CREATE TABLE prop2
(
id INTEGER,
value TEXT,
FOREIGN KEY(id) REFERENCES objs(id)
);
For a list of ids I get as a result of JOINs, I want to find values of these two properties. For that, I am performing the JOINs followed by another JOIN with the 'prop1' table. I then repeat this for 'prop2' table. I suspect this is inefficient (too many joins) and can be improved. I have two questions.
Is this the correct way to design the DB ?
What is the most efficient way of extracting values of the properties I want ?
I would suggest the following structure.
CREATE TABLE objs
(
id INTEGER,
name TEXT
);
CREATE TABLE properties
(
id INTEGER,
Property_name varchar(50),
Property_type varchar(10),
value TEXT,
FOREIGN KEY(id) REFERENCES objs(id)
);
Storing all the different types of properties in different table is a very bad idea. You can just store the property name and type(string, numeric etc.). You can also add multiple value columns like numeric_value, string_value and so on.

Constraint to table column name (postgresql)

I'm implementing a product database using the single table inheritance (potentially later class table inheritance) model for product attributes. That's all working fine and well but I'm trying to figure out how best to deal with product variants whilst maintaining referential integrity.
Right now a simplified version of my main product table looks like this:
CREATE TABLE product (
id SERIAL NOT NULL,
name VARCHAR(100) NOT NULL,
brand VARCHAR(40) NOT NULL,
color VARCHAR(40)[] NOT NULL
)
(color is an array so that all of the standard colors of any given product can be listed)
For handling variants I've considered tracking the properties on which products vary in a table called product_variant_theme:
CREATE TABLE product_variant_theme (
id SERIAL NOT NULL,
product_id INT NOT NULL,
attribute_name VARCHAR(40) NOT NULL
)
Wherein I insert rows with the product_id in question and add the column name for the attribute into the attribute_name field e.g. 'color'.
Now feel free to tell me if this is an entirely stupid way to go about this in the first place, but I am concerned by the lack of a constraint between attribute_name and the actual column name itself. Obviously if alter the product table and remove that column I might still be left with rows in my second table that refer to it. The functional equivalent of what I'm looking for would be something like a foreign key on attribute_name to the information_schema view that describes the tables, but I don't think there's any way to do that directly, and I'm wondering if there is any reasonable way to get that kind of functionality here.
Thanks.
Are you looking for something like this?
product
=======
id
name
attribute
=========
id
name
product_attribute_map
=====================
product_id
attribute_id
value

Polymorphism in SQL database tables?

I currently have multiple tables in my database which consist of the same 'basic fields' like:
name character varying(100),
description text,
url character varying(255)
But I have multiple specializations of that basic table, which is for example that tv_series has the fields season, episode, airing, while the movies table has release_date, budget etc.
Now at first this is not a problem, but I want to create a second table, called linkgroups with a Foreign Key to these specialized tables. That means I would somehow have to normalize it within itself.
One way of solving this I have heard of is to normalize it with a key-value-pair-table, but I do not like that idea since it is kind of a 'database-within-a-database' scheme, I do not have a way to require certain keys/fields nor require a special type, and it would be a huge pain to fetch and order the data later.
So I am looking for a way now to 'share' a Primary Key between multiple tables or even better: a way to normalize it by having a general table and multiple specialized tables.
Right, the problem is you want only one object of one sub-type to reference any given row of the parent class. Starting from the example given by #Jay S, try this:
create table media_types (
media_type int primary key,
media_name varchar(20)
);
insert into media_types (media_type, media_name) values
(2, 'TV series'),
(3, 'movie');
create table media (
media_id int not null,
media_type not null,
name varchar(100),
description text,
url varchar(255),
primary key (media_id),
unique key (media_id, media_type),
foreign key (media_type)
references media_types (media_type)
);
create table tv_series (
media_id int primary key,
media_type int check (media_type = 2),
season int,
episode int,
airing date,
foreign key (media_id, media_type)
references media (media_id, media_type)
);
create table movies (
media_id int primary key,
media_type int check (media_type = 3),
release_date date,
budget numeric(9,2),
foreign key (media_id, media_type)
references media (media_id, media_type)
);
This is an example of the disjoint subtypes mentioned by #mike g.
Re comments by #Countably Infinite and #Peter:
INSERT to two tables would require two insert statements. But that's also true in SQL any time you have child tables. It's an ordinary thing to do.
UPDATE may require two statements, but some brands of RDBMS support multi-table UPDATE with JOIN syntax, so you can do it in one statement.
When querying data, you can do it simply by querying the media table if you only need information about the common columns:
SELECT name, url FROM media WHERE media_id = ?
If you know you are querying a movie, you can get movie-specific information with a single join:
SELECT m.name, v.release_date
FROM media AS m
INNER JOIN movies AS v USING (media_id)
WHERE m.media_id = ?
If you want information for a given media entry, and you don't know what type it is, you'd have to join to all your subtype tables, knowing that only one such subtype table will match:
SELECT m.name, t.episode, v.release_date
FROM media AS m
LEFT OUTER JOIN tv_series AS t USING (media_id)
LEFT OUTER JOIN movies AS v USING (media_id)
WHERE m.media_id = ?
If the given media is a movie,then all columns in t.* will be NULL.
Consider using a main basic data table with tables extending off of it with specialized information.
Ex.
basic_data
id int,
name character varying(100),
description text,
url character varying(255)
tv_series
id int,
BDID int, --foreign key to basic_data
season,
episode
airing
movies
id int,
BDID int, --foreign key to basic_data
release_data
budget
What you are looking for is called 'disjoint subtypes' in the relational world. They are not supported in sql at the language level, but can be more or less implemented on top of sql.
You could create one table with the main fields plus a uid then extension tables with the same uid for each specific case. To query these like separate tables you could create views.
Using the disjoint subtype approach suggested by Bill Karwin, how would you do INSERTs and UPDATEs without having to do it in two steps?
Getting data, I can introduce a View that joins and selects based on specific media_type but AFAIK I cant update or insert into that view because it affects multiple tables (I am talking MS SQL Server here). Can this be done without doing two operations - and without a stored procedure, natually.
Thanks
Question is quite old but for modern postresql versions it's also worth considering using json/jsonb/hstore type.
For example:
create table some_table (
name character varying(100),
description text,
url character varying(255),
additional_data json
);