Как представить дерево данных в SQL?

Я пишу древовидную структуру данных, которая объединена от Дерева и TreeNode. Дерево будет содержать корень и высокоуровневые действия с данными. Я пользуюсь библиотекой UI для представления дерева в форме окон, где я могу связать дерево с TreeView.

Я должен буду сохранить это дерево и узлы в DB. Что будет лучшим способом сохранить дерево и получить следующие функции:

  1. Интуитивная реализация.
  2. Легкая привязка. Будет легко переместиться от дерева до структуры DB и назад (если таковые имеются)

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

Какие-либо идеи?

44
задан Brian Tompsett - 汤莱恩 27 December 2015 в 19:55
поделиться

3 ответа

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

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

Если вы только когда-либо обращаетесь к целую дереву, модель списка соседних способов затрудняет извлечение всех узлов под корнем без использования рекурсивного запроса. Если вы добавите дополнительный столбец, который ссылается на голову, вы можете сделать Выберите * где head_id = @id и получите все дерево в одном нерешительном запросе, но оно денормалирует базу данных.

Некоторые базы данных имеют пользовательские расширения, которые делают хранение и извлечение наширархических данных проще, например, Oracle имеет соединение .

3
ответ дан 26 November 2019 в 22:06
поделиться

ligget78 дал мне еще одну идею, как сделать обновление одного столбца:

Создайте новый DataContext только для этого вида обновления и включите только необходимые столбцы в этот DataContext.

Таким образом ненужные столбцы даже не будут загружены и, конечно, не будут отправлены обратно в базу данных.

-121--3979504-

Да.

Во-первых, как вы заметили, вся документация написана для Objective-C, который является совершенно другим языком.

Одно различие - имя метода. В Objective-C при отправке сообщения (Python сказал бы «вызов метода») объекту имя метода (селектор) и аргументы смешиваются:

NSURL *URL = /*…*/;
NSError *error = nil;

QTMovie *movie = [QTMovie movieWithURL:URL
    error:&error];

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

movie = QTMovie.movieWithURL(URL, error = ???)

, вы бы получили исключение, потому что класс QTMovie не имеет метода с именем moviveURL ; сообщение в примере Objective-C использует селектор moveyWeyURL: error: . moveyWeyURL: и moveyWeyURL будут двумя другими селекторами.

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

foo = Foo.foo(fred, bar=bar, baz=baz)

Теперь это вызывает foo: bar: baz: , верно?

Не так быстро. Foo также может иметь метод с именем foo: baz: bar: . Поскольку аргументы ключевого слова Python не упорядочены, возможно, вы вызываете этот метод. Аналогично, если вы попытались вызвать foo: baz: bar: , вы можете в конечном итоге вызвать foo: bar: baz: . На самом деле этот случай маловероятен, но если бы он когда-либо случился, вы бы не смогли надежно вызвать ни один из методов.

Итак, в PyObjC необходимо вызвать метод так:

movie = QTMovie.movieWithURL_error_(URL, ???)

Возможно, вас интересует???. C не допускает множественных возвращаемых значений, поэтому в Objective-C аргумент error: принимает указатель на переменную указателя, и метод сохранит объект в этой переменной (это называется return-by-reference). Python не имеет указателей, поэтому способ обработки таких аргументов мостом заключается в том, что вы передаете None, и метод возвращает кортеж. Таким образом, правильный пример:

movie, error = QTMovie.movieWithURL_error_(URL, None)

Вы можете видеть, как даже простой пример отклоняется от того, что документация может показать вам в Objective-C.

Есть и другие проблемы, такие как GIL. Приложения для какао станут более параллельными, и вы захотите участвовать в этом, особенно с заманчивыми классами, такими как NSOperation. И GIL является серьезной ответственностью, особенно на многоядерных машинах . Я говорю это как Python парень сам (когда не пишу для Cocoa). Как демонстрирует Дэвид Бизли в этом видео, это холодный, трудный факт; Это нельзя отрицать.

Итак, если бы я собирался отказаться от Objective-C для своих приложений, я бы занялся MacRuby . В отличие от PyObjC и RubyCocoa, сообщения объектам Cocoa не пересекают языковой мост; это от-к-земле -до реализации Ruby в Cocoa, с расширениями языка, чтобы лучше поддерживать написание кода Cocoa в нем.

Но это слишком далеко впереди тебя. Ты только начинаешь. Начните с цели-C. Лучше избежать всех несоответствий импеданса между используемым языком и языком, для которого написана документация, сохранив их на одном языке.

Кроме того, вы найдете некоторые ошибки (например, сообщения умершим объектам) труднее диагностировать, не зная, как работает Objective-C. Вы будете писать эти ошибки как новый программист Cocoa, независимо от того, на каком языке вы пишете код.

