Site icon OS CONFIG

Соединение нескольких таблиц в SQL

Краткая инструкция по соединению нескольких таблиц в базе данных, которые позволят связать и вывести необходимые данные с помощью SQL-запросов. Подходит как для MariaDB/MySQL, так и для PostgreSQL.

К примеру, имеется у нас самая простая примитивная база данных contracts для ведения списка заключённых договоров с контрагентами, в которой содержатся 3 таблицы с данными:

Сами по себе эти таблицы представляют из себя набор технически структуированных данных, однако при выводе данных одной из этих таблиц логическая структура полностью или частично отсутствует. Для логической связи в SQL использкются операторы соединения и сравнения.

Самый простой способ связи таблиц — внутреннее соединение с помощью оператора INNER JOIN.

Синтаксис выглядит следующим образом:

SELECT 
	column1, column2, column5, columnN 
FROM 
	table1 
	INNER JOIN table2
	ON column2 = column3;

То есть, данные будут выводиться из таблиц table1 и table2, а оператор ON говорит о том, столбец column2 из таблицы table1 будет равен column3 из таблицы table2.

Таким образом, мы соединили таблицы table1 и table2 и настроили между ними связь при помощи столбцов column2 и column3.

Из имеющегося примера выше, соединим две таблицы contracts и contractors и настроим связь колонок org_id с company_id:

SELECT 
	contract_id, company_name, contract_date_start, contract_date_end 
FROM 
	contracts 
	INNER JOIN contractors 
	ON org_id = company_id;

Результат:

Также можем усложнить задачу, соединив три таблицы:

SELECT 
	contract_id, company_name, service_type, contract_date_start, contract_date_end 
FROM 
	contracts 
	INNER JOIN contractors 
	ON org_id = company_id 
	INNER JOIN services 
	ON serv_id = service_id;

В данном запросе используются две связи столбцов: org_id с company_id и serv_id с service_id.

Результат:

Как видно из скриншотов выше, у внутреннего соединения есть недостаток, связанный с исключением вывода результатов из-за несоответствия данных связей. Здесь нам поможет внешнее соединение OUTER JOIN. Различают два вида внешнего соединения: левое LEFT OUTER JOIN и правое RIGHT OUTER JOIN. Отличаются они тем, в левом соединении в выводе добавятся пустые или неиспользованные строки таблицы слева, а в правом — справа.

Для левого соединения синтаксис выглядит следующим образом:

SELECT 
	column1, column2, column5, columnN 
FROM 
	table1 
	LEFT OUTER JOIN table2
	ON column2 = column3;

а для правого — так:

SELECT 
	column1, column2, column5, columnN 
FROM 
	table1 
	RIGHT OUTER JOIN table2
	ON column2 = column3;

Из имеющегося примера выше, склеим две таблицы contracts и contractors с помощью левого и правого внешнего соединения и настроим связь колонок org_id с company_id:

SELECT 
	contract_id, company_name, contract_date_start, contract_date_end 
FROM 
	contracts 
	LEFT OUTER JOIN contractors 
	ON org_id = company_id;
SELECT 
	contract_id, company_name, contract_date_start, contract_date_end 
FROM 
	contracts 
	RIGHT OUTER JOIN contractors 
	ON org_id = company_id;

Из скриншотов видно, что при использовании левого внешнего соединения добавился номер договора (contract_id) 22/0188, но наименование компании (company_name) пустое, так как этот договор не привязан ни к одной из компаний, содержащихся в БД. При использовании правого соединения — наоборот, появилось наименование компании (company_name) Лучшие из лучших, но нет ни одного договора в БД, к которому привязан этот контрагент.

Результаты левого и правого внешнего соединения можно объединить с помощью оператора UNION.

Для такого объединения синтаксис будет выглядеть так:

(SELECT 
	column1, column2, column5, columnN 
FROM 
	table1 
	LEFT OUTER JOIN table2
	ON column2 = column3)
	
UNION

(SELECT 
	column1, column2, column5, columnN 
FROM 
	table1 
	RIGHT OUTER JOIN table2
	ON column2 = column3);

Для нашего примера объединим левое и правое внешние соединения следующим образом:

(SELECT 
	contract_id, company_name, contract_date_start, contract_date_end 
FROM 
	contracts 
	LEFT OUTER JOIN contractors 
	ON org_id = company_id)

UNION

