Форум программистов, компьютерный форум, киберфорум
SQLite
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.62/34: Рейтинг темы: голосов - 34, средняя оценка - 4.62
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
1

Как правильно обновить id после удаления записи

11.12.2023, 18:15. Показов 6276. Ответов 96
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Всех приветствую. У меня есть таблица,в которой первичный ключ не autoincrement rowid,поэтому я сам создал колонку rowid:
SQL
1
CREATE TABLE IF NOT EXISTS  favourite_posts (ROWID INTEGER,id INTEGER,name TEXT,DATE TEXT,author TEXT,content TEXT,url TEXT,PRIMARY KEY(id),UNIQUE(ROWID))
Я не делал rowid autoincrement,т.к я хочу,чтобы id были упорядоченными,а autoincrement за этим не следит. К примеру,после удаления поста с rowid 2 rowid будут равны 1 и 3,чего мне бы не хотелось. После удаления поста я пытаюсь обновить таблицу так:
SQL
1
UPDATE TABLE favourite_posts SET ROWID =ROWID-1 WHERE ROWID >deleted_row_id
Но,к примеру,при попытке удалить пост с rowid 1 и обновить rowid для значений 2,3 я получаю ошибку типа unique constraint,поскольку,как я понимаю,sqlite вначале пытается обновить rowid со значением 3,заменив его на 2,но ведь значение 2 уже есть. Подскажите пожалуйста,как изменить порядок обновления колонки ROWID,или как наиболее оптимально обновить записи в бд после удаления записи. Понимаю,что многие скажут мне,что-то типа того,я занимаюсь ерундой,но во-первых мне будет приятнее,чтобы roid был упорядоченным,а во-вторых я прочитал,что limit с offset менее оптимальны (а они мне будут нужны в запросах) чем выбор по условию where. Я могу гарантировать,что будет удаляться только один пост (хотя я не откажусь,если мне кто-то напишет запрос,который будет работать,если мы удалим сразу несколько постов) и что следующий ROWID всегда на 1 больше предыдущего. Заранее всех благодарю за помощь.
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
11.12.2023, 18:15
Ответы с готовыми решениями:

Как обновить данные в datagrid view после удаления строки?
1. Удаляю строку 2. Восстанавливаю базу из резервной копии (напрямую из программы) 3. Жму кнопку обновить (код ниже), - ничего не...