Итак, выучить C, затем выучить Objective-C. Рабочее знание обоих не должно занимать больше нескольких недель, и в конце вы будете готовы ко всему остальному.

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

Что касается Objective-C: Книга Хиллегасса является самой популярной, но я ее не использовал. (Я скинул его, и он выглядит хорошо.) Я прочитал документ Apple на языке , затем прыгнул прямо в написание небольших Cocoa app. Я прочитал некоторые из руководств , со смешанными результатами. Существует учебник конвертера валют , но он совсем не помог мне, и не совсем отражает современное приложение Cocoa. (Современные приложения по-прежнему используют розетки и действия, но и Bindings, и реалистичный конвертер валют будет почти полностью пара привязок.)

-121--2654203-

Что-то вроде таблицы «nodes», где каждая строка узла содержит родительский идентификатор (в дополнение к обычным данным узла). Для root родительский элемент имеет значение NULL.

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

0
ответ дан 26 November 2019 в 22:06
поделиться

Это можно сделать несколькими способами, как показано ниже.

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

Метод A: Используйте AccountManager (уровень API 5 +)

Вы можете использовать AccountManager.getAccounts или AccountManager.getTaxType , чтобы получить список всех имен учетных записей на устройстве. К счастью, для определенных типов учетных записей (включая com.google ) имена учетных записей являются адресами электронной почты. Пример фрагмента ниже.

Pattern emailPattern = Patterns.EMAIL_ADDRESS; // API level 8+
Account[] accounts = AccountManager.get(context).getAccounts();
for (Account account : accounts) {
    if (emailPattern.matcher(account.name).matches()) {
        String possibleEmail = account.name;
        ...
    }
}

Обратите внимание, что для этого требуется разрешение GET _ ACCOUNTS :

<uses-permission android:name="android.permission.GET_ACCOUNTS" />

Дополнительные сведения об использовании AccountManager можно найти в образце кода менеджера контактов в SDK.

Метод B: Использовать ContactsContract. Профиль (уровень 14 API +)

С Android 4.0 (Ice Cream Sandwich), вы можете получить адреса электронной почты пользователя, получив доступ к их профилю. Доступ к профилю пользователя немного тяжел, так как требует двух разрешений (подробнее об этом ниже), но адреса электронной почты являются довольно чувствительными частями данных, поэтому это цена допуска.

Ниже приведен полный пример использования CursorLoader для извлечения строк данных профиля, содержащих адреса электронной почты.

public class ExampleActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor> {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getLoaderManager().initLoader(0, null, this);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle arguments) {
        return new CursorLoader(this,
                // Retrieve data rows for the device user's 'profile' contact.
                Uri.withAppendedPath(
                        ContactsContract.Profile.CONTENT_URI,
                        ContactsContract.Contacts.Data.CONTENT_DIRECTORY),
                ProfileQuery.PROJECTION,

                // Select only email addresses.
                ContactsContract.Contacts.Data.MIMETYPE + " = ?",
                new String[]{ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE},

                // Show primary email addresses first. Note that there won't be
                // a primary email address if the user hasn't specified one.
                ContactsContract.Contacts.Data.IS_PRIMARY + " DESC");
    }

    @Override
    public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
        List<String> emails = new ArrayList<String>();
        cursor.moveToFirst();
        while (!cursor.isAfterLast()) {
            emails.add(cursor.getString(ProfileQuery.ADDRESS));
            // Potentially filter on ProfileQuery.IS_PRIMARY
            cursor.moveToNext();
        }

        ...
    }

    @Override
    public void onLoaderReset(Loader<Cursor> cursorLoader) {
    }

    private interface ProfileQuery {
        String[] PROJECTION = {
                ContactsContract.CommonDataKinds.Email.ADDRESS,
                ContactsContract.CommonDataKinds.Email.IS_PRIMARY,
        };

        int ADDRESS = 0;
        int IS_PRIMARY = 1;
    }
}

Для этого требуются разрешения READ _ PROFILE и READ _ CONTACTS :

<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
-121--1809164-

Откройте приложение/config/config.php и задайте следующие значения:

$config['uri_protocol'] = "PATH_INFO";

$config['enable_query_strings'] = TRUE; 

Теперь последовательности запросов должны работать нормально.

-121--2519091-

Наиболее простой реализацией является структура списка смежности :

id  parent_id  data

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

Другая модель - это вложенные наборы :

id lft rgt data

, где lft и rgt - произвольные значения, определяющие иерархию (любой дочерний lft , rgt должен находиться в пределах любого родительского lft , rgt )

Однако в MySQL это может быть улучшено с помощью abitilies SPATIAL .

См. следующие статьи в моем блоге:

для получения более подробных объяснений.

34
ответ дан 26 November 2019 в 22:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: