это поможет вам
function CallPageSync(url, data) {
var response;
$.ajax({
async: false,
url: url,
data: data,
timeout: 4000,
success: function(result) {
response = result;
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
response = "err--" + XMLHttpRequest.status + " -- " + XMLHttpRequest.statusText;
}
});
return response;
}
, и вы можете называть его как
response = CallPageSync(checkPageURL, "un=" + username);
Теперь, когда MySQL 8.0 приближается к выпуску, , все популярные базы данных SQL будут поддерживать рекурсивные запросы в стандартном синтаксисе.
WITH RECURSIVE MyTree AS (
SELECT * FROM MyTable WHERE ParentId IS NULL
UNION ALL
SELECT m.* FROM MyTABLE AS m JOIN MyTree AS t ON m.ParentId = t.Id
)
SELECT * FROM MyTree;
я протестировал рекурсивные запросы в MySQL 8.0 в моем представлении рекурсивный запрос Throwdown в 2017.
Ниже мой исходный ответ с 2008:
<час>существует несколько способов хранить данные с древовидной структурой в реляционной базе данных. То, что Вы показываете в своем примере, использует два метода:
Другое решение называют Вложенные Наборы , и оно может быть сохранено в той же таблице также. Читайте" Деревья и Иерархии в SQL для Присяжных острословов " Joe Celko для намного большей информации об этих проектах.
я обычно предпочитаю дизайн, названный Таблица Закрытия (иначе столбец "Adjacency Relation") for storing tree-structured data. It requires another table, but then querying trees is pretty easy.
I cover Closure Table in my presentation Models for Hierarchical Data with SQL and PHP and in my book SQL Antipatterns: Avoiding the Pitfalls of Database Programming .
CREATE TABLE ClosureTable (
ancestor_id INT NOT NULL REFERENCES FlatTable(id),
descendant_id INT NOT NULL REFERENCES FlatTable(id),
PRIMARY KEY (ancestor_id, descendant_id)
);
Store all paths in the Closure Table, where there is a direct ancestry from one node to another. Include a row for each node to reference itself. For example, using the data set you showed in your question:
INSERT INTO ClosureTable (ancestor_id, descendant_id) VALUES
(1,1), (1,2), (1,4), (1,6),
(2,2), (2,4),
(3,3), (3,5),
(4,4),
(5,5),
(6,6);
Now you can get a tree starting at node 1 like this:
SELECT f.*
FROM FlatTable f
JOIN ClosureTable a ON (f.id = a.descendant_id)
WHERE a.ancestor_id = 1;
The output (in MySQL client) looks like the following:
+----+
| id |
+----+
| 1 |
| 2 |
| 4 |
| 6 |
+----+
In other words, nodes 3 and 5 are excluded, because they are part of a separate hierarchy, not descending from node 1.
Re: comment from e-satis about immediate children (or immediate parent). You can add a "path_length
" к ClosureTable
, чтобы облегчить запрашивать специально для непосредственного ребенка или родителя (или любое другое расстояние).
INSERT INTO ClosureTable (ancestor_id, descendant_id, path_length) VALUES
(1,1,0), (1,2,1), (1,4,2), (1,6,1),
(2,2,0), (2,4,1),
(3,3,0), (3,5,1),
(4,4,0),
(5,5,0),
(6,6,0);
Тогда можно добавить термин в поиске запросов непосредственных детей данного узла. Это потомки, чьи path_length
1.
SELECT f.*
FROM FlatTable f
JOIN ClosureTable a ON (f.id = a.descendant_id)
WHERE a.ancestor_id = 1
AND path_length = 1;
+----+
| id |
+----+
| 2 |
| 6 |
+----+
<час> комментарий Ре от @ashraf: "Как насчет того, чтобы сортировать целое дерево [по имени]?"
Вот запрос в качестве примера для возврата всех узлов, которые являются потомками узла 1, соединяют их с FlatTable, который содержит другие атрибуты узла такой как [1 112], и вид именем.
SELECT f.name
FROM FlatTable f
JOIN ClosureTable a ON (f.id = a.descendant_id)
WHERE a.ancestor_id = 1
ORDER BY f.name;
<час> комментарий Ре от @Nate:
SELECT f.name, GROUP_CONCAT(b.ancestor_id order by b.path_length desc) AS breadcrumbs
FROM FlatTable f
JOIN ClosureTable a ON (f.id = a.descendant_id)
JOIN ClosureTable b ON (b.descendant_id = a.descendant_id)
WHERE a.ancestor_id = 1
GROUP BY a.descendant_id
ORDER BY f.name
+------------+-------------+
| name | breadcrumbs |
+------------+-------------+
| Node 1 | 1 |
| Node 1.1 | 1,2 |
| Node 1.1.1 | 1,2,4 |
| Node 1.2 | 1,6 |
+------------+-------------+
<час> пользователь А предложил редактирование сегодня. ТАКИМ ОБРАЗОМ, модераторы утвердили редактирование, но я инвертирую его.
редактирование предложило, чтобы ORDER BY в последнем запросе выше был ORDER BY b.path_length, f.name
, по-видимому, чтобы удостовериться, что упорядочивание соответствует иерархии. Но это не работает, потому что это заказало бы "Узел 1.1.1" после "Узла 1.2".
, Если Вы хотите, чтобы упорядочивание соответствовало иерархии разумным способом, который возможен, но не просто путем упорядочивания длиной пути. Например, см. мой ответ на [1 119] MySQL Closure Table иерархическая база данных - Как вытащить информацию в правильном порядке .
Для Расширения решения для SQL счета, можно в основном сделать то же использование плоской антенной решетки. Далее больше, если Ваши строки, у всех есть та же длина и Ваше максимальное количество детей, известны (скажите в двоичном дереве), можно сделать это с помощью единственной строки (символьный массив). Если у Вас есть произвольное число детей, это усложняет вещи немного... Я должен был бы проверить свои старые примечания для наблюдения то, что может быть сделано.
Затем жертвуя небольшим количеством памяти, особенно если Ваше дерево редко и/или несбалансировано, Вы можете, с небольшим количеством индексной математики, доступ все строки случайным образом путем хранения дерева, ширина сначала в массиве как так (для двоичного дерева):
String[] nodeArray = [L0root, L1child1, L1child2, L2Child1, L2Child2, L2Child3, L2Child4] ...
эй знают Вашу длину строки, Вы знаете это
, я работаю теперь, так не может провести много времени на нем, но с интересом я могу выбрать немного кода, чтобы сделать это.
Мы используем, чтобы сделать это для поиска в двоичных деревьях, сделанных из кодонов DNA, процесс создал дерево, тогда мы сгладили его к шаблонам искомого текста и, когда найдено, хотя индексная математика (отворот сверху) мы возвращаем узел... очень быстрый и эффективный, жесткий, что наше дерево редко имело пустые узлы, но мы могли искать гигабайты данных в один миг.
Если вложенные карты хеша или массивы могут быть созданы, то я могу просто спуститься по таблице с начала и добавить каждый объект к вложенному массиву. Я должен проследить каждую строку до корневого узла для знания который уровень во вложенном массиве вставить в. Я могу использовать memoization так, чтобы я не должен был искать того же родителя много раз.
Редактирование: Я считал бы всю таблицу в массив сначала, таким образом, она не будет неоднократно запрашивать DB. Конечно, это не будет практично, если Ваша таблица будет очень большой.
После того, как структура создается, я должен сделать, глубина сначала пересекает через нее и распечатывает HTML.
нет никакого лучшего фундаментального способа хранить эту информацию с помощью одной таблицы (я мог быть неправым хотя;), и хотел бы видеть лучшее решение). Однако при создании схемы использовать динамично созданные таблицы базы данных тогда Вы открыли совершенно новый мир в жертве простоты и риске ада SQL;).
Если элементы находятся в древовидном порядке, как показано в Вашем примере, можно использовать что-то как следующий пример Python:
delimiter = '.'
stack = []
for item in items:
while stack and not item.startswith(stack[-1]+delimiter):
print "</div>"
stack.pop()
print "<div>"
print item
stack.append(item)
то, Что это делает, поддерживают стек, представляющий текущую позицию в дереве. Для каждого элемента в таблице это выталкивает элементы стека (закрывающий отделения соответствия), пока это не находит родителя текущего объекта. Тогда это производит запуск того узла и продвигает его к стеку.
, Если Вы хотите произвести расположение с отступом использования дерева, а не вложенные элементы, можно просто пропустить операторы печати, чтобы распечатать отделения и распечатать много пробелов, равных некоторым несколько из размера стека перед каждым объектом. Например, в Python:
print " " * len(stack)
Вы могли также легко использовать этот метод для построения ряда вложенных списков или словарей.
Редактирование: Я вижу от Вашего разъяснения, что имена не были предназначены, чтобы быть путями узла. Это предлагает альтернативный подход:
idx = {}
idx[0] = []
for node in results:
child_list = []
idx[node.Id] = child_list
idx[node.ParentId].append((node, child_list))
Это создает дерево массивов кортежей (!). idx [0] представляет корень (корни) дерева. Каждый элемент в массиве является с 2 кортежами, состоящим из самого узла и списка всех его детей. После того, как созданный, можно держаться за idx [0] и отбросить idx, если Вы не хотите получить доступ к узлам их идентификатором.
Предположение, что Вы знаете, что корневые элементы являются нулем, вот является псевдокодом для вывода к тексту:
function PrintLevel (int curr, int level)
//print the indents
for (i=1; i<=level; i++)
print a tab
print curr \n;
for each child in the table with a parent of curr
PrintLevel (child, level+1)
for each elementID where the parentid is zero
PrintLevel(elementID, 0)
Хорошо, учитывая выбор, я использовал бы объекты. Я создал бы объект для каждой записи, где каждый объект имеет children
набор, и сохраните их всех в массиве помощника (/хеш-таблица), где идентификатор является ключом. И блиц через набор однажды, добавляя детей к соответствующим дочерним полям. Простой.
, Но потому что Вы не забава путем ограничения использования некоторого хорошего ООП, я, вероятно, выполнил бы итерации на основе:
function PrintLine(int pID, int level)
foreach record where ParentID == pID
print level*tabs + record-data
PrintLine(record.ID, level + 1)
PrintLine(0, 0)
Редактирование: это подобно нескольким другим записям, но я думаю, что это немного более чисто. Одна вещь я добавлю: это чрезвычайно интенсивно SQL. Это противно . , Если у Вас есть выбор, пойдите путем ООП.
С Oracle 9i, можно использовать ПОДКЛЮЧЕНИЕ.
SELECT LPAD(' ', (LEVEL - 1) * 4) || "Name" AS "Name"
FROM (SELECT * FROM TMP_NODE ORDER BY "Order")
CONNECT BY PRIOR "Id" = "ParentId"
START WITH "Id" IN (SELECT "Id" FROM TMP_NODE WHERE "ParentId" = 0)
С SQL Server 2005, можно использовать рекурсивное общее выражение таблицы (CTE).
WITH [NodeList] (
[Id]
, [ParentId]
, [Level]
, [Order]
) AS (
SELECT [Node].[Id]
, [Node].[ParentId]
, 0 AS [Level]
, CONVERT([varchar](MAX), [Node].[Order]) AS [Order]
FROM [Node]
WHERE [Node].[ParentId] = 0
UNION ALL
SELECT [Node].[Id]
, [Node].[ParentId]
, [NodeList].[Level] + 1 AS [Level]
, [NodeList].[Order] + '|'
+ CONVERT([varchar](MAX), [Node].[Order]) AS [Order]
FROM [Node]
INNER JOIN [NodeList] ON [NodeList].[Id] = [Node].[ParentId]
) SELECT REPLICATE(' ', [NodeList].[Level] * 4) + [Node].[Name] AS [Name]
FROM [Node]
INNER JOIN [NodeList] ON [NodeList].[Id] = [Node].[Id]
ORDER BY [NodeList].[Order]
Оба произведут следующие результаты.
Name 'Node 1' ' Node 1.1' ' Node 1.1.1' ' Node 1.2' 'Node 2' ' Node 2.1'
Можно эмулировать любую другую структуру данных с hashmap, таким образом, это не ужасное ограничение. Сканируя от вершины до нижней части, Вы создаете hashmap для каждой строки базы данных с записью для каждого столбца. Добавьте каждый из этих hashmaps к "основному" hashmap, включил идентификатор. Если какой-либо узел имеет "родителя", которого Вы еще не видели, создаете запись заполнителя для него в основном hashmap и заполняете его, когда Вы видите фактический узел.
Для распечатывания его сделайте простую передачу в глубину через данные, отслеживание уровня отступа по пути. Можно сделать это легче путем хранения "дочерней" записи для каждой строки и заполнения ее, поскольку Вы сканируете данные.
Что касается того, существует ли "лучший" способ сохранить дерево в базе данных, которая зависит от того, как Вы собираетесь использовать данные. Я видел системы, которые имели известную максимальную глубину, которая использовала различную таблицу для каждого уровня в иерархии. Это имеет много смысла, если уровни в дереве не довольно эквивалентны, в конце концов (высокоуровневые категории, являющиеся отличающимся, чем листы).
Это было записано быстро, и не симпатично и не эффективно (плюс он, автополя много, преобразовывая между int
и Integer
являются раздражающими!), но это работает.
Это, вероятно, нарушает правила, так как я создаю свои собственные объекты, но эй я делаю это как диверсию от реальной работы:)
Это также предполагает, что набор результатов/таблица полностью читается в своего рода структуру, прежде чем Вы начнете создавать Узлы, которые не были бы лучшим решением, если у Вас есть сотни тысяч строк.
public class Node {
private Node parent = null;
private List<Node> children;
private String name;
private int id = -1;
public Node(Node parent, int id, String name) {
this.parent = parent;
this.children = new ArrayList<Node>();
this.name = name;
this.id = id;
}
public int getId() {
return this.id;
}
public String getName() {
return this.name;
}
public void addChild(Node child) {
children.add(child);
}
public List<Node> getChildren() {
return children;
}
public boolean isRoot() {
return (this.parent == null);
}
@Override
public String toString() {
return "id=" + id + ", name=" + name + ", parent=" + parent;
}
}
public class NodeBuilder {
public static Node build(List<Map<String, String>> input) {
// maps id of a node to it's Node object
Map<Integer, Node> nodeMap = new HashMap<Integer, Node>();
// maps id of a node to the id of it's parent
Map<Integer, Integer> childParentMap = new HashMap<Integer, Integer>();
// create special 'root' Node with id=0
Node root = new Node(null, 0, "root");
nodeMap.put(root.getId(), root);
// iterate thru the input
for (Map<String, String> map : input) {
// expect each Map to have keys for "id", "name", "parent" ... a
// real implementation would read from a SQL object or resultset
int id = Integer.parseInt(map.get("id"));
String name = map.get("name");
int parent = Integer.parseInt(map.get("parent"));
Node node = new Node(null, id, name);
nodeMap.put(id, node);
childParentMap.put(id, parent);
}
// now that each Node is created, setup the child-parent relationships
for (Map.Entry<Integer, Integer> entry : childParentMap.entrySet()) {
int nodeId = entry.getKey();
int parentId = entry.getValue();
Node child = nodeMap.get(nodeId);
Node parent = nodeMap.get(parentId);
parent.addChild(child);
}
return root;
}
}
public class NodePrinter {
static void printRootNode(Node root) {
printNodes(root, 0);
}
static void printNodes(Node node, int indentLevel) {
printNode(node, indentLevel);
// recurse
for (Node child : node.getChildren()) {
printNodes(child, indentLevel + 1);
}
}
static void printNode(Node node, int indentLevel) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < indentLevel; i++) {
sb.append("\t");
}
sb.append(node);
System.out.println(sb.toString());
}
public static void main(String[] args) {
// setup dummy data
List<Map<String, String>> resultSet = new ArrayList<Map<String, String>>();
resultSet.add(newMap("1", "Node 1", "0"));
resultSet.add(newMap("2", "Node 1.1", "1"));
resultSet.add(newMap("3", "Node 2", "0"));
resultSet.add(newMap("4", "Node 1.1.1", "2"));
resultSet.add(newMap("5", "Node 2.1", "3"));
resultSet.add(newMap("6", "Node 1.2", "1"));
Node root = NodeBuilder.build(resultSet);
printRootNode(root);
}
//convenience method for creating our dummy data
private static Map<String, String> newMap(String id, String name, String parentId) {
Map<String, String> row = new HashMap<String, String>();
row.put("id", id);
row.put("name", name);
row.put("parent", parentId);
return row;
}
}
При использовании вложенных наборов (иногда называемый Измененным Обходом дерева Предзаказа), можно извлечь всю древовидную структуру или любое поддерево в ней в древовидном порядке с единым запросом, за счет вставок, являющихся более дорогим, поскольку необходимо управлять столбцами, которые описывают чтобы путь через тебя древовидная структура.
Для [1 117] django-mptt, я использовал структуру как это:
id parent_id tree_id level lft rght -- --------- ------- ----- --- ---- 1 null 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12
, Который описывает дерево, которое похоже на это (с id
представление каждого объекта):
1 +-- 2 | +-- 3 | +-- 4 | +-- 5 +-- 6 +-- 7
Или, как вложенная схема набора, которая делает его более очевидным, как lft
и rght
значения работают:
__________________________________________________________________________ | Root 1 | | ________________________________ ________________________________ | | | Child 1.1 | | Child 1.2 | | | | ___________ ___________ | | ___________ ___________ | | | | | C 1.1.1 | | C 1.1.2 | | | | C 1.2.1 | | C 1.2.2 | | | 1 2 3___________4 5___________6 7 8 9___________10 11__________12 13 14 | |________________________________| |________________________________| | |__________________________________________________________________________|
, Как Вы видите, для получения всего поддерева для данного узла, в древовидном порядке, просто необходимо выбрать все строки, которые имеют lft
и rght
значения между lft
и rght
значения. Также просто получить дерево предков для данного узла.
level
столбец является небольшим количеством денормализации для удобства больше, чем что-нибудь и tree_id
, столбец позволяет Вам перезапускать lft
и rght
нумерация для каждого узла верхнего уровня, который сокращает количество столбцов, затронутых вставками, перемещениями и удалениями, как lft
и rght
, столбцы должны быть скорректированы соответственно, когда эти операции происходят, чтобы создать или преодолеть разрывы. Я сделал [приблизительно 1 118] примечания разработки в то время, когда я пытался обернуть голову вокруг запросов, требуемых для каждой операции.
С точки зрения фактического работания с этими данными для отображения дерева я создал tree_item_iterator
служебная функция, которая, для каждого узла, должна дать Вам достаточную информацию для генерации безотносительно вида дисплея, который Вы хотите.