FireBase + RecyclerView| как обновить список, после удаления item из базы?
Добрый всем день! есть метод который удаляет айтем (значения items берутся из FireBase): public void DelPos(View v){ ...

Как обновить datagridview после добавления записи?
Как можно обновить datagridview после добавленич записи? string connectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data...

96
 Аватар для Аватар
3895 / 1414 / 494
Регистрация: 31.05.2012
Сообщений: 4,968
18.12.2023, 12:09 41
Author24 — интернет-сервис помощи студентам
Цитата Сообщение от Swa111 Посмотреть сообщение
Ни какой боязни. Для простоты понимая автору
там цитата от ТС, ему и адресовано
Цитата Сообщение от Swa111 Посмотреть сообщение
Вы не правы, будет сначала выполнен подзапрос
а я и не утверждал обратного, слово формально о чем то говорит? и далее по тексту об оптимизаторе и плане
0
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
18.12.2023, 16:48  [ТС] 42
Да, смотрел. объяснение тут https://www.sqlite.org/optoverview.html (п. 13)
Мне всё равно непонятно,почему при прибавлении 1 пересканируется вся таблица,хотя в примере был этот запрос и о проблемах не писалось,однако на stackoverflow вроде о проблемах написали.
Ни какой боязни. Для простоты понимая автору. Что Join что in дадут одинаковый план.
Кстати о планах. Для понимания начал отлаживать планы в своих таблицах. Возможно у меня старый sqlite в android,но почему-то при запросе:
SQL
1
SELECT id,name,DATE FROM favourite_posts LIMIT 20 OFFSET 0
Выдаётся план:
6 0 0 SCAN TABLE favourite_posts
А вот при запросе
SQL
1
SELECT id FROM favourite_posts LIMIT 20 OFFSET 0
Выдаётся план SCAN TABLE favourite_posts USING COVERING INDEX sqlite_autoindex_favourite_posts_1. Почему в первом случае не используется индекс по id? Если я буду выбирать content у поста с определённым id,то он тоже будет сканировать всю таблицу вместо использования индекса? Я не делал индексы по другим полям,т.к не планирую,к примеру,проводить поиск по имени поста и т.д,а если захочу,то создам эти индексы.
Это потребует два полных прохода по обоим таблицам, но будет быстрее чем постоянное обращение к индексу на post_category_relation
Обоснуйте пожалуйста почему?
тот запрос что у Вас можно с небольшой доработкой использовать в триггере, в нем проверять если это была последняя ссылка на категорию то удали категорию
А почему он изначально не подходит и как его доработать для триггера? Возможно мне вообще стоит удалять категории перед удалением поста,т.е если у нас есть только один пост в категориях и его id равен id удаляемого поста,то удалять их,хотя не уверен,что я не нарвусь на ошибку,т.к у меня есть соответствующие внешние ключи. Или мой вариант,т.е удалять все категории,у которых нет постов,быстрее?
Важно что бы были индексы tags(id) и TagsOfPosts(idPost, ...)
Ну если эти поля первичные ключи или unique,вроде с индексами проблем быть не должно.
0
754 / 271 / 57
Регистрация: 01.06.2023
Сообщений: 750
18.12.2023, 17:23 43
Мне всё равно непонятно,почему при прибавлении 1 пересканируется вся таблица,хотя в примере был этот запрос и о проблемах не писалось,однако на stackoverflow вроде о проблемах написали.
У меня не получилось воспроизвести данную проблему, возможно зависит от версии библиотеки, что бы не напороться просто ни чего не делайте с результатом max. См. пример в предыдущих постах.

Выдаётся план SCAN TABLE favourite_posts USING COVERING INDEX sqlite_autoindex_favourite_posts_1. Почему в первом случае не используется индекс по id? Если я буду выбирать content у поста с определённым id,то он тоже будет сканировать всю таблицу вместо использования индекса? Я не делал индексы по другим полям,т.к не планирую,к примеру,проводить поиск по имени поста и т.д,а если захочу,то создам эти индексы.
Различия в колонках. Во втором случае вы запрашиваете только id, для ее вывода достаточно просканировать индекс и не заходить в основную таблицу для извлечения дополнительных данных. (Физически индексы отличные от rowid являются такими же дополнительными таблицами с ссылками на строки основной таблицы) Индексы обычно меньше по размеру чем основная таблица поэтому "ленивый" оптимизатор выбирает прочитать меньше данных по индексу. План собственно об этом говорит USING COVERING INDEX - используется покрывающий индекс, т.е. индекс включающий в себя все запрошенные столбцы

Обоснуйте пожалуйста почему?
SQL
1
DELETE FROM categories WHERE NOT EXISTS(SELECT id FROM post_category_relation WHERE id=categories.id)
Читается так пройдись по каждой записи categories, определи есть ли связанная запись в таблице post_category_relation, если нет то удали. т.е. допустим имеем 1001 запись в categories. 2000 записей post_category_relation, только одна запись categories осиротела. Нам нужно прочитать 1001 запись из categories и 1001 раз проверить по индексу post_category_relation. Индексы организованы как двоичное дерево т.е. что бы найти нужную запись (а затем инвертировать результат) понадобиться 10 переходов по индексу, т.е. 10010 чтений индекса post_category_relation(idCategory). По факту нужно будет потрогать все записи из таблицы post_category_relation.

SQL
1
2
3
4
5
DELETE FROM categories WHERE categories.id IN (
  SELECT id FROM categories 
  EXCEPT 
  SELECT idCategory FROM post_category_relation 
)
читается так считай по индексу 1001 запись из categories, прочитай из post_category_relation 2000 записей idCategory, удали из первого массива элементы встречающиеся во втором. Удали из таблиц categories строки id которых есть в предыдущем результате.

Ну если эти поля первичные ключи или unique,вроде с индексами проблем быть не должно.
У Вас часто меняется концепция по полям и индексам, сложно уследить

А почему он изначально не подходит и как его доработать для триггера? Возможно мне вообще стоит удалять категории перед удалением поста,т.е если у нас есть только один пост в категориях и его id равен id удаляемого поста,то удалять их,хотя не уверен,что я не нарвусь на ошибку,т.к у меня есть соответствующие внешние ключи. Или мой вариант,т.е удалять все категории,у которых нет постов,быстрее?
SQL
1
2
3
4
5
 CREATE TRIGGER categoriesOfPosts_delete after DELETE ON categoriesOfPosts 
    WHEN (NOT EXISTS(SELECT 1 FROM categoriesOfPosts WHERE idCategory = OLD.idCategory))
    BEGIN
      DELETE FROM categories WHERE id = OLD.idCategory
    END ;
Читается следующим образом после удаления строки в categoriesOfPosts проверь остались строки в таблице categoriesOfPosts строки с такой же категорией как и удаленной строки. Если не осталось, удали строку из таблицы categories.

после этого как только будет удалена последняя строка из связки, вместе с ней удалиться и сама категория. ВНИМАНИЕ!!! это не сработает если обновить категорию через UPDATE.
0
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
18.12.2023, 18:24  [ТС] 44
используется покрывающий индекс, т.е. индекс включающий в себя все запрошенные столбцы
Получается чтобы выбрать только content нужного мне поста,или несколько столбцов,мне нужно создать дополнительные индексы,если столбцы не первичные ключи и не уникальные?
что бы найти нужную запись (а затем инвертировать результат) понадобиться 10 переходов по индексу
Т.е это число всегда будет равно 10,а значит количество будет равно 1001*10,или может быть и другое количество переходов? Просто откудо это число 10? Вот эту информацию,наверное,даже план мне не покажет.
Читается следующим образом после удаления строки в categoriesOfPosts проверь остались строки в таблице categoriesOfPosts строки с такой же категорией как и удаленной строки. Если не осталось, удали строку из таблицы categories.
Как я понимаю вариант 3 с триггером самый оптимальный?,или всё-таки второй лучше? Кстати что за таблица OLD?
0
 Аватар для Аватар
3895 / 1414 / 494
Регистрация: 31.05.2012
Сообщений: 4,968
18.12.2023, 18:59 45
Цитата Сообщение от КАВ Посмотреть сообщение
Просто откудо это число 10?
стоит почитать что такое двоичный или бинарный поиск можно даже в вики. для 10000 это число будет 14
0
754 / 271 / 57
Регистрация: 01.06.2023
Сообщений: 750
18.12.2023, 22:38 46
Получается чтобы выбрать только content нужного мне поста,или несколько столбцов,мне нужно создать дополнительные индексы,если столбцы не первичные ключи и не уникальные?
Нет, индексы нужны только для поиска, если поле не поисковое, то не нужно его включать в индекс. Ни чего страшного если придется сходить еще в основную таблицу за дополнительными данными. Как я написал выше индекс физически на диске это еще одна упорядоченная таблица. т.е. чем больше индексов тем больше места нужно на диске, тем дольше будет происходить вставка/удаление (хотя это будет заметно только если вставлять тысячами строк)

Т.е это число всегда будет равно 10
Вообще примерно 11 - это степень двойки для хранения узлов бинарного дерева мощностью в 2000 элементов.
Опять же это примерное число. И тем не менее чем больше данных тем дольше поиск. здесь как бы чудес не бывает.

Кстати что за таблица OLD?
Если уж так решили удариться в оптимизацию, то советую сначала почитать теорию CREATE TRIGGER, о том что собрались оптимизировать. Но по-моему мнению начали слишком рано, сформируйте сначала скелет приложения и если не будет удовлетворять производительность, то оптимизируете. Как бы не прискорбно было это признавать, но в современном мире выигрывает тот, кто раньше конкурентов выдаст продукт на рынок в более менее юзабельном виде.

Как я понимаю вариант 3 с триггером самый оптимальный?,или всё-таки второй лучше?
с триггером будет все само происходить, не нужно городить логику вне СУБД, главное не меняйте категории через update, только вставка и удаление строк в categoriesOfPosts
0
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
18.12.2023, 22:52  [ТС] 47
Обоснуйте пожалуйста,почему 14?
0
754 / 271 / 57
Регистрация: 01.06.2023
Сообщений: 750
18.12.2023, 22:58 48
Обоснуйте пожалуйста,почему 14?
Аватар хотел сказать что для обхода двоичного дерева мощностью 10000 элементов понадобиться сделать примерно 14 шагов. Опять же это ближайшая степень двойки. Не забивайте себе голову, эти тонкости Вам не пригодятся.
0
 Аватар для Аватар
3895 / 1414 / 494
Регистрация: 31.05.2012
Сообщений: 4,968
18.12.2023, 23:02 49
Цитата Сообщение от КАВ Посмотреть сообщение
почему 14
вернее 13: ceil(log2(10000 - 1))
0
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
18.12.2023, 23:21  [ТС] 50
Ни чего страшного если придется сходить еще в основную таблицу за дополнительными данными.
Просто мне не нравится то,что он сканирует всю таблицу,когда этого,на мой взгляд,можно не делать. Получается я ничего не смогу с этим сделать,кроме создания дополнительного индекса? Это будет происходить только при выборе с offset и limit,т.е там,где нет условия where?
чем больше индексов тем больше места нужно на диске, тем дольше будет происходить вставка/удаление
Это потому,что при вставке/удалении нужно будет снова всё переиндексировать?
Если уж так решили удариться в оптимизацию, то советую сначала почитать теорию CREATE TRIGGER, о том что собрались оптимизировать.
Как я понял,немного прочитав про это,old и new это старые и новые значения,но мне непонятно,как это работает,т.е что мы перед удалением можем знать,какое будет новое значение,хотя возможно я неправильно понял принцип ключевых слов old и new.
с триггером будет все само происходить, не нужно городить логику вне СУБД, главное не меняйте категории через update, только вставка и удаление строк в categoriesOfPosts
Это потому,что при обновлении значений старая запись удаляется,а значит сработает триггер? Я понимаю,что триггер служит для автоматизации процессов,т.е чтобы не писать лишний java код,а сделать это со стороны бд (кстати интересно,будет ли он показан в explain query plan)? Но ведь при желании в триггер можно вставить и второй вариант,поэтому я и спрашиваю,какой вариант оптимальный - второй или третий.

Добавлено через 2 минуты
Ну ближайшая степень это всё же 13,а не 14,или нужна степень большая этого числа?
0
754 / 271 / 57
Регистрация: 01.06.2023
Сообщений: 750
18.12.2023, 23:34 51
Просто мне не нравится то,что он сканирует всю таблицу,когда этого,на мой взгляд,можно не делать. Получается я ничего не смогу с этим сделать,кроме создания дополнительного индекса? Это будет происходить только при выборе с offset и limit,т.е там,где нет условия where?
Давайте так, присылайте конкретные запросы в которых Вы сомневаетесь или же, если запрос не получается составить, то сначала присылаете схему БД и не то как создаете в java, а с нормальными именами, в идеале если приложите сам db файл (можно ограниченный), затем задете вопрос "как сделать то то".

Это потому,что при вставке/удалении нужно будет снова всё переиндексировать?
Не совсем, нужно найти место в дереве куда вставить новую строку. Просто запомните, что индексы очень хороши для поиска, но приходится платить дополнительным местом, и временем на вставку, обновление и удаление строк.

Как я понял,немного прочитав про это,old и new это старые и новые значения,но мне непонятно,как это работает,т.е что мы перед удалением можем знать,какое будет новое значение,хотя возможно я неправильно понял принцип ключевых слов old и new.
Прочитайте еще раз документацию, там расписано что, когда можно использовать.

Это потому,что при обновлении значений старая запись удаляется,а значит сработает триггер? Я понимаю,что триггер служит для автоматизации процессов,т.е чтобы не писать лишний java код,а сделать это со стороны бд (кстати интересно,будет ли он показан в explain query plan)? Но ведь при желании в триггер можно вставить и второй вариант,поэтому я и спрашиваю,какой вариант оптимальный - второй или третий.
В Вашем случае триггер лучший выбор. Отдайте все на откуп БД.

Но ведь при желании в триггер можно вставить и второй вариант,
Второй вариант для массового применения и не очень годится для точечного использования. И да, запросы часто приходится писать по разному в зависимости от объема данных.
0
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
19.12.2023, 01:45  [ТС] 52
Давайте так, присылайте конкретные запросы в которых Вы сомневаетесь
Так я же писал про explain query plan и разницу,когда мы выбираем только id,и когда выбираем ещё и другие колонки,т.е в первом случае используется индекс,во втором нет. И у меня возник вопрос,можно ли как-то улучшить этот запрос,т.е чтобы хотябы использовался cover index,как при первом запросе.
И да, запросы часто приходится писать по разному в зависимости от объема данных.
Т.е для небольшого объёма данных может быть лучше один запрос,а для большого другой?
0
754 / 271 / 57
Регистрация: 01.06.2023
Сообщений: 750
19.12.2023, 08:11 53
И у меня возник вопрос,можно ли как-то улучшить этот запрос,т.е чтобы хотябы использовался cover index,как при первом запросе.
Использование покрывающих индексов это приятный бонус от использования индексов которые были созданы для ускорения поиска. не нужно все пихать в индексы, даже если Вы выиграете в конкретном запросе, скорее всего проиграете во всех остальных. Операции чтения обычно самые дешевые.

Т.е для небольшого объёма данных может быть лучше один запрос,а для большого другой?
Именно, для составления запроса мало понимать схему БД, нужно еще знать распределение тех или иных ключей, объем данных. А еще они могут быть разными от поставленных задач. Например так называемый второй запрос предназначен для массовой вычистки осиротевших категорий. Третий (тот что с триггером) для оперативного удаления
0
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
19.12.2023, 15:49  [ТС] 54
Использование покрывающих индексов это приятный бонус
Т.е если я вижу в плане что-то типа scan table,но без использования индекса,то даже для очень больших запросов мне ничего не надо оптимизировать,как например в моём случае?
Именно, для составления запроса мало понимать схему БД, нужно еще знать распределение тех или иных ключей,
А как это распределение узнать и как формировать оптимальные запросы,исходя из этого распределения? Есть ли полезное чтиво для понимания этих процессов с подробными примерами,показывающими разные пути решения одной задачи с подробными объяснениями,почему тот или иной подход в данном случае наиболее оптимальный?
Например так называемый второй запрос предназначен для массовой вычистки осиротевших категорий. Третий (тот что с триггером) для оперативного удаления
Так по сути мне и надо массово удалять осиротевшие категории/теги,т.к один пост может содержать несколько категорий,или триггер всё же будет работать быстрее? Я не пытаюсь спорить,просто я хочу этот момент понять для себя. Кстати если при запросе с триггером удалить все посты:
SQL
1
DELETE FROM posts
То триггер автоматом удалит все категории/теги? Просто у меня в приложении есть и такая функция. Кстати раз речь пошла про запросы,я хочу вернуть пост,если он есть,который идёт перед/после поста,который будет в начале/в конце списка (зависит от того,по возрастанию ли пойдут посты,или по убыванию). Я думаю выполнить запрос так:
По возрастанию:
SQL
1
SELECT id,name,DATE FROM posts WHERE ROWID >(SELECT ROWID FROM POSTS WHERE id=some_number) LIMIT 1
По убыванию:
SQL
1
SELECT id,name,DATE FROM posts WHERE ROWID <(SELECT ROWID FROM POSTS WHERE id=some_number) LIMIT 1
Где some_number - id моего поста в конце списка.
Я бы мог,конечно,хранить rowid в поле объекта post,и тогда бы мой запрос упростился,но я не хочу это делать,т.к мне сервер не отдаёт rowid. Насколько оптимальный мой запрос?
0
754 / 271 / 57
Регистрация: 01.06.2023
Сообщений: 750
19.12.2023, 16:14 55
Т.е если я вижу в плане что-то типа scan table,но без использования индекса,то даже для очень больших запросов мне ничего не надо оптимизировать,как например в моём случае?
Не всегда. Если таблица большая то это плохо. если маленькая (умещается на страниц данных) то сойдет.

- SEARCH TABLE USING COVERING INDEX - очень хорошо
- SEARCH TABLE USING PRIMARY KEY - тоже хорошо, но если строки жирные, то хуже чем предыдущий вариант
- SEARCH TABLE USING INDEX - хороший вариант, но хуже двух предыдущих
- SCAN TABLE - плохо для больших таблиц, нормально для маленьких

А как это распределение узнать и как формировать оптимальные запросы,исходя из этого распределения? Есть ли полезное чтиво для понимания этих процессов с подробными примерами,показывающими разные пути решения одной задачи с подробными объяснениями,почему тот или иной подход в данном случае наиболее оптимальный?
Как разработчик БД вы должны знать это распределение. Например что в бд будет храниться определенное количество постов, тегов и категорий. Для SQLite не встречал обучающих курсов где это паказывали.

Так по сути мне и надо массово удалять осиротевшие категории/теги,т.к один пост может содержать несколько категорий,или триггер всё же будет работать быстрее? Я не пытаюсь спорить,просто я хочу этот момент понять для себя. Кстати если при запросе с триггером удалить все посты:

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

То триггер автоматом удалит все категории/теги?
Да. Так как в таблицах связках настроен внешний ключ на посты, то удаление постов повлечет каскадное удаление из таблиц связок, а они за собой потянут удаление осиротевших тегов и категорий.

вернуть пост,если он есть,который идёт перед/после поста,который будет в начале/в конце списка (зависит от того,по возрастанию ли пойдут посты,или по убыванию)
не хватает сортировки, (сортировки физически не будет. просто это говорит субд возьми следующую/предыдущюю запись, а не случайную)

Следующая
SQL
1
2
3
4
SELECT id,name,DATE 
FROM posts 
WHERE ROWID >(SELECT ROWID FROM POSTS WHERE id=some_number) 
ORDER BY ROWID LIMIT 1
Предыдущая
SQL
1
2
3
4
5
SELECT id,name,DATE 
FROM posts 
WHERE ROWID <(SELECT ROWID FROM POSTS WHERE id=some_number)
ORDER BY ROWID DESC
LIMIT 1
0
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
19.12.2023, 17:39  [ТС] 56
Не всегда. Если таблица большая то это плохо. если маленькая (умещается на страниц данных) то сойдет.
Что значит большая таблица и как тогда оптимизировать запрос,чтобы он хорошо работал и для большой таблицы?
К примеру запрос:
SQL
1
SELECT id,name,DATA FROM posts LIMIT some_value offset some_number?
Просто с одной стороны Вы пишете,что операции чтения очень (или самые) дешовые и что не нужно создавать дополнительных индексов,а с другой стороны Вы пишете,что для большой таблицы это всё-таки плохо.
- SEARCH TABLE USING COVERING INDEX - очень хорошо
А как сделать так,чтобы использовались только покрывающие индексы и чем они так хороши,что лучше,к примеру,первичных ключей? Почему даже для строк они хороши?
- SEARCH TABLE USING PRIMARY KEY - тоже хорошо, но если строки жирные, то хуже чем предыдущий вариант
А если первичный ключ не строка,чем это плохо для строк? Если строка то я,наверное,понимаю,т.к,как Вы писали,большие строки гораздо сложнее читать,если они находятся сразу на нескольких страницах.
- SEARCH TABLE USING INDEX - хороший вариант, но хуже двух предыдущих
Почему он хуже? Может потому,что наши индексы могут быть не уникальны?
Как разработчик БД вы должны знать это распределение. Например что в бд будет храниться определенное количество постов, тегов и категорий. Для SQLite не встречал обучающих курсов где это паказывали.
Но Вы то это откудо-то узнали. Допустим я знаю,что в бд может хранится x постов,y категорий z теговразмер ассоциативной таблицы с post,category: x1,а с post,tag - y1. Как я могу оптимизировать запросы,зная эти данные.
Если взять что нужно удалить 1000 осиротевших записей, то запрос отработает 1 раз и сделает это быстрее чем 1000 раз запустится триггер, но триггер это время распределяет на каждое удаление и это будет не зметно. пользователь не заметит если пост удаляется не 10мс, а условно 11 мс.
В каком случае триггер запустится 1000 раз? Вы имеете ввиду когда я захочу удалить все посты? Ну если разница в несколько мс,то это,на мой взгляд,не сильно страшно,а вот,к примеру,если будет удалятся с триггером 5000 или 10000 осиротевших записей,к примеру,5 секунд,а без триггера это же количество будет удалятся где-то секунду,то тут,на мой взгляд,уже можно начинать задумываться,т.к разница есть,к примеру,в 5 раз. А вообще из трёх запросов,на Ваш взгляд,какой будет самый быстрый - второй без триггера,третий (если его сделать без триггера),или третий с триггером и почему? Мне не сложно удалить категории/теги без триггера,если это улучшит производительность,хотя,конечно,с триггером мне придётся писать меньше кода на java и на том же developers.android.com рекомендуют всё,что возможно,делать в sqlite,а не в коде программы,т.к так улучшится производительность. Кстати если я буду делать триггер,надеюсь можно навесить несколько триггеров на удаление поста,т.е вначале удалить осиротевшие категории,а потом осиротевшие теги. Жаль,что в sqlite нету триггера на полную очистку таблицы. Тогда бы при удалении одного поста удалялись бы категории/теги связанные только с этим постом,а при удалении всех постов удалялись бы сразу все категории/теги,т.е не надо было бы сканировать таблицы с категориями/тегами при каждом удалении поста.
0
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
20.12.2023, 01:43  [ТС] 57
К сожалению пока не получил ответа на мои вопросы. В общем продолжил изучать планы запроса,но мне непонятны некоторые вещи:
SQL
1
SELECT MAX(ROWID) +1 FROM favourite_posts
План показывает 3 0 0 SEARCH TABLE favourite_posts. Во-первых в чём отличие scan от search,а во-вторых почему он не использует индексы,как он использует их в некоторых запросах?
SQL
1
DELETE FROM favourite_posts WHERE id=15945
4 0 0 SEARCH TABLE favourite_posts USING INDEX sqlite_autoindex_favourite_posts_1 (id=?)
Почему здесь нет covered index?
14 0 0 SEARCH TABLE post_tag_relationship USING COVERING INDEX sqlite_autoindex_post_tag_relationship_1 (post_id=?)
А здесь он почему-то есть,как и для категорий. Ещё непонятно почему вначале удаляются записи с таблицей с тегами,а потом с таблицей с категориями. Мне-то последовательность не важна,но просто любопытный факт. Также интересно,какой запрос под капотом использует sqlite,чтобы каскадно удалить записи?
Пока что я для удаления категорий использую старый запрос,т.к хочу отладить работу sql запросов,но потом,конечно,за меню или на триггер,или на Ваш второй запрос,или на третий запрос без триггера. Главное чтобы это работало быстрее,почему я и спрашиваю у Вас,какой запрос будет работать быстрее?
SQL
1
DELETE FROM categories WHERE NOT EXISTS(SELECT id FROM post_category_relationship WHERE id=categories.id)
3 0 0 SCAN TABLE categories
5 0 0 CORRELATED SCALAR SUBQUERY 1
9 5 0 SCAN TABLE post_category_relationship
А тут с чего он вздумал сканировать две таблицы (такая же ситуация и с тегами)? Ведь id в поле с категориями unique,значит должен использоваться индекс. Насчёт второй таблицы понятно т.к,как мы с Вами выяснили,для второго столбца нужен отдельный индекс. Кстати интересно,если у нас составной индекс из трёх столбцов,для второго и третьего столбца,если нам нужно работать именно с ними,тоже нужен отдельный индекс,или только для третьего? И что значит SCALAR SUBQUERY 1?
0
754 / 271 / 57
Регистрация: 01.06.2023
Сообщений: 750
20.12.2023, 08:49 58
Что значит большая таблица

Значит в ней много данных. много строк. и она занимает несколько страниц на диске

как тогда оптимизировать запрос,чтобы он хорошо работал и для большой таблицы?
SQL
1
SELECT id,name,DATA FROM posts LIMIT some_value offset some_number
здесь как минимум нет сортировки.
1. запоминать позицию id и уже от нее запрашивать очередную порцию данных.
2. Сразу разбивать выборку на порции и в приложении помнить что на стронице N содержатся посты с номерами K10,K11,...
3. Если нужно только перемещение вперед, то открывайте курсор и делаете fetch порциями

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

Почему даже для строк они хороши?
Опредлимся с терминами. Строка в БД это не последовательность символов образующих текст, а одна запись в таблице.

А как сделать так,чтобы использовались только покрывающие индексы и чем они так хороши,что лучше,к примеру,первичных ключей?
Благодаря тому что обычно в поисковые индексы включают только ограниченный набор записей, то размер записи (строки) будет меньше, соответственно меньше операций чтения нужно сделать. Если пихать все поля в индексы, то это преимущество теряется и операция чтения становится похожа на SEARCH TABLE USING PRIMARY KEY только по индексу

А если первичный ключ не строка,чем это плохо для строк? Если строка то я,наверное,понимаю,т.к,как Вы писали,большие строки гораздо сложнее читать,если они находятся сразу на нескольких страницах.
См. выше терминалогию что строка есть запись. Под жирной запись понимаю если в ней есть поля содержащие большой объем данных (например длинный текст или блок бинарных данных) или содержит много полей например текстового типа.

Почему он хуже? Может потому,что наши индексы могут быть не уникальны?
Потому что по факту нужно сделать выборку по двум таблицам

1) По бинарному дереву индекса найти нужную запись, извлечь ее.
2) Строка индекса содержит ссылку на физическое располодение записи в основной, по этой ссылке нужно сходить в основную таблицу и достать недостающие колонки

