MySQL объединяет несколько таблиц - PullRequest
6 голосов
/ 11 июня 2011

I. Введение

Я создаю систему поддержки, в которой пользователь из определенной страны поднимает вопрос по определенной категории, а экспертам из этой страны, административного деления и категории назначается проблема.

E.x. Пользователь из страны Germany с почтовым индексом 1000 поднимает проблему в категории Software. Эксперты из страны Germany и / или провинции с границами почтового индекса MIN_PROVINCE_ZIPCODE <= 1000 >= MAX_PROVINCE_ZIPCODE и / или региона с границами почтового индекса MIN_REGION_ZIPCODE <= 1000 >= MAX_REGION_ZIPCODE и категории Software получают решение проблемы.

т.е .: Выберите все выпуски, в которых страна выдачи равна стране эксперта, а категория выпуска равна одной из категорий экспертов, и / или почтовый индекс выпуска больше или равен минимальному почтовому индексу провинции и меньше или равен максимальному почтовому индексу провинции, и / или почтовый индекс выдачи больше или равен минимальному почтовому индексу региона, а код выпуска меньше или равен почтовому индексу.

"и / или" означает, что если экспертам поручено принимать вопросы от конкретного административного отдела (ов), а если нет, то назначить им все, что соответствует их стране и категории

II. Схемы и записи базы данных

* Имейте в виду! *

а) Эксперты могут быть частью ...

  1. одна, несколько или нет категории (ей)
  2. одна, несколько или нет провинций
  3. один, несколько или нет регионов

b) Если эксперты НЕ часть ...

  1. категории, тогда им не будет назначено вопросов
  2. провинции, тогда им будут назначены все вопросы для их страны и категории
  3. регион, тогда им будут назначены все вопросы для их провинции (ей)

1. Schemas

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

б) Результат:

+----------+-----------+
| issue_id | expert_id |
+----------+-----------+
|        3 |         1 |
|        1 |         1 |
|        4 |         1 |
|        2 |         2 |
|        3 |         3 |
+----------+-----------+

в) Точный результат был бы:

+----------+-----------+
| issue_id | expert_id |
+----------+-----------+
|        1 |         1 |
|        2 |         2 |
|        3 |         3 |
+----------+-----------+

Объяснение, почему приведенный выше визуальный результат является точным.

Во-первых, давайте получим «полное объединение», чтобы мы могли провести сравнение:
г) Запрос:

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), фиксированным вручную результатом, мы можем заметить ...

  1. issue_id номер 3 может НЕ быть назначенным на expert_id номер 1, потому что issue_id номер 1 из страны Germany, как и эксперт, назначенный на category_id номер 2, как и эксперт, НО имеет почтовый индекс 1500, а expert_id номер 1 назначается для получения issues только от province_id число 4 и regions_id число 8 и 10 внутри этого province. Таким образом, regions почтовые индексы варьируются от 2000 до 2299 и от 2600 до 2699, где наш issue почтовый индекс не принадлежит.
  2. issue_id номер 1 может быть назначен на expert_id номер 1, потому что issue_id номер 1 из страны Germany, так же как эксперт, назначен на номер category_id 2, как и эксперт, имеет почтовый индекс 2100, который находится между границами province_id числа 4 и region_id числа 8 в пределах провинции, которой назначено покрытие expert_id номер 1.
  3. issue_id номер 4 может НЕ быть назначенным на expert_id номер 1, потому что issue_id номер 4 из страны Germany, так же как и эксперт присвоенный на category_id номер 4, НО имеет почтовый индекс 2800, который находится в границах province_id числа 4, но это не в пределах region_id числа 8 и 10, которому назначено покрытие expert_id числа 1
  4. issue_id номер 2 может быть назначен на expert_id номер 2, потому что issue_id номер 2 из страны France, так же как эксперт, назначен на category_id номер 1, как и эксперт, имеет почтовый индекс 1900, который находится в пределах province_id числа 6, назначенного для этого эксперта.
  5. issue_id номер 3 может быть назначен на expert_id номер 3, потому что issue_id номер 3 из страны Germany, так же как эксперт, назначен на category_id номер 1, как эксперт. Кроме того, этот эксперт не имеет каких-либо административных ограничений. То есть этот эксперт может взять issues из всех Germany

Таким образом, мы перечислили все issues, которые можно присвоить experts.

2. Отсутствует половина решения

Как видите, моя половина решения не учитывает ограничения административного деления.
Я не могу использовать процедуру (ы) или представления для достижения этой цели, хотя я могу разделить его на несколько запросов, если это поможет.

База данных - MySQL (5.0.1 - MySQL Community Server (GPL)), а язык программирования - PHP (5.1).

Ответы [ 2 ]

3 голосов
/ 12 июня 2011

Я просто изменяю ответ от @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 |
2 голосов
/ 11 июня 2011

Ваш 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...