Если я правильно понял этот вопрос. Вы не можете построить свой велосипед только одним запросом (я не знаком с термином "ход рыцаря"). На самом деле, вы можете - но это должен быть рекурсивный SQL запрос . Потому что вы будете вычислять транзитивное замыкание отношения часть-часть.
К сожалению, я не сразу знаю, как их написать. Синтаксис SQL откровенно ужасен и рекурсивен SQL выглядит даже ужаснее, поэтому ниже приведен пример кода с использованием al oop.
На самом деле вам нужна только одна таблица для представления данных, поскольку отношение basicpart/1
ничего не вносит в таблицу, кроме как помечает определенные «вещи» как basi c. Но это также вещи, которые не появляются в assembly/2
на первой позиции.
Примечания:
- Не используется ENUMS, которые на самом деле не являются " типы "в MySQL / MariaDB, но просто ограничение на поле определенной таблицы c. (Например, WTF!)
- Представление мультисета кода Пролога («велосипед имеет два колеса») сведено в несколько рядов, отдельно обозначаемых числовым идентификатором c суррогата. Это связано с догмой «Первая нормальная форма » практики RDBMS. Нет ничего плохого в том, чтобы иметь мультимножества в качестве значений, если язык запросов и механизм СУБД могут его поддерживать. Например, вы можете иметь XML значений в PostgreSQL, заполненных запросами к его содержимому, насколько я помню 1 .
DELIMITER //
DROP PROCEDURE IF EXISTS prepare;
CREATE PROCEDURE prepare()
BEGIN
DROP TABLE IF EXISTS assembly;
CREATE TABLE assembly
(id INT AUTO_INCREMENT KEY, -- surrogate key because a bike may have several wheels
part VARCHAR(10) NOT NULL,
subpart VARCHAR(10) NOT NULL);
INSERT INTO assembly(part,subpart) VALUES
("bike","wheel"),
("bike","wheel"),
("bike","frame"),
("wheel","spoke"),
("wheel","rim"),
("wheel","hub"),
("frame","rearframe"),
("frame","frontframe"),
("frontframe","fork"),
("frontframe","handles"),
("hub","gears"),
("hub","axle"),
("axle","bolt"),
("axle","nut");
END;
DROP PROCEDURE IF EXISTS compute_transitive_closure;
CREATE PROCEDURE compute_transitive_closure()
BEGIN
DROP TABLE IF EXISTS pieces;
CREATE TABLE pieces
(id INT AUTO_INCREMENT KEY,
part VARCHAR(10) NOT NULL,
subpart VARCHAR(10) NOT NULL,
path VARCHAR(500) NOT NULl DEFAULT "",
depth INT NOT NULL DEFAULT 0);
INSERT INTO pieces(part,subpart,path,depth) VALUES
("ROOT","bike","/bike",0);
SET @depth=0;
l: LOOP
INSERT INTO pieces(part,subpart,path,depth)
SELECT
p.subpart,
a.subpart,
CONCAT(p.path,'/',a.subpart),
@depth+1
FROM
pieces p,
assembly a
WHERE
p.depth = @depth AND p.subpart = a.part;
IF ROW_COUNT() <= 0 THEN
LEAVE l;
ELSE
SELECT * FROM pieces;
END IF;
SET @depth=@depth+1;
END LOOP;
END; //
DELIMITER ;
Поместить вышеприведенное в файл SQL.txt
, а затем в базе данных testme
:
MariaDB [testme]> source SQL.txt;
MariaDB [testme]> CALL prepare;
MariaDB [testme]> CALL compute_transitive_closure;
Затем через 4 прохода через l oop вы получите:
+----+------------+------------+--------------------------------+-------+
| id | part | subpart | path | depth |
+----+------------+------------+--------------------------------+-------+
| 1 | ROOT | bike | /bike | 0 |
| 2 | bike | wheel | /bike/wheel | 1 |
| 3 | bike | wheel | /bike/wheel | 1 |
| 4 | bike | frame | /bike/frame | 1 |
| 5 | wheel | spoke | /bike/wheel/spoke | 2 |
| 6 | wheel | spoke | /bike/wheel/spoke | 2 |
| 7 | wheel | rim | /bike/wheel/rim | 2 |
| 8 | wheel | rim | /bike/wheel/rim | 2 |
| 9 | wheel | hub | /bike/wheel/hub | 2 |
| 10 | wheel | hub | /bike/wheel/hub | 2 |
| 11 | frame | rearframe | /bike/frame/rearframe | 2 |
| 12 | frame | frontframe | /bike/frame/frontframe | 2 |
| 20 | frontframe | fork | /bike/frame/frontframe/fork | 3 |
| 21 | frontframe | handles | /bike/frame/frontframe/handles | 3 |
| 22 | hub | gears | /bike/wheel/hub/gears | 3 |
| 23 | hub | gears | /bike/wheel/hub/gears | 3 |
| 24 | hub | axle | /bike/wheel/hub/axle | 3 |
| 25 | hub | axle | /bike/wheel/hub/axle | 3 |
| 27 | axle | bolt | /bike/wheel/hub/axle/bolt | 4 |
| 28 | axle | nut | /bike/wheel/hub/axle/nut | 4 |
| 29 | axle | bolt | /bike/wheel/hub/axle/bolt | 4 |
| 30 | axle | nut | /bike/wheel/hub/axle/nut | 4 |
+----+------------+------------+--------------------------------+-------+
1 : Это заставило меня выкопать «Глубина базы данных: теория отношений для практиков» , O'Reilly 2005, Chris Date , отличное введение в реляционную модель. На странице 30 Date рассматривает «наборы как значения» (но не рассматривает «мультимножества»):
Второй (и независимо от того, что вы думаете о моем первом аргументе), факт в том, что набор, подобный {P2,P4,P5}
, не более и не менее разложим в СУБД, чем символьная строка. Как строки символов, наборы имеют некоторую внутреннюю структуру; однако, как и со строками символов, удобно игнорировать эту структуру для определенных целей. Другими словами, если символьная строка совместима с требованиями 1NF, то есть если символьные строки имеют атоми c, то наборы также должны быть. Реальная точка зрения, к которой я прихожу, заключается в том, что понятие атомарности не имеет абсолютного значения; это зависит только от того, что мы хотим сделать с данными. Иногда мы хотим иметь дело с целым набором номеров деталей как с одной вещью, а иногда мы хотим иметь дело с отдельными номерами деталей в этом наборе - но затем мы опускаемся до более низкого уровня детализации (более низкого уровня абстракции).