(SELECT 
	contract_id, company_name, contract_date_start, contract_date_end 
FROM 
	contracts 
	RIGHT OUTER JOIN contractors 
	ON org_id = company_id);

При использовании оператора UNION ALL левое и правое соединения будут показаны отдельно друг от друга, но в одной таблице:

(SELECT 
	contract_id, company_name, contract_date_start, contract_date_end 
FROM 
	contracts 
	LEFT OUTER JOIN contractors 
	ON org_id = company_id)

UNION ALL

(SELECT 
	contract_id, company_name, contract_date_start, contract_date_end 
FROM 
	contracts 
	RIGHT OUTER JOIN contractors 
	ON org_id = company_id);

Для написания данной статьи были использованы следующие скрипты:

для СУБД PostgreSQL

CREATE DATABASE contracts OWNER db_user;

\c contracts;

CREATE TABLE contractors (
company_id SERIAL PRIMARY KEY,
company_name TEXT NOT NULL,
company_address TEXT NOT NULL,
responsible_person TEXT NOT NULL,
phone_number TEXT NOT NULL
);

CREATE TABLE services (
service_id SERIAL PRIMARY KEY,
service_type TEXT NOT NULL
);

CREATE TABLE contracts (
id SERIAL PRIMARY KEY,
contract_id TEXT NOT NULL,
org_id INTEGER DEFAULT 0,
serv_id INTEGER DEFAULT 0,
contract_date_start DATE,
contract_date_end DATE
);

INSERT INTO contractors (company_id, company_name, company_address, responsible_person, phone_number) VALUES (1, 'Титаны', 'г. Москва, Тверская ул., д. 17', 'Сидоров Василий Петрович', '+7 (916) 123-45-67');
INSERT INTO contractors (company_id, company_name, company_address, responsible_person, phone_number) VALUES (2, 'Гиганты', 'г. Москва, Тверская ул., д. 15', 'Иванов Тимофей Константинович', '+7 (926) 321-45-99');
INSERT INTO contractors (company_id, company_name, company_address, responsible_person, phone_number) VALUES (3, 'МегаКонтакт', 'г. Москва, Дмитровское шоссе, д. 112', 'Пупкин Василий Александрович', '+7 (909) 766-55-35');
INSERT INTO contractors (company_id, company_name, company_address, responsible_person, phone_number) VALUES (4, 'Лучшие из лучших', 'г. Москва, Ленинградское шоссе, д. 15', 'Петров Иван Андреевич', '+7 (910) 452-12-15');
INSERT INTO contractors (company_id, company_name, company_address, responsible_person, phone_number) VALUES (5, 'Наша забота', 'г. Москва, Дмитровское шоссе, д. 112', 'Лукашин Вениамин Владимирович', '+7 (903) 767-85-25');

INSERT INTO services (service_id, service_type) VALUES (1, 'ИТ-услуги, обслуживание оргтехники');
INSERT INTO services (service_id, service_type) VALUES (2, 'Поддержка видеонаблюдения и сигнализации');
INSERT INTO services (service_id, service_type) VALUES (3, 'Услуги телефонной связи');
INSERT INTO services (service_id, service_type) VALUES (4, 'Интернет-провайдеры');
INSERT INTO services (service_id, service_type) VALUES (5, 'Поставка расходных материалов');
INSERT INTO services (service_id, service_type) VALUES (6, 'Поставка питьевой воды');

INSERT INTO contracts (id, contract_id, org_id, serv_id, contract_date_start, contract_date_end) VALUES (1, '19/0008', 2, 3, '2019-01-15', '2022-12-29');
INSERT INTO contracts (id, contract_id, org_id, serv_id, contract_date_start, contract_date_end) VALUES (2, '20/0057', 1, 2, '2020-02-08', '2022-11-30');
INSERT INTO contracts (id, contract_id, org_id, serv_id, contract_date_start, contract_date_end) VALUES (3, '21/0148', 3, 1, '2021-01-12', '2022-07-31');
INSERT INTO contracts (id, contract_id, org_id, serv_id, contract_date_start, contract_date_end) VALUES (4, '22/0175', 5, 8, '2021-03-29', '2023-05-31');
INSERT INTO contracts (id, contract_id, org_id, serv_id, contract_date_start, contract_date_end) VALUES (5, '22/0188', 7, 4, '2022-04-18', '2023-10-31');

для СУБД MariaDB/MySQL

CREATE DATABASE contracts;

GRANT ALL PRIVILEGES ON contracts.* to 'db_user'@'localhost';
FLUSH PRIVILEGES;

USE contracts;

CREATE TABLE contractors (
company_id int(8) unsigned NOT NULL auto_increment,
company_name varchar(255) NOT NULL default ' ',
company_address varchar(255) NOT NULL default ' ',
responsible_person varchar(255) NOT NULL default ' ',
phone_number varchar(32) NOT NULL default ' ',

PRIMARY KEY (company_id),
UNIQUE KEY company_name (company_name)
) ENGINE=MyISAM COMMENT='Company Data';

CREATE TABLE services (
service_id int(8) unsigned NOT NULL auto_increment,
service_type varchar(255) NOT NULL default ' ',

PRIMARY KEY (service_id),
UNIQUE KEY service_type (service_type)
) ENGINE=MyISAM COMMENT='Information about services';

CREATE TABLE contracts (
id int(8) unsigned NOT NULL auto_increment,
contract_id varchar(32) NOT NULL default ' ',
org_id int(8) default 0,
serv_id int(8) default 0,
contract_date_start DATE,
contract_date_end DATE,

PRIMARY KEY (id),
UNIQUE KEY service_type (contract_id)
) ENGINE=MyISAM COMMENT='List of contracts';

INSERT INTO contractors (company_id, company_name, company_address, responsible_person, phone_number) VALUES (NULL, 'Титаны', 'г. Москва, Тверская ул., д. 17', 'Сидоров Василий Петрович', '+7 (916) 123-45-67');
INSERT INTO contractors (company_id, company_name, company_address, responsible_person, phone_number) VALUES (NULL, 'Гиганты', 'г. Москва, Тверская ул., д. 15', 'Иванов Тимофей Константинович', '+7 (926) 321-45-99');
INSERT INTO contractors (company_id, company_name, company_address, responsible_person, phone_number) VALUES (NULL, 'МегаКонтакт', 'г. Москва, Дмитровское шоссе, д. 112', 'Пупкин Василий Александрович', '+7 (909) 766-55-35');
INSERT INTO contractors (company_id, company_name, company_address, responsible_person, phone_number) VALUES (NULL, 'Лучшие из лучших', 'г. Москва, Ленинградское шоссе, д. 15', 'Петров Иван Андреевич', '+7 (910) 452-12-15');
INSERT INTO contractors (company_id, company_name, company_address, responsible_person, phone_number) VALUES (NULL, 'Наша забота', 'г. Москва, Дмитровское шоссе, д. 112', 'Лукашин Вениамин Владимирович', '+7 (903) 767-85-25');

INSERT INTO services (service_id, service_type) VALUES (NULL, 'ИТ-услуги, обслуживание оргтехники');
INSERT INTO services (service_id, service_type) VALUES (NULL, 'Поддержка видеонаблюдения и сигнализации');
INSERT INTO services (service_id, service_type) VALUES (NULL, 'Услуги телефонной связи');
INSERT INTO services (service_id, service_type) VALUES (NULL, 'Интернет-провайдеры');
INSERT INTO services (service_id, service_type) VALUES (NULL, 'Поставка расходных материалов');
INSERT INTO services (service_id, service_type) VALUES (NULL, 'Поставка питьевой воды');

INSERT INTO contracts (id, contract_id, org_id, serv_id, contract_date_start, contract_date_end) VALUES (NULL, '19/0008', 2, 3, '2019-01-15', '2022-12-29');
INSERT INTO contracts (id, contract_id, org_id, serv_id, contract_date_start, contract_date_end) VALUES (NULL, '20/0057', 1, 2, '2020-02-08', '2022-11-30');
INSERT INTO contracts (id, contract_id, org_id, serv_id, contract_date_start, contract_date_end) VALUES (NULL, '21/0148', 3, 1, '2021-01-12', '2022-07-31');
INSERT INTO contracts (id, contract_id, org_id, serv_id, contract_date_start, contract_date_end) VALUES (NULL, '22/0175', 5, 8, '2021-03-29', '2023-05-31');
INSERT INTO contracts (id, contract_id, org_id, serv_id, contract_date_start, contract_date_end) VALUES (NULL, '22/0188', 7, 4, '2022-04-18', '2023-10-31');

Exit mobile version