Но Вы то это откудо-то узнали.
Я предположил. Там опыт и всякое такое.

А вообще из трёх запросов,на Ваш взгляд,какой будет самый быстрый - второй без триггера,третий (если его сделать без триггера),или третий с триггером и почему?
Прочитайте предыдущий пост, сколько можно повторять

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

Жаль,что в sqlite нету триггера на полную очистку таблицы. Тогда бы при удалении одного поста удалялись бы категории/теги связанные только с этим постом,а при удалении всех постов удалялись бы сразу все категории/теги,т.е не надо было бы сканировать таблицы с категориями/тегами при каждом удалении поста.
если будет удалятся с триггером 5000 или 10000 осиротевших записей,к примеру,5 секунд,а без триггера это же количество будет удалятся где-то секунду
Все эти цифры взяты с потолка просто что бы показать примерное расхождение. Точные Вы сможете получить только при реальном использовании. Сделайте с триггерами, если не устроит производительность тогда и поговорим.
Как Вариант при массовом удалении, удаляете триггеры и удаляете все данные и создаете триггеры заново.

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

Во-первых в чём отличие scan от search
SCAN - просто читает данные, строку за строкой. SEARCH - делает поиск по одному из индексов а заетм только извлекает нужные строки.

План показывает 3 0 0 SEARCH TABLE favourite_posts
Так сработала оптмиизация max. Найди последнюю запись в индексе по rowid и верни ее значение.

