Я пытаюсь понять, как правильно хранить упорядоченную информацию в реляционной базе данных.
Пример:
Скажем, у меня есть плейлист, состоящий из песен. Внутри моей реляционной базы данных у меня есть таблица Playlists
, содержащая некоторые метаданные (имя, создатель и т. Д.). У меня также есть таблица с именем Songs
, playlist_id
а также информация о песне (имя, исполнитель, продолжительность и т. Д.).
По умолчанию, когда новая песня добавляется в список воспроизведения, она добавляется в конец. При заказе на Song-ID (по возрастанию), порядок будет порядок добавления. Но что если пользователь сможет переупорядочить песни в плейлисте?
Я выдвинул пару идей, каждая из которых имела свои преимущества и недостатки:
- Столбец с именем
order
, который является целым числом . Когда песня перемещается, порядок всех песен между ее старым и новым положением изменяется, чтобы отразить это изменение. Недостатком этого является то, что каждый раз, когда песня перемещается, нужно выполнять множество запросов, и алгоритм перемещения не такой тривиальный, как с другими опциями. - Столбец с именем
order
, который является десятичным (NUMERIC
). Когда песня перемещается, ей присваивается значение с плавающей запятой между двумя соседними числами. Недостаток: десятичные поля занимают больше места, и, возможно, из-за них может не хватить точности, если только не будут приняты меры по перераспределению диапазона после каждых нескольких изменений. - Другим способом было бы иметь
previous
иnext
поле, которое ссылается на другие песни. (или имеют значение NULL в случае с первой, или последней песней в списке воспроизведения прямо сейчас; в основном вы создаете связанный список ). Недостаток: запросы типа «найти X-ую песню в списке» больше не являются постоянными, а имеют линейное время.
Какая из этих процедур чаще всего используется на практике? Какая из этих процедур является самой быстрой в средних и больших базах данных? Есть ли другие способы архивировать это?
РЕДАКТИРОВАТЬ: для простоты, в примере песня принадлежит только одному списку воспроизведения (отношение многие-к-одному). Конечно, можно также использовать Junction Table, чтобы song⟷playlist был отношением «многие ко многим» (и примените одну из вышеуказанных стратегий к этой таблице).
update songorder set order = order - 1 where order >= 12 & order <= 42; update songorder set order = 42 where id = 123;
это два обновления - не тридцать. Три, если вы хотите наложить уникальное ограничение на порядок.Queries like 'find the Xth Song in the list' are no longer constant-time
верно и для варианта 2.Ответы:
Базы данных оптимизированы для определенных вещей. Быстрое обновление большого количества строк является одним из них. Это становится особенно актуальным, когда вы позволяете базе данных выполнять свою работу.
Рассмотреть возможность:
И вы хотите перейти
Beat It
к концу, у вас будет два запроса:Вот и все. Это очень хорошо масштабируется с очень большими числами. Попробуйте поместить несколько тысяч песен в гипотетический список воспроизведения в вашей базе данных и посмотрите, сколько времени потребуется для перемещения песни из одного места в другое. Поскольку они имеют очень стандартизированные формы:
У вас есть два подготовленных заявления, которые вы можете использовать очень эффективно.
Это дает некоторые существенные преимущества - порядок таблиц можно обдумать. Третья песня имеет
order
3, всегда. Единственный способ гарантировать это - использовать последовательные целые числа в качестве порядка. Использование псевдосвязанных списков или десятичных чисел или целых чисел с пробелами не позволит вам гарантировать это свойство; в этих случаях единственный способ получить n-ю песню - это отсортировать всю таблицу и получить n-ную запись.И действительно, это намного проще, чем вы думаете. Просто выяснить, что вы хотите сделать, сгенерировать два оператора обновления, а другим людям посмотреть на эти два оператора обновления и понять, что делается.
источник
order
такorder by
как это ключевое слово?order
значение при добавлении новой песни в список воспроизведения. Скажем, это 9-я песня, есть ли лучший способ вставить 9,order
чем делатьCOUNT
до добавления записи?Прежде всего, из вашего описания того, что вы сделали, неясно, но вам нужна
PlaylistSongs
таблица, которая содержит aPlaylistId
и aSongId
, описывающие, какие песни принадлежат каким плейлистам.Именно в этой таблице вы должны добавить информацию для заказа.
Мой любимый механизм с реальными числами. Я реализовал это недавно, и это сработало как шарм. Когда вы хотите переместить песню в определенную позицию, вы вычисляете ее новое
Ordering
значение как среднее значениеOrdering
значений предыдущей и следующей песни. Если вы используете 64-битное действительное число, вы достигнете точности примерно в то же самое время, когда ад замерзнет, но если вы действительно пишете свое программное обеспечение для потомков, тогда подумайте о переназначении хороших округленных целочисленныхOrdering
значений для всех песен в каждой плейлист время от времени.В качестве дополнительного бонуса, вот код, который я написал, который реализует это. Конечно, вы не можете использовать его как есть, и для меня сейчас было бы слишком много работы по его дезинфекции для вас, поэтому я только публикую его, чтобы вы могли извлечь из него идеи.
Класс
ParameterTemplate
(что угодно, не спрашивайте!) Метод получает список шаблонов параметров, к которым принадлежит этот шаблон, от его родителяActivityTemplate
. (Как бы то ни было, не спрашивайте!) Код содержит некоторую защиту от нехватки точности. Делитель используется для тестирования: в модульном тесте используется большой делитель, чтобы быстро выйти за пределы точности и, таким образом, активировать защитный код точности. Второй метод является общедоступным и «только для внутреннего использования; не вызывать», чтобы тестовый код мог его вызывать. (Он не может быть закрытым для пакета, потому что мой тестовый код не находится в том же пакете, что и код, который он тестирует.) Поле, которое управляет порядком, вызываетсяOrdering
, вызывается черезgetOrdering()
иsetOrdering()
. Вы не видите SQL, потому что я использую объектно-реляционное отображение через Hibernate.источник
Что сработало для меня, для небольшого списка из порядка 100 наименований было использовать гибридный подход:
Таким образом, вы получите целочисленный порядок без пробелов, сохраненный в десятичном столбце. Это довольно чисто, я чувствую. Но он может не очень хорошо масштабироваться, если у вас есть сотни тысяч строк, которые нужно обновить, и все сразу. Но если да, то почему вы используете пользовательскую сортировку? (Примечание: если у вас большая таблица с миллионами пользователей, но у каждого пользователя есть только несколько сотен элементов для сортировки, вы можете использовать описанный выше подход очень хорошо, так как в любом случае вы будете использовать предложение where, чтобы ограничить изменения только одним пользователем )
источник