MySQL несколько табличных соединений
И. Интро
я создаю систему поддержки, в которой пользователь из определенной страны поднимает вопрос о определенной категории, и экспертам из этой страны, административного отдела и категории присваивается проблема.
E. x. Пользователь из country Germany
с индексом 1000
поднимается вопрос о категории Software
. Эксперты из страны Germany
, и / или провинция с границами почтового индекса MIN_PROVINCE_ZIPCODE <= 1000 >= MAX_PROVINCE_ZIPCODE
, и / или регион с границами почтового индекса MIN_REGION_ZIPCODE <= 1000 >= MAX_REGION_ZIPCODE
, и категория Software
получил вопрос.
т. е.: Выберите Все темы где проблема страны равен экспертов страны, и вопрос категория совпадает с одним из экспертов категории, и/или выпуск индекс больше или равен минимальному провинция почтовый индекс и меньше или равна максимальной провинция zip-код, и/или выпуск индекс больше или равен области минимальный зип-код и выдачу кода меньше или равна почтовые индексы.
"и/или" означает, если эксперты назначено принимать вопросы от конкретного административного подразделения(ов), если они не являются, а затем назначить им все, что соответствует их стране и категории
II. Схемы И Записи Базы Данных
*имейте в виду!*
A) эксперты могут быть частью...
- один, несколько или нет категории (категорий)
- один, несколько, или нет провинция(с)
- один, несколько или нет региона(ов)
b) если эксперты не часть...
- категория, тогда никакие вопросы не будут назначены им
- провинция, тогда все вопросы для их страны и категории будут назначены им
- регион, тогда все вопросы для их провинции(ов) будут назначены им
1. Схемы
CREATE TABLE IF NOT EXISTS `categories` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(300) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `provinces` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`country` varchar(300) NOT NULL,
`province` varchar(300) NOT NULL,
`min_zipcode` int(5) unsigned NOT NULL,
`max_zipcode` int(5) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `regions` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`provinceid` int(11) unsigned NOT NULL,
`region` varchar(300) NOT NULL,
`min_zipcode` int(5) unsigned NOT NULL,
`max_zipcode` int(5) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `issues` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`categoryid` int(11) unsigned NOT NULL,
`country` varchar(150) NOT NULL,
`zipcode` int(5) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `experts` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`country` varchar(150) NOT NULL DEFAULT 'none',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `experts_categories` (
`expertid` int(11) unsigned NOT NULL,
`categoryid` int(11) unsigned NOT NULL,
PRIMARY KEY (`expertid`,`categoryid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `experts_provinces` (
`expertid` int(11) unsigned NOT NULL,
`provinceid` int(11) unsigned NOT NULL,
PRIMARY KEY (`expertid`,`provinceid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `experts_regions` (
`expertid` int(11) NOT NULL,
`regionid` int(11) NOT NULL,
PRIMARY KEY (`expertid`,`regionid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
2. Записи
INSERT INTO `categories` (`id`, `name`) VALUES
(1, 'Software'),
(2, 'Hardware');
INSERT INTO `experts` (`id`, `country`) VALUES
(1, 'Germany'),
(2, 'France'),
(3, 'Germany');
INSERT INTO `experts_categories` (`expertid`, `categoryid`) VALUES
(1, 1),
(1, 2),
(2, 1),
(3, 1);
INSERT INTO `experts_provinces` (`expertid`, `provinceid`) VALUES
(1, 4),
(2, 6),
(2, 7);
INSERT INTO `experts_regions` (`expertid`, `regionid`) VALUES
(1, 8),
(1, 10);
INSERT INTO `issues` (`id`, `categoryid`, `country`, `zipcode`) VALUES
(1, 2, 'Germany', 2100),
(2, 1, 'France', 1900),
(3, 1, 'Germany', 1500),
(4, 2, 'Germany', 2800),
(5, 2, 'France', 1850);
INSERT INTO `provinces` (`id`, `country`, `province`, `min_zipcode`, `max_zipcode`) VALUES
(1, 'Germany', 'Province One', 1000, 1299),
(2, 'Germany', 'Province Two', 1300, 1499),
(3, 'Germany', 'Province Three', 1500, 1999),
(4, 'Germany', 'Province Four', 2000, 2899),
(5, 'France', 'Province One', 1000, 1799),
(6, 'France', 'Province Two', 1800, 2199),
(7, 'France', 'Province Three', 2200, 2399);
INSERT INTO `regions` (`id`, `provinceid`, `region`, `min_zipcode`, `max_zipcode`) VALUES
(1, 1, 'Region One', 1000, 1099),
(2, 1, 'Region Two', 1100, 1159),
(3, 1, 'Region Three', 1160, 1299),
(4, 2, 'Region One', 1300, 1400),
(5, 2, 'Region Two', 1401, 1499),
(6, 3, 'Region One', 1500, 1699),
(7, 3, 'Region Two', 1700, 1999),
(8, 4, 'Region One', 2000, 2299),
(9, 4, 'Region Two', 2300, 2599),
(10, 4, 'Region Three', 2600, 2699),
(11, 4, 'Region Four', 2700, 2899),
(12, 5, 'Region One', 1000, 1699),
(13, 5, 'Region Two', 1700, 1799),
(14, 6, 'Region One', 1800, 2000),
(15, 6, 'Region Two', 2001, 2199),
(16, 7, 'Region One', 2200, 2299),
(17, 7, 'Region Two', 2300, 2399);
3. Визуальные Схемы
mysql> DESC `categories`;
+-------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+----------------+
| id | int(11) unsigned | NO | PRI | NULL | auto_increment |
| name | varchar(300) | NO | | NULL | |
+-------+------------------+------+-----+---------+----------------+
mysql> DESC `provinces`;
+-------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------------+------+-----+---------+----------------+
| id | bigint(11) unsigned | NO | PRI | NULL | auto_increment |
| country | varchar(300) | NO | | NULL | |
| province | varchar(300) | NO | | NULL | |
| min_zipcode | int(5) unsigned | NO | | NULL | |
| max_zipcode | int(5) unsigned | NO | | NULL | |
+-------------+---------------------+------+-----+---------+----------------+
mysql> DESC `regions`;
+-------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+------------------+------+-----+---------+----------------+
| id | int(11) unsigned | NO | PRI | NULL | auto_increment |
| provinceid | int(11) unsigned | NO | | NULL | |
| region | varchar(300) | NO | | NULL | |
| min_zipcode | int(5) unsigned | NO | | NULL | |
| max_zipcode | int(5) unsigned | NO | | NULL | |
+-------------+------------------+------+-----+---------+----------------+
mysql> DESC `issues`;
+------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+----------------+
| id | bigint(11) unsigned | NO | PRI | NULL | auto_increment |
| categoryid | int(11) unsigned | NO | | NULL | |
| country | varchar(150) | NO | | NULL | |
| zipcode | int(5) | NO | | NULL | |
+------------+---------------------+------+-----+---------+----------------+
mysql> DESC `experts`;
+---------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+------------------+------+-----+---------+----------------+
| id | int(11) unsigned | NO | PRI | NULL | auto_increment |
| country | varchar(150) | NO | | none | |
+---------+------------------+------+-----+---------+----------------+
mysql> DESC `experts_categories`;
+------------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+-------+
| expertid | int(11) unsigned | NO | PRI | NULL | |
| categoryid | int(11) unsigned | NO | PRI | NULL | |
+------------+------------------+------+-----+---------+-------+
mysql> DESC `experts_provinces`;
+------------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+-------+
| expertid | int(11) unsigned | NO | PRI | NULL | |
| provinceid | int(11) unsigned | NO | PRI | NULL | |
+------------+------------------+------+-----+---------+-------+
mysql> DESC `experts_regions`;
+----------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+---------+------+-----+---------+-------+
| expertid | int(11) | NO | PRI | NULL | |
| regionid | int(11) | NO | PRI | NULL | |
+----------+---------+------+-----+---------+-------+
4. Визуальные Записи
mysql> SELECT * FROM `categories`;
+----+----------+
| id | name |
+----+----------+
| 1 | Software |
| 2 | Hardware |
+----+----------+
mysql> SELECT * FROM `provinces`;
+----+---------+----------------+-------------+-------------+
| id | country | province | min_zipcode | max_zipcode |
+----+---------+----------------+-------------+-------------+
| 1 | Germany | Province One | 1000 | 1299 |
| 2 | Germany | Province Two | 1300 | 1499 |
| 3 | Germany | Province Three | 1500 | 1999 |
| 4 | Germany | Province Four | 2000 | 2899 |
| 5 | France | Province One | 1000 | 1799 |
| 6 | France | Province Two | 1800 | 2199 |
| 7 | France | Province Three | 2200 | 2399 |
+----+---------+----------------+-------------+-------------+
mysql> SELECT * FROM `regions`;
+----+------------+--------------+-------------+-------------+
| id | provinceid | region | min_zipcode | max_zipcode |
+----+------------+--------------+-------------+-------------+
| 1 | 1 | Region One | 1000 | 1099 |
| 2 | 1 | Region Two | 1100 | 1159 |
| 3 | 1 | Region Three | 1160 | 1299 |
| 4 | 2 | Region One | 1300 | 1400 |
| 5 | 2 | Region Two | 1401 | 1499 |
| 6 | 3 | Region One | 1500 | 1699 |
| 7 | 3 | Region Two | 1700 | 1999 |
| 8 | 4 | Region One | 2000 | 2299 |
| 9 | 4 | Region Two | 2300 | 2599 |
| 10 | 4 | Region Three | 2600 | 2699 |
| 11 | 4 | Region Four | 2700 | 2899 |
| 12 | 5 | Region One | 1000 | 1699 |
| 13 | 5 | Region Two | 1700 | 1799 |
| 14 | 6 | Region One | 1800 | 2000 |
| 15 | 6 | Region Two | 2001 | 2199 |
| 16 | 7 | Region One | 2200 | 2299 |
| 17 | 7 | Region Two | 2300 | 2399 |
+----+------------+--------------+-------------+-------------+
mysql> SELECT * FROM `issues`;
+----+------------+---------+---------+
| id | categoryid | country | zipcode |
+----+------------+---------+---------+
| 1 | 2 | Germany | 2100 |
| 2 | 1 | France | 1900 |
| 3 | 1 | Germany | 1500 |
| 4 | 2 | Germany | 2800 |
| 5 | 2 | France | 1850 |
+----+------------+---------+---------+
mysql> SELECT * FROM `experts`;
+----+---------+
| id | country |
+----+---------+
| 1 | Germany |
| 2 | France |
| 3 | Germany |
+----+---------+
mysql> SELECT * FROM `experts_categories`;
+----------+------------+
| expertid | categoryid |
+----------+------------+
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 3 | 1 |
+----------+------------+
mysql> SELECT * FROM `experts_provinces`;
+----------+------------+
| expertid | provinceid |
+----------+------------+
| 1 | 4 |
| 2 | 6 |
| 2 | 7 |
+----------+------------+
mysql> SELECT * FROM `experts_regions`;
+----------+----------+
| expertid | regionid |
+----------+----------+
| 1 | 8 |
| 1 | 10 |
+----------+----------+
III. Решение
мне удалось придумать половину раствора.
1. Моя половина решения
а) запрос:
SELECT
`i`.`id` `issue_id`,
`e`.`id` `expert_id`
FROM `issues` `i`
INNER JOIN `experts` `e`
ON `i`.`country` = `e`.`country`
INNER JOIN `experts_categories` `ec`
ON `e`.`id` = `ec`.`expertid`
AND `i`.`categoryid` = `ec`.`categoryid`
ORDER BY `e`.`id`, `ec`.`categoryid` ASC
b) результат:
+----------+-----------+
| issue_id | expert_id |
+----------+-----------+
| 3 | 1 |
| 1 | 1 |
| 4 | 1 |
| 2 | 2 |
| 3 | 3 |
+----------+-----------+
c) точный результат имел бы бывало:
+----------+-----------+
| issue_id | expert_id |
+----------+-----------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----------+-----------+
объяснение, почему вышеуказанный визуальный результат является точным.
во-первых, давайте получим "полное соединение", чтобы мы могли сделать сравнения:
d) запрос:
SELECT
`i`.`id` `issue_id`, `e`.`id` `expert_id`, `i`.`categoryid` `issue_category_id`, `ec`.`categoryid` `expert_category_id`,
`i`.`country` `issue_country`, `e`.`country` `expert_country`,
`i`.`zipcode` `issue_zipcode`,
`p`.`id` `province_id`, `p`.`min_zipcode` `province_min_zipcode`, `p`.`max_zipcode` `province_max_zipcode`,
`r`.`id` `region_id`, `r`.`min_zipcode` `region_min_zipcode`, `r`.`max_zipcode` `region_max_zipcode`
FROM `issues` `i`
INNER JOIN `experts` `e`
ON `i`.`country` = `e`.`country`
INNER JOIN `experts_categories` `ec`
ON `ec`.`expertid` = `e`.`id`
AND `i`.`categoryid` = `ec`.`categoryid`
LEFT JOIN `experts_provinces` `ep`
ON `e`.`id` = `ep`.`expertid`
LEFT JOIN `provinces` `p`
ON `ep`.`provinceid` = `p`.`id`
LEFT JOIN `experts_regions` `er`
ON `e`.`id` = `er`.`expertid`
LEFT JOIN `regions` `r`
ON `er`.`regionid` = `r`.`id`
AND `p`.`id` = `r`.`provinceid`
ORDER BY `e`.`id`,`ec`.`categoryid` ASC
e) результат:
+----------+-----------+-------------------+--------------------+---------------+----------------+---------------+-------------+----------------------+----------------------+-----------+--------------------+--------------------+
| issue_id | expert_id | issue_category_id | expert_category_id | issue_country | expert_country | issue_zipcode | province_id | province_min_zipcode | province_max_zipcode | region_id | region_min_zipcode | region_max_zipcode |
+----------+-----------+-------------------+--------------------+---------------+----------------+---------------+-------------+----------------------+----------------------+-----------+--------------------+--------------------+
| 3 | 1 | 1 | 1 | Germany | Germany | 1500 | 4 | 2000 | 2899 | 10 | 2600 | 2699 |
| 3 | 1 | 1 | 1 | Germany | Germany | 1500 | 4 | 2000 | 2899 | 8 | 2000 | 2299 |
| 1 | 1 | 2 | 2 | Germany | Germany | 2100 | 4 | 2000 | 2899 | 10 | 2600 | 2699 |
| 1 | 1 | 2 | 2 | Germany | Germany | 2100 | 4 | 2000 | 2899 | 8 | 2000 | 2299 |
| 4 | 1 | 2 | 2 | Germany | Germany | 2800 | 4 | 2000 | 2899 | 10 | 2600 | 2699 |
| 4 | 1 | 2 | 2 | Germany | Germany | 2800 | 4 | 2000 | 2899 | 8 | 2000 | 2299 |
| 2 | 2 | 1 | 1 | France | France | 1900 | 7 | 2200 | 2399 | NULL | NULL | NULL |
| 2 | 2 | 1 | 1 | France | France | 1900 | 6 | 1800 | 2199 | NULL | NULL | NULL |
| 3 | 3 | 1 | 1 | Germany | Germany | 1500 | NULL | NULL | NULL | NULL | NULL | NULL |
+----------+-----------+-------------------+--------------------+---------------+----------------+---------------+-------------+----------------------+----------------------+-----------+--------------------+--------------------+
таким образом, сравнивая (b) результат запроса с (c), вручную фиксированный результат, мы можем заметить...
-
issue_id
может не назначаетсяexpert_id
, потому чтоissue_id
из страныGermany
, как и эксперт, назначается наcategory_id
, как эксперта, но имеет почтовый код1500
иexpert_id
назначается takeissues
только сprovince_id
иregions_id
и10
внутриprovince
. Так чтоregions
диапазон почтовых индексов от2000
to2299
и от2600
to2699
, где нашissue
почтовый индекс не принадлежит. -
issue_id
может быть назначеноexpert_id
, потому чтоissue_id
из страныGermany
, как и эксперт, назначается наcategory_id
, как и эксперт, имеет почтовый индекс2100
, который находится между границамиprovince_id
иregion_id
в крае, который назначается покрытоexpert_id
. -
issue_id
can не назначаетсяexpert_id
, потому чтоissue_id
из страныGermany
, как и эксперт, назначается наcategory_id
, но имеет почтовый код2800
, который находится в границахprovince_id
, но это не в границахregion_id
и10
, который назначается для покрытияexpert_id
-
issue_id
может быть назначеноexpert_id
, потому чтоissue_id
из страныFrance
, как и эксперт, назначается наcategory_id
, как и эксперт, имеет почтовый индекс1900
, который находится в границахprovince_id
, назначенный для охвата этим экспертом. -
issue_id
количество3
может быть назначеноexpert_id
, потому чтоissue_id
из страныGermany
, как и эксперт, назначается наcategory_id
, как и эксперт. Кроме того, этот эксперт не имеет каких-либо административных ограничений. То есть, этот эксперт может взятьissues
из всехGermany
таким образом, мы перечислили все issues
это может быть присвоено experts
.
2. Недостающая половина решение
как вы можете видеть, мое Половинное решение не учитывает ограничения административного деления.
Я не могу использовать процедуры или представления для достижения этого, хотя я могу разделить его на несколько запросов, если это поможет.
База данных MySQL (5.0.1 - MySQL Community Server (GPL)) и язык программирования PHP (5.1).
2 ответов
Я просто изменяю ответ от @krubo.
Если вы хотите подзапрос, запрос будет:
SELECT
tis.id AS issue_id,
tex.id AS expert_id,
tis.categoryid AS issue_category_id,
tex.categoryid AS expert_category_id,
tis.country AS issue_country,
tex.country AS expert_country,
tis.zipcode AS issue_zipcode,
tis.provinceid AS province_id,
tis.province_minzipcode AS province_minzipcode,
tis.province_maxzipcode AS province_maxzipcode,
tis.regionid AS region_id,
tis.region_minzipcode AS region_minzipcode,
tis.region_maxzipcode AS region_maxzipcode
FROM
(
SELECT
i.id, categoryid, i.country, zipcode,
provinces.id AS provinceid, provinces.min_zipcode AS province_minzipcode,
provinces.max_zipcode AS province_maxzipcode, regions.id AS regionid,
regions.min_zipcode AS region_minzipcode,
regions.max_zipcode AS region_maxzipcode
FROM
issues AS i
LEFT JOIN provinces ON i.country=provinces.country
AND i.zipcode BETWEEN provinces.min_zipcode AND provinces.max_zipcode
LEFT JOIN regions on provinces.id=regions.provinceid
AND i.zipcode BETWEEN regions.min_zipcode AND regions.max_zipcode
) AS tis
JOIN
(
SELECT
e.id, country, categoryid, provinceid, regionid
FROM
experts e
JOIN experts_categories ON e.id=experts_categories.expertid
LEFT JOIN experts_provinces ON e.id=experts_provinces.expertid
LEFT JOIN experts_regions ON e.id=experts_regions.expertid
) AS tex
WHERE
tis.country=tex.country
AND tis.categoryid=tex.categoryid
AND (tis.provinceid IS NULL
OR tex.provinceid IS NULL
OR tis.provinceid=tex.provinceid)
AND (tis.regionid IS NULL
OR tex.regionid IS NULL
OR tis.regionid=tex.regionid);
результат:
+----------+-----------+-------------------+--------------------+---------------+----------------+---------------+-------------+----------------------+----------------------+-----------+--------------------+--------------------+
| issue_id | expert_id | issue_category_id | expert_category_id | issue_country | expert_country | issue_zipcode | province_id | province_min_zipcode | province_max_zipcode | region_id | region_min_zipcode | region_max_zipcode |
+----------+-----------+-------------------+--------------------+---------------+----------------+---------------+-------------+----------------------+----------------------+-----------+--------------------+--------------------+
| 1 | 1 | 2 | 2 | Germany | Germany | 2100 | 4 | 2000 | 2899 | 8 | 2000 | 2299 |
| 2 | 2 | 1 | 1 | France | France | 1900 | 6 | 2000 | 2199 | 14 | 1800 | 2000 |
| 3 | 3 | 1 | 1 | Germany | Germany | 1500 | 3 | 2000 | 1999 | 6 | 1500 | 1699 |
ваш SQL будет очень сложным, если вы не используете хотя бы одно представление для его организации. Попробуйте это представление, чтобы сопоставить каждую проблему с провинцией и регионом:
create view viewissues as
select issues.id, categoryid, issues.country, zipcode,
provinces.id as provinceid, regions.id as regionid
from issues
left join provinces on issues.country=provinces.country
and issues.zipcode between provinces.min_zipcode and provinces.max_zipcode
left join regions on provinces.id=regions.provinceid
and issues.zipcode between regions.min_zipcode and regions.max_zipcode;
и это представление, чтобы перечислить экспертов по их категориям, провинциям, если таковые имеются, и регионам, если таковые имеются:
create view viewexperts as
select experts.id, country, categoryid, provinceid, regionid
from experts
join experts_categories on experts.id=experts_categories.expertid
left join experts_provinces on experts.id=experts_provinces.expertid
left join experts_regions on experts.id=experts_regions.expertid;
Теперь ваш окончательный запрос может быть более управляемым, выбрав исходя из этого:
select distinct viewissues.id, viewexperts.id
from viewissues join viewexperts
where viewissues.country=viewexperts.country
and viewissues.categoryid=viewexperts.categoryid
and (viewissues.provinceid is null
or viewexperts.provinceid is null
or viewissues.provinceid=viewexperts.provinceid)
and (viewissues.regionid is null
or viewexperts.regionid is null
or viewissues.regionid=viewexperts.regionid);