Почему здесь нет covered index?
Данный тип поиска может быть только на select

Также интересно,какой запрос под капотом использует sqlite,чтобы каскадно удалить записи?
SQL
1
DELETE FROM <childtable> WHERE <linkcolumn> = <parenttable>.rowid
А тут с чего он вздумал сканировать две таблицы (такая же ситуация и с тегами)?
И что значит SCALAR SUBQUERY 1?
так у Вас в запросе две таблицы. читается так
1) пройдись по каждой записи в таблице categories
2) для каждой записи выполни корелирующий запроc
3) пройдись по таблице post_category_relationship и найди с id из родительского запроса
4) В плане почему то не пишется но подразумевается потом пометь запись как удаленная

Скорее всего у Вас нет индекса post_category_relationship(id)

Кстати интересно,если у нас составной индекс из трёх столбцов,для второго и третьего столбца,если нам нужно работать именно с ними,тоже нужен отдельный индекс,
Да
0
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
20.12.2023, 18:03  [ТС] 59
запоминать позицию id и уже от нее запрашивать очередную порцию данных.
А,это типа как я выше писал запрос для извлечения одного поста. По поводу сортировки вроде sqlite гарантирует,что результат всегда будет выдаваться по rowid,поэтому в целях оптимизации,если сортировка идёт по возрастанию,я не добавляю order by. Правда для первой страницы запоминать позицию id не получится,а для последующих,на мой взгляд,идея хорошая.
2. Сразу разбивать выборку на порции и в приложении помнить что на стронице N содержатся посты с номерами K10,K11,...
Так это я и делаю,используя offset,только теперь я могу обойтись и без него на всех страницах,кроме первой.
3. Если нужно только перемещение вперед, то открывайте курсор и делаете fetch порциями
Во-первых я не уверен,что это будет работать в android,а во-вторых назад пользователь тоже должен перемещаться.
Я предположил. Там опыт и всякое такое.
А есть ли ещё подводные камни,которые Вы сами выяснили в sqlite?
Прочитайте предыдущий пост, сколько можно повторять
Получается,что второй запрос будет работать быстрее,если я решу удалить осиротевшие категории/теги после удаления нескольких постов,а третий (неважно с триггером или без) будет работать быстрее,если я решу удалить осиротевшие записи сразу после удаления поста?
В самом триггере можно указать несколько действий.
Вот этого я и не знал. И это можно сделать в одном запросе,как я понимаю через запятую,? Кстати правильно ли я понимаю,что old это значение,которое было удалено и оно актуально для удаления и обновления,а new - для вставки и обновления?
Так сработала оптмиизация max. Найди последнюю запись в индексе по rowid и верни ее значение.
Но в плане не указано,что search usin index,а просто указано search без какого-либо упоминания про индексы.
Скорее всего у Вас нет индекса post_category_relationship(id)
Да,пока что нет,но ведь есть уникальное поле для categories,а значит пусть бы он хотя бы не сканировал эту таблицу.

