Функция итерации MySQL или VBA для поиска взаимосвязанных записей - PullRequest
0 голосов
/ 30 ноября 2011

Я использую MS Access с базой данных MySQL.В одной таблице есть записи, которые представляют «документы»:

Table: doc
Primary Key: doc_id

Эти записи могут быть связаны вместе с помощью таблицы ссылок:

Table: link
Primary Key: link_id
Foreign Key: doc_id_A
Foreign Key: doc_id_B

Таким образом, записи могут быть связаны в цепочке, напримерДокумент A связан с документом B, который связан с документом C и т. Д., А также документ A может быть связан с любым количеством других документов.

В действительности «семейство» взаимосвязанных документов не будетне превышает 20 записей.

Я ищу эффективный метод MySQL, или функцию VBA, или запрос, чтобы найти всех членов «семейства» для одной указанной записи.

Любые предложения будут приветствоваться!

Ответы [ 3 ]

1 голос
/ 08 декабря 2011

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

  1. Ссылка на себя
  2. ... столько раз, сколько оналайки
  3. Ссылка на другой документ
  4. ... несколько раз
  5. Отдельно связана с тем же другим документом
  6. ... несколько раз
  7. Хранить информацию о ссылке

Таким образом, ваша таблица ссылок может иметь 10 отдельных ссылок 1-1, 1-1, 1-1, 1-2, 1-2, 1-2, 2-1, 2-1, 2-1, 2-2 и т. Д. С двумя документами в «семье».


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

Но в любом случае, вот мой ответ, он протестирован и работает в Access-2010 и локальных таблицах.ADODB должен так же хорошо работать со связанными таблицами.

Option Compare Database
Option Explicit
Const MaxInFamily = 30

'Requires a reference to "Microsoft ActiveX Data Objects 2.x Library" (VBA Menu: Tools, references)

Function GetFamily(id As Long) As Long()
Dim Found(MaxInFamily) As Long
Dim MaxFound As Integer
Dim CurrentSearch As Integer
Dim Sql As String
Dim rs As New ADODB.Recordset

    Found(1) = id
    MaxFound = 1

    For CurrentSearch = 1 To MaxInFamily

        If CurrentSearch > MaxFound Then Exit For

        Sql = "SELECT doc_id_2 as NewID FROM link WHERE doc_id_1 = " & Found(CurrentSearch) _
                & " AND doc_id_2 NOT IN (" & ArrayToCsv(Found, MaxFound) & ")" _
                & " UNION " _
                & " SELECT doc_id_1 FROM link WHERE doc_id_2 = " & Found(CurrentSearch) _
                & " AND doc_id_1 NOT IN (" & ArrayToCsv(Found, MaxFound) & ")"

        rs.Open Sql, CurrentProject.Connection

        Do While Not rs.EOF
            MaxFound = MaxFound + 1
            Found(MaxFound) = rs("NewID").Value
            rs.MoveNext
        Loop

        rs.Close

    Next CurrentSearch

    GetFamily = Found

End Function

Function ArrayToCsv(SourceArray() As Long, ItemCount As Integer) As String
Dim Csv As String
Dim ArrayIndex As Integer

    For ArrayIndex = 1 To ItemCount
        Csv = Csv & SourceArray(ArrayIndex)
        If ArrayIndex < ItemCount Then Csv = Csv & ", "
    Next ArrayIndex

    ArrayToCsv = Csv

End Function

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

1 голос
/ 12 декабря 2011

У меня была похожая проблема некоторое время назад. Вместо создания ссылок между документами я использовал отдельную таблицу, содержащую семейства:

Table: LinkTable
PK: LinkId, DocumentID
FK: DocumentID

Вместо того, чтобы иметь пары идентификаторов документов, каждый раз, когда вы создаете новое семейство, вы вводите новый LinkId.

LinkId | DocumentID

1      | 7
1      | 9
1      | 13
2      | 4
2      | 22
2      | 23
2      | 30
3      | 6
3      | 80

Здесь у вас есть три семьи с идентификаторами документов:

{ 7, 9, 13 },
{ 4, 22, 23, 30 },
{ 6, 80 }

Извлекать семейства проще, однако для вставки и удаления документов в семейства и из них требуется несколько более сложная логика.

1 голос
/ 07 декабря 2011

К сожалению, это невозможно сделать в MySQL, учитывая такую ​​структуру таблиц, как MySQL не поддерживает рекурсивные запросы. Я предлагаю изучить ответы в Mysql рекурсии? - есть несколько советов о том, как хранить данные в MySQL, чтобы можно было писать такие запросы.

У вас также есть три других варианта:

Если вы знаете макс. глубина таких семейств, и она не огромная, вы все равно можете достичь этого с помощью MySQL. Для глубины 3 уровня это будет выглядеть так:

SELECT A.doc_id_a as a, B.doc_id_a a_child, C.doc_id_a as a_sub_child 
FROM links as A, links as B, links as C 
WHERE A.doc_id_a = your_doc_id AND
      A.doc_id_b = B.doc_id_a AND
      B.doc_id_b = C.doc_id_a

Следуя той же логике, вы можете добавить столько слоев, сколько вам нужно. Единственное - вам нужно получить результат из всех столбцов и найти уникальные значения (и может быть много строк, если отношение не всегда 1-1).

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

family = array();
family = getFamily('your_doc_id', family);

function getFamily(id) {
   children = DB->getColumn('SELECT doc_id_b FROM links WHERE doc_id_a = ?', id);
   if (empty(children)) return family;
   else {
     foreach (children as child) {
       family[] = getFamily(child);
     }
   }
 }

Наконец, вы можете переключиться на PostgreSQL, который поддерживает рекурсивные запросы :) (http://www.postgresql.org/docs/8.4/static/queries-with.html).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...