Добавлено через 5 минут
Кстати ещё хочу уточнить,получается если столбцы,по которым мы хотим делать простой или составной индекс,в сумме будут содержать большие значения,или хотя бы один из столбцов будет их содержать,к примеру массив байтов,лучше индекс не делать?
0
754 / 271 / 57
Регистрация: 01.06.2023
Сообщений: 750
20.12.2023, 18:18 60
А есть ли ещё подводные камни,которые Вы сами выяснили в sqlite?
sqlite на самом деле не совсем мое направление, больше по postgresql

Получается,что второй запрос будет работать быстрее,если я решу удалить осиротевшие категории/теги после удаления нескольких постов,а третий (неважно с триггером или без) будет работать быстрее,если я решу удалить осиротевшие записи сразу после удаления поста?
Не то чтоб быстрее, просто его выполнение будет размазано между действиями пользователя и затеряется

И это можно сделать в одном запросе,как я понимаю через запятую,?
через ";" см https://www.sqlite.org/lang_createtrigger.html

что old это значение,которое было удалено и оно актуально для удаления и обновления,а new - для вставки и обновления?
Верно

Но в плане не указано,что search usin index,а просто указано search без какого-либо упоминания про индексы.
Он там не явный, сама таблица выстроена в дерево по Rowid? может поэтому и пишет что ищет в таблице а не индексе

Да,пока что нет,но ведь есть уникальное поле для categories,а значит пусть бы он хотя бы не сканировал эту таблицу.
Вы удаляете из categories, поэтому нужно сначала найти запись в таблице categories потом пометить что она удалена.

Возможно у вас вообще неправильно запрос написан, Вы же так и не прислали структуру бд со всеми зависимостями

Добавлено через 4 минуты
Кстати ещё хочу уточнить,получается если столбцы,по которым мы хотим делать простой или составной индекс,в сумме будут содержать большие значения,или хотя бы один из столбцов будет их содержать,к примеру массив байтов,лучше индекс не делать?
Индексы нужны только для поиска. если не собираетесь искать по полю то не нужно его тащить в индекс. По большим текстовым полям обычно не ищут, ну или используют другой тип индексов которые отличается от обычных, см тут https://www.sqlite.org/fts3.html.

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

Еще составной может понадобиться если поиск идет сразу по нескольким полям. Например нужно удалить связку между постом и тегом.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
20.12.2023, 18:18
Помогаю со студенческими работами здесь

Как правильно обновить DataGrid после добавления строки
Уважаемые Гуру! В ZAML имеется DataGrid с привязкой к ObservableCollection: &lt;Grid Grid.Row=&quot;1&quot; Name=&quot;оснТаблица&quot; ...

Как обновить datagridview после добавления кнопки записи?
Это моя кнопка кода загружает данные datagridview с моей базой данных в SQL Server. После нажатия на кнопку я хочу обновить данные в...

Обновить грид после удаления из бд
Пытался обновить после запроса на удаление Select'ом. Но либо оно ничего не обновляло, либо клонировало одну из строк базы. хелп плиз) ...

Как правильно обновить dataGridView1 после update в отдельной форме
Здравствуйте! В главной форме имеем dataGridView1. Заполняем dataGridView1 вручную Кнопкой вызываем форму для редактирования выбранной...

Список файлов в listView. Как убрать пути и обновить список после удаления элемента
В listView отображается список файлов директории. Вопросы: 1. Как сделать так, чтоб в списке были только названия файлов, без пути к...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
60
Ответ Создать тему

Редактор формул (кликните на картинку в правом углу, чтобы закрыть)
Новые блоги и статьи
Что нового в C# 14
UnmanagedCoder 10.03.2025
Предстоящая версия C# 14 обещает принести изменения, которые сделают разработку еще более приятной и эффективной. Что стоит отметить, так это влияние сообщества разработчиков на формирование новых. . .
Формулы поворота
Igor3D 10.03.2025
Добрый день Тема Эти формулы приводятся во множестве тьюториалов, часто под видом "матрица вращения на плоскости". x' = x * cos(a) - y * sin(a) y' = y * cos(a) + x * sin(a) Как бы Вы их. . .
Что нового в .NET 10
UnmanagedCoder 10.03.2025
. NET 10 выходит как релиз с длительной поддержкой (LTS), включающей три года обновлений. В этом обновлении Microsoft сфокусировались на нескольких направлениях: производительность, оптимизация. . .
Отложенное высвобождение, RCU и Hazard Pointer в C++26
NullReferenced 09.03.2025
Многопоточное программирование стало важной частью современной разработки. Когда несколько потоков одновременно работают с общими данными, возникает целый ряд проблем, связанных с синхронизацией и. . .
Неблокирующийся стек на C++26
NullReferenced 09.03.2025
Традиционные способы синхронизации в многопоточном программировании — мьютексы, семафоры, условные переменные — часто превращаются в узкое место в плане производительности. При этом неблокирующиеся. . .
Обработка строк в C++26: Новые возможности string и string_view
NullReferenced 09.03.2025
Новый стандарт C++26 предлагает много улучшений для работы с привычными string и относительно новыми string_view. string_view - это невладеющая ссылка на последовательность символов, появившаяся в. . .
Мой первый аддон для Blender 3D, с помощью нейронки (не зная даже азов пайтона, но это не значит что так и с остальным).
Hrethgir 09.03.2025
Потратил весь день. Пол-дня мне хватило, чтобы понять что с версией с 14B мне не одолеть написание функционального кода, на языке с которым я вообще никак не знаком - пайтон. Версия 22B от другого. . .
Einstein@Home сегодня исполняется двадцать лет!
Programma_Boinc 09.03.2025
Einstein@Home сегодня исполняется двадцать лет! Отправлено 19 февраля 2025 года в 17:20:21 UTC Я хочу поздравить всех наших волонтеров, разработчиков и ученых из Einstein@Home. Мы официально. . .
Заполнители и расширенный набор символов в C++26
NullReferenced 09.03.2025
C++26 представляет два важных обновления: заполнители и расширенный набор символов. Заполнители (placeholders) решают давнюю проблему лаконичности кода в шаблонных выражениях и лямбда-функциях. Они. . .
Контракты в C++26
NullReferenced 09.03.2025
Контракты – это механизм, позволяющий указывать предусловия, постусловия и инварианты для функций в коде. Эта функциональность должна была стать частью C++20, но была исключена на встрече комитета. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru