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

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

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

Студворк — интернет-сервис помощи студентам
Всех приветствую. У меня есть таблица,в которой первичный ключ не 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
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
13.12.2023, 23:22  [ТС]
Студворк — интернет-сервис помощи студентам
Поэтому делать вакум после каждого обновления - это плохо, так как возможно потребуется потрогать каждую страницу, т.е. расходуем ресурс флеш памяти (если только это не проходит в кеше).
Так ведь добавление записи тоже расходует ресурс во флеш памяти,или с vacuum это будет происходить гораздо больше,т.к мы будем работать с несколькими страницами?
По поводу производительности offset если у Вас, число записей не много (порядка нескольких тысяч), сами строки короткие, то вообще не стоит об этом задумываться. С точки зрения пользователя это будет не заметно.
Так,а вот это интересно. Причём здесь длинна строк? Вообще в бд будет хранится html страница каждого поста,чтобы пользователь мог просматривать избранный пост оффлайн. Конечно размер строки может быть разный,но думаю в среднем 5000-10000 символов будет,хотя,конечно,есть страницы гораздо больше.
если нужен монотонный индекс по номерам без пробелов, то добавьте еще одну колонку с номером и его уже обновляйте.
В данный момент у меня так и сделано,но,как я уже писал,при обновлении я напарываюсь на unique constraint. Я специально делал эту колонку уникальной,чтобы sqlite оптимизировал мои запросы. Вообще сейчас у меня бд такая:
Java
1
2
3
4
5
        db.execSQL("CREATE TABLE IF NOT EXISTS " + favouritePostsTableName + " (ROWID INTEGER,id INTEGER,name TEXT,date TEXT,author TEXT,content TEXT,url TEXT,PRIMARY KEY(id),UNIQUE(ROWID))");
        db.execSQL("CREATE TABLE IF NOT EXISTS " + categories + " (id INTEGER PRIMARY KEY,name TEXT,description TEXT,author TEXT)");
        db.execSQL("CREATE TABLE IF NOT EXISTS " + tags + " (id INTEGER PRIMARY KEY,name TEXT,description TEXT,author TEXT)");
        db.execSQL("CREATE TABLE IF NOT EXISTS "+postCategoryRelation+"(id INTEGER,id INTEGER,PRIMARY KEY(post_id,id),FOREIGN KEY(post_id) REFERENCES " + favouritePostsTableName + "(id) ON DELETE CASCADE ON UPDATE CASCADE,FOREIGN KEY(id) REFERENCES "+categories+"(id) ON DELETE CASCADE ON UPDATE CASCADE)");
        db.execSQL("CREATE TABLE IF NOT EXISTS "+postTagRelation+"(post_id INTEGER,id INTEGER,PRIMARY KEY(post_id,id),FOREIGN KEY(post_id) REFERENCES " + favouritePostsTableName + "(id) ON DELETE CASCADE ON UPDATE CASCADE,FOREIGN KEY(id) REFERENCES "+tags+"(id) ON DELETE CASCADE ON UPDATE CASCADE)");
Этот вариант бд ещё не окончательный,поэтому если кто-то считает,что её можно улучшить,я не откажусь от советов.
По сути id постов у меня отдаётся сервером. Да,мне нужно,чтобы посты отображались в порядке добавления (Колонка ROWID) и чтобы на страницу было н постов.
Поле в этом случае заполняйте/обновляйте по триггеру добавления/обновления записи.
Не могли бы Вы пожалуйста написать пример этого триггера,т.к я не специалист в sqlite. Сейчас я просто получаю количество постов и добавляю к нему единицу. Полученное значение я вставляю в rowid.
Но при этом обратите внимание что если удалить первую запись, то нужно обновить все записи в таблице и потребуется 2Х свободного места от размера обновляемой таблицы.
Т.е исходная таблица увеличится в три раза? Я вообще подумываю отказаться от такого подхода,т.к если записей будет несколько тысяч (что,на мой взгляд,мало вероятно,но я должен предусмотреть любой сценарий),во время удаления поста будут тормоза в несколько секунд,а на слабых мобильных устройствах и несколько десятков секунд. Я думаю у пользователей приложения может возникнуть резонный вопрос,чего так долго. Я подумываю над другим подходом - ROWID у нас будет также уникальная колонка,но если с точки зрения rowid мы удаляем предпоследний пост (для получения его rowid нам нужно будет выполнять отдельный sql запрос),то мы обновим последнюю ,т.е немного уменьшим вероятность переполнения ROWID,хотя главный вопрос останется - оптимизация offset.
если переход по страницам строго последовательный, то запоминайте минимальный и максимальный ID строк в странице:
Дело в том,что я не могу гарантировать,что мне не понадобится изменить количество результатов на странице.
если пользователю нужен нумерованный список в порядке добавления, используйте оконную функцию row_number() over (order by id).
А row_number() будет работать для моей колонки ROWID,т.к я её переопределил.
0
919 / 292 / 58
Регистрация: 01.06.2023
Сообщений: 816
14.12.2023, 00:32
Если не собираетесь обновлять тысячи раз за сутки поля, то о ресурсе флеш можете не задумываться. (хотя Вы как раз собираетесь обновляя счетчик)

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

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

Не очень понятно чем unique поможет в оптимизации. А вот индекс уникальный поможет. И пример с триггерами
Кликните здесь для просмотра всего текста
SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
DROP TABLE favouritePostsTableName;
 
CREATE TABLE IF NOT EXISTS favouritePostsTableName (id INTEGER PRIMARY KEY autoincrement, nOrder INTEGER, name TEXT,DATE TEXT,author TEXT,url TEXT);
CREATE UNIQUE INDEX favouritePostsTableName_order ON favouritePostsTableName(nOrder);
 
DROP TABLE favouritePostsTableNamecontent;
CREATE TABLE IF NOT EXISTS favouritePostsTableNamecontent (id INTEGER,content TEXT, FOREIGN KEY(id) REFERENCES favouritePostsTableName(id) ON DELETE CASCADE ON UPDATE CASCADE, UNIQUE(id));
CREATE INDEX favouritePostsTableNamecontent_id ON favouritePostsTableNamecontent(id);
 
CREATE VIEW IF NOT EXISTS favouritePostsTableNamewithContent AS
   SELECT favouritePostsTableName.*, content  FROM favouritePostsTableName LEFT JOIN favouritePostsTableNamecontent ON favouritePostsTableName.id = favouritePostsTableNamecontent.id;
   
--drop trigger favouritePostsTableName_after_insert;
CREATE TRIGGER favouritePostsTableName_after_insert
after INSERT ON favouritePostsTableName
BEGIN
  UPDATE favouritePostsTableName SET nOrder=COALESCE((SELECT nOrder FROM favouritePostsTableName ORDER BY nOrder DESC LIMIT 1),0) + 1
   WHERE id=NEW.id;
END;
 
--drop trigger favouritePostsTableName_after_delete;
CREATE TRIGGER favouritePostsTableName_after_delete
after DELETE ON favouritePostsTableName
BEGIN
  UPDATE favouritePostsTableName 
  SET nOrder=t.nOrder
  FROM (
    SELECT id, ROW_NUMBER() OVER (ORDER BY nOrder) - 1 + OLD.nOrder AS nOrder FROM favouritePostsTableName WHERE nOrder > OLD.nOrder
  ) AS t
   WHERE favouritePostsTableName.id = t.id AND COALESCE(favouritePostsTableName.nOrder,0) <> t.nOrder;
END;
 
 
DELETE FROM favouritePostsTableName;
 
INSERT INTO favouritePostsTableName(name,DATE,url) VALUES ('test name', '01.01.2023', 'c:\1.txt');
INSERT INTO favouritePostsTableName(name,DATE,url) VALUES ('test2 name', '01.01.2023', 'c:\2.txt');
INSERT INTO favouritePostsTableName(name,DATE,url) VALUES ('test3 name', '01.01.2023', 'c:\3.txt');
SELECT * FROM favouritePostsTableName;
DELETE FROM favouritePostsTableName WHERE name = 'test3 name';
INSERT INTO favouritePostsTableName(name,DATE,url) VALUES ('test4 name', '01.01.2023', 'c:\4.txt');
SELECT * FROM favouritePostsTableName;
DELETE FROM favouritePostsTableName WHERE name = 'test name';
SELECT * FROM favouritePostsTableName;


пример оптимизаций offset выше привел, row_number() без разницы по какому полю

по поводу "вероятность переполнения ROWID", вообще не стоит беспокоится в Вашем случае
0
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
14.12.2023, 01:53  [ТС]
Огромное Вам спасибо. Дело в том,что id - это идентификатор поста,который отдаёт сервер. Я его сделал первичным ключом,а rowId сделал колонкой,которая почти как autoincrement,правда я ей сам присваиваю значения. Не знал,что можно создавать не уникальные колонки,но уникальный индекс. Получается,если в эту колонку вставить повторяющееся значение,то не будет ошибки из-за того,что был создан уникальный индекс,а значение не уникально? По поводу длинных строк понял,хотя это,конечно,усложнит запрос,т.к я буду часто выбирать и эти длинные строки. Только непонятно почему если они будут в общей таблице,а не в отдельной,будет выполняться больше операций чтения,ведь я по-любому почти всегда буду их выбирать кроме,к примеру,случая когда мне надо будет узнать,есть ли пост в избранном (тогда я просто буду выбирать id,который не autoincrement и который приходит с сервера). А,сейчас догадался. Возможно это связано с тем,что Вы их поместили в представление. Кстати почему во второй таблице,я имею ввиду таблицу favouritePostsTableNamecontent поле id unique,но не первичный ключ. Вообще,на мой взгляд,очень странно,что при использовании ключевого слова unique sqlite не пытается оптимизировать мой запрос,ведь я же явно сообщаю,что колонка первичный ключ,или просто уникальная (в зависимости от оператора).
0
919 / 292 / 58
Регистрация: 01.06.2023
Сообщений: 816
14.12.2023, 08:30
Не знал,что можно создавать не уникальные колонки,но уникальный индекс. Получается,если в эту колонку вставить повторяющееся значение,то не будет ошибки из-за того,что был создан уникальный индекс,а значение не уникально?
Добавить дубли не получится так как не даст именно уникальный индекс

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

SQL
1
2
3
4
5
6
7
8
9
10
11
SELECT 
  favouritePostsTableName.*, content
FROM 
  (
   SELECT * 
   FROM favouritePostsTableName
   ORDER BY nOrder
   LIMIT 100
   offset 1000 
  ) AS favouritePostsTableName 
  LEFT JOIN favouritePostsTableNamecontent ON favouritePostsTableName.id = favouritePostsTableNamecontent.id
т.е. в данном случае нужно будет прочитать 1000 строк по индексу favouritePostsTableName(nOrder) по ним вычитается 1000 строк из таблицы favouritePostsTableName если они идут упорядочено то потребуется меньше страниц и только после этого будет прочитано 100 строк из таблицы favouritePostsTableNamecontent.

Кстати почему во второй таблице,я имею ввиду таблицу favouritePostsTableNamecontent поле id unique,но не первичный ключ
В данном случае он и первичный и вторичный ключ, это не много нарушает принципы нормализации в угоду производительности (колоночное хранилище для бедных)

Вообще,на мой взгляд,очень странно,что при использовании ключевого слова unique sqlite не пытается оптимизировать мой запрос,ведь я же явно сообщаю,что колонка первичный ключ,или просто уникальная (в зависимости от оператора)
Если не изменяет память, то в учет берутся именно индексы
0
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
14.12.2023, 16:23  [ТС]
Огромное Вам спасибо. Забыл спросить ещё некоторые детали. Почему для контента Вы не создали уникальный индекс,т.е вместо
SQL
1
CREATE INDEX favouritePostsTableNamecontent_id ON favouritePostsTableNamecontent(id)
не использовали
SQL
1
CREATE UNIQUE INDEX favouritePostsTableNamecontent_id ON favouritePostsTableNamecontent(id)
Такжн что даёт нам представление. Как я предполагаю,при вставке значений в таблицу значения будут автоматически добавляться туда,что оптимизирует мои запросы. Также хотелось бы уточнить,почему Вы использовали LEFT JOIN. К примеру когда я захочу получить все посты определённой категории,или категории определённого поста,мне надо использовать left join,или просто join?
Добавить дубли не получится так как не даст именно уникальный индекс
Интересно,а что если сделать nOrder уникальным индексом и не ухучшится ли производительность,если первичный ключ не будет autoincrement,т.к id поста отдаётся сервером и если ухучшится,как её улучшить? Просто id - это произвольное уникальное число,которое возвращает сервер как для поста,так и для категории/тега и т.д. Конечно я бы мог от него отказаться но мне он,к примеру,помогает понять,есть ли пост в избранном. И почему favouritePostsTableNamecontent(id) не первичный ключ,а просто уникальное значение,при этом индекс не уникальный? Так же я хотел бы сохранить id,который отдаёт сервер,и для категории/тега. Вообще мне ме был бы нужен ROWID,но я хочу видеть посты в порядке их добавления,т.е такое у меня тз,при этом порядок может быть и обратный,но это уже,на мой взгляд,не так важно.Простите пожалуйста,что задаю слишком много вопросов,просто я на будущее хочу понять для себя,почему это работает так или иначе.
Сокращение числа чтений произойдет потому что вычитывать больше данных придется именно из таблиц где данных много
Я вот думал,а реально ли всё-таки сделать content в одной таблице,чтобы не дублировать post_id,но понял,что это будет сделать очень сложно. Кстати ведь мой запрос можно ещё больше оптимизировать. Дело в том,что я показываю контент только того поста,который открывает пользователь,а значит я могу выбирать контент поста только с определённым id (надеюсь,что все нужные индексы уже есть). Кстати нужны ли индексы для таблиц с категориями/тегами и ассоциативными таблицами для категорий/тегов,и если нужны,то составные или отдельные для двух колонок ассоциативной таблицы? Я удивляюсь,что для ROW_NUMBER() не важно поле. Просто непонятно,как эта функция возвращает номер записи,если в sqlite,вроде,нет такого понятия,как позиция записи. Также я думаю,не будет ли тормозов при удалении поста,когда триггер будет пытаться обновить записи при удалении. Также меня смущает оператор autoincrement. Просто я читал,что при удалении всех записей rowid не сбрасывается,если же я сам увеличиваю эту запись,я могу спокойно удалить её,и тогда rowid не будет сам увеличиваться,хотя я читал в одном чате,что в sqlite под android вроде rowid сбрасывается при удалении,но это вроде незадокументированно,поэтому я не хочу полагатся на это. Понятное дело,что при желании можно достичь переполнения,т.к,к примеру,при удалении поста в начале или середине таблицы в id будут дырки,но я бы хотел минимизировать эту вероятность,т.е если,к примеру,у меня 5 постов,и я удаляю пятый,то следующий,добавленный после этого,будет пятый,ведь такая минимизация не скажется на производительности. А с autoincrement этого так просто не сделаешь,почему я и не делал ROWID autoincrement. Таким образом,если я удалю все посты,то пост будет начинатся с rowid = 1. Но тогда вопрос,не будет ли пересканирование таблицы,при попытке вставить max(rowid) +1 и как поведёт себя функция max,если в таблице не будет записей?
В данном случае он и первичный и вторичный ключ, это не много нарушает принципы нормализации в угоду производительности (колоночное хранилище для бедных)
А разве если мы заменим его просто primary key вместо unique,производительность ухучшится?
Если не изменяет память, то в учет берутся именно индексы
А разве sqlite не создаёт индексы,если колонка unique или primary key,ведь вроде sqlite сам,когда ему надо,создаёт индексы.
0
919 / 292 / 58
Регистрация: 01.06.2023
Сообщений: 816
14.12.2023, 16:55
Такжн что даёт нам представление
Представление просто удобно что бы не писать соединения.
В принципе через триггер на представление можно манипулировать обоими таблицами см. пример тут https://club.shelek.ru/viewart.php?id=353.
Хотел накидать пример но потом стало лень, уж извините.

Интересно,а что если сделать nOrder уникальным индексом
nOrder и так уникальный через индекс

SQL
1
CREATE UNIQUE INDEX favouritePostsTableName_order ON favouritePostsTableName(nOrder);
не ухучшится ли производительность,если первичный ключ не будет autoincrement
Нет не ухудшится

И почему favouritePostsTableNamecontent(id) не первичный ключ,а просто уникальное значение,при этом индекс не уникальный
да, правильнее использовать уникальный индекс что бы реализовать связь один к одному. Не уверен что ключ может быть сразу первичным и вторичным

но я хочу видеть посты в порядке их добавления
добавьте поле с датой/временем добавления

ROW_NUMBER() не важно поле. Просто непонятно,как эта функция возвращает номер запис
функция просто сортирует выдачу и присваивает строке порядковый номер, никакой магии, просто номер п/п в выдаче

Также я думаю,не будет ли тормозов при удалении поста,когда триггер будет пытаться обновить записи при удалении.
Будут особенно если будете удалять самую старую строку

Понятное дело,что при желании можно достичь переполнения,
не беспокойтесь об этом, размер поля потом можно увеличить до bigint, его пределов даже теоретически достич не возможно

Но тогда вопрос,не будет ли пересканирование таблицы,при попытке вставить max(rowid) +1 и как поведёт себя функция max,если в таблице не будет записей?
Посмотрите как реализовано присвоение nOrder в запросе выше. Не знаю насколько оптимизирован в sqlite функция max, может потребовать чтения всей таблицы в отличии от limit 1

SQL
1
nOrder=COALESCE((SELECT nOrder FROM favouritePostsTableName ORDER BY nOrder DESC LIMIT 1),0) + 1
в этом случае прочитается единственная строка по индексу

А разве sqlite не создаёт индексы,если колонка unique или primary key,ведь вроде sqlite сам,когда ему надо,создаёт индексы.
у меня индексы не создались, м.б. на других весриях и создаются, но индексы и ключи это немного о разном
0
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
14.12.2023, 21:31  [ТС]
Представление просто удобно что бы не писать соединения.
Понятно. Т.е если я,к примеру,выполняю запрос select * from some_view,то у меня сразу же выполняется запрос,который я написал в представлении some_view,т.е это можно сравнить с раскрытием макросов в latex? Как я уже спрашивал,нужны ли индексы для ассоциативных таблиц и какое соединение предпочесть для получения категорий/тегов поста,или постов выбранной категории? Как я понимаю лучше создать уникальные индексы для всех колонок,по которым я буду вести проверку? Для оптимизации offset достаточно одного индекса?
Хотел накидать пример но потом стало лень, уж извините.
Так Вы же дали ссылку на пример. Если Вы хотели его улучшить,или хотели накидать пример по другим моментам,то когда Вам будет не лень,накидайте его пожалуйста.
0
919 / 292 / 58
Регистрация: 01.06.2023
Сообщений: 816
14.12.2023, 22:30
Как я понимаю лучше создать уникальные индексы для всех колонок,по которым я буду вести проверку?
Нет, индексы должны быть обычными

Для оптимизации offset достаточно одного индекса?
для Offset все зависит от того как будетет искать посты, приведите пример сценария использования

А вот это я не понял. Я пока что не нашёл secondary key в sqlite
Да, попутал терминалогию, имелось ввиду FOREIGN KEY. Как оказалось можно создавать и PK и FK на один и тот же столбец

Кстати имеет ли смысл добавлять уникальный индекс,если колонка первичный ключ и поможет ли уникальный индекс для связи многие ко многим и должен ли он быть составным? Вообще в каких случаях полезны составные индексы? Один из примеров,это если я сортирую по двум колонкам,а вот если я использую оператор and,к примеру select a,b from c where a=1 and b=2,нужно ли мне делать составной индекс для a и b,или лучше два отдельных индекса - для a и для b?
Почитал еще не много про PK в sqlite. Если столбец определен как INTEGER PRIMARY KEY, на самом деле это псевдоним rowid столбца: Данные для таблиц rowid хранятся в виде B-древовидной структуры, содержащей по одной записи для каждой строки таблицы, используя значение rowid в качестве ключа. таким образом для обычного первичного ключа создавать индекс не нужно

Для составного первичного ключа индекс создается автоматически

Для внешних ключей индексы не создаются, но хорошей практикой будет их создать для увеличения скорости поиска

Составной индекс нужен если нужна уникальность комбинации полей (уникальный индекс) или если как написали в пример поиск идет сразу по двум полям

Вопрос только как создать триггер,который сразу вставлял бы мне увеличенное значение для ROWID. Пока перечитывал сообщение понял,что Вы его уже написали.
Посмотрите код ниже, поле favouritePosts.id будет автомтически увеличивать свое значение при добавлении новых записей

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

SQL
1
INSERT INTO favouritePosts(idPost, name) VALUES (1, 'firstPost');
Кстати думаю поменять первичный ключ,т.е вместо id моего поста буду использовать ROWID,тогда в принципе если мне нужна будет сортировка по возрастанию,оператор order by не будет нужен,а значит sqlite не будет делать лишнюю работу,что увеличит скорость работы,т.е производительность. В правильном ли направлении я мыслю и если нет,то почему?
не понял

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

Кстати если я сразу создам BIGINT как тип данных для поля,насколько сильно увеличится размер бд и будут ли при этом проблемы с производительностью?
Скорее всего Вы не заметите разницы

А если таблица уже отсортированно,не будет ли происходить пересортировки? Также если я буду использовать order by вместе с этой функцией? Теоретически тогда можно обойтись этой функцией,не используя offset и limit,хотя с другой стороны она будет сортировать всю выборку,вместо н элементов,как это сделано сейчас.
пересортировка будет обязательно, просто если таблица упорядочена, это в теории займет меньше времени. Нет, обойтись этой функцией не получится, она не для поиска, а для выдачи результата.

Вот прочитал,что почему-то если к max() прибавлять один,могут быть тормоза
Посмотрел планы, max требует прочтения всей таблицы.

SQL
1
EXPLAIN SELECT MAX(id) FROM favouritePosts;
Пример который я привел считывает только последнюю строку из индекса, т.е. не зависит от размера таблицы, но требует наличие индекса по искомому столбцу

SQL
1
EXPLAIN SELECT id FROM favouritePosts ORDER BY id DESC LIMIT 1;
Кстати оказывается sqlite (по крайней мере в android) создаёт уникальный индекс,если есть ключевое слово unique,то автоматически создаётся уникальный индекс
Не обращал внимания, учту

Ниже код создания бд по моему мнению оптимальной для Вас.

Кликните здесь для просмотра всего текста
SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
PRAGMA foreign_keys = ON;
 
CREATE TABLE favouritePosts
 (
  -- заменит rowid поле атоинкрементируемое его можно использовать для определения порядка 
   id INTEGER PRIMARY KEY,
 --По сути id постов у меня отдаётся сервером
   idPost INTEGER,
   name TEXT,
   DATE TEXT,
   author TEXT,
   url TEXT
 );
 
 
--Для префиксного поиска по названию поста
CREATE INDEX favouritePosts_name ON favouritePosts(UPPER(name));
 
--select * from favouritePosts where upper(name) between 'FIRST' and 'FIRSTя'
--запись аналогична 
--select * from favouritePosts where upper(name) like 'FIRST%'
--но использует индекс для поиска. работает только если известно начало названия
 
--Если хранить контент отдельно
 
CREATE TABLE postsContent (
  id INTEGER PRIMARY KEY,
  content TEXT,
  FOREIGN KEY(id) REFERENCES favouritePosts(id) ON DELETE CASCADE ON UPDATE CASCADE
);
 
 
--Выполняем сразу за одно обращение, для вставки поста с контентом
INSERT INTO favouritePosts(idPost, name) VALUES (1, 'firstPost');
INSERT INTO postsContent VALUES (last_insert_rowid(), 'Content of firstPost');
SELECT last_insert_rowid();
 
 
 
CREATE TABLE categories  (
  --Предположу что так же предоставляется сервером
  id INTEGER PRIMARY KEY,
  name TEXT,
  description TEXT,
  author TEXT
);
 
--Индекс для поиска, поиск регистрозваисимый поэтому нормализуем в вехний регистр, where upper(name) = "КАТЕГОРИЯ!" or  upper(name) like "КАТ%"
CREATE INDEX categories_name ON categories(UPPER(name));
 
CREATE TABLE tags  (
  --Предположу что так же предоставляется сервером
  id INTEGER PRIMARY KEY,
  name TEXT,
  description TEXT,
  author TEXT
);
 
 
--Индекс для поиска, поиск регистрозваисимый поэтому нормализуем в вехний регистр, where upper(name) = "КАТЕГОРИЯ!" or  upper(name) like "КАТ%"
CREATE INDEX tags_name ON tags(UPPER(name));
 
CREATE TABLE categoriesOfPosts (
  idCategory INTEGER,
  idPost INTEGER,
  PRIMARY KEY (idCategory, idPost),
  FOREIGN KEY(idPost) REFERENCES favouritePosts(id) ON DELETE CASCADE ON UPDATE CASCADE,
  FOREIGN KEY(idCategory) REFERENCES categories(id) ON DELETE CASCADE ON UPDATE CASCADE
);
 
--так как создался автоматический индекс по (idCategory, idPost), то отдельно по idCategory создавать не нужно
--для поиска категорий поста
CREATE INDEX categoriesOfPosts_post ON categoriesOfPosts(idPost);
 
 
 
CREATE TABLE TagsOfPosts (
  idTag INTEGER,
  idPost INTEGER,
  PRIMARY KEY (idTag, idPost),
  FOREIGN KEY(idPost) REFERENCES favouritePosts(id) ON DELETE CASCADE ON UPDATE CASCADE,
  FOREIGN KEY(idTag) REFERENCES tags(id) ON DELETE CASCADE ON UPDATE CASCADE
);
 
--для поиска тегов поста
CREATE INDEX TagsOfPosts_post ON TagsOfPosts(idPost);
0
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
14.12.2023, 23:46  [ТС]
Нет, индексы должны быть обычными
Обоснуйте пожалуйста почему,ведь если я уверен,что какие-то поля уникальные,значит я могу использовать ключевое слово unique и тогда создастся уникальный индекс,по крайней мере так почему-то заявляет гугл.
Для внешних ключей индексы не создаются, но хорошей практикой будет их создать для увеличения скорости поиска
А если внешний ключ у нас и primary key,причём составной,то индексы создавать не надо,или всё равно надо?
Составной индекс нужен если нужна уникальность комбинации полей (уникальный индекс) или если как написали в пример поиск идет сразу по двум полям
А если я виду поиск как по двум полям,так и по одному полю,нужен ли простой индекс?
не понял
Ну смотрите: Если у нас первичный ключ не rowid,но есть и rowid (будем считать,что она строго больше нуля и увеличивается на 1),то сортировки,наверное,не будет,поэтому чтобы она была,мы должны явно задать order by ROWID. Если же у нас ROWID первичный ключ,то нам не обязательно писать order by ROWID,т.к записи уже будут в нужном нам порядке,а значит запрос будет выполняться быстрее. Так вот я хочу уточнить,правильно ли я рассуждаю?
Триггер будет вносить задержки, но думаю Вам не стоит пока об этом думать, ниже пример базы без триггеров
Интересно почему,ведь я просто вставляю запись в бд,а не обновляю,к примеру,10000 записей,после удаления первой.
Скорее всего Вы не заметите разницы
А с памятью разница будет,ведь каждое значение колонки такого типа должно занимать,на мой взгляд,как минимум в два раза больше байт.
Посмотрел планы, max требует прочтения всей таблицы.
Странно,просто я читал пост (в прошлом сообщении давал ссылку),в котором есть разница работы этой функции. Кстати Вы пробовали сразу написать EXPLAIN QUERY PLAN вместо explain (https://www.sqlite.org/eqp.html)?
idPost INTEGER
Интересно,почему Вы не поставили этому полю unique,ведь id постов будет разный?
--так как создался автоматический индекс по (idCategory, idPost), то отдельно по idCategory создавать не нужно
Интересно почему,как раз возвращаясь к вопросу о создании отдельных индексов,если есть составные. Ещё непонятно зачем мы отдельным запросом пытаемся выполнить SELECT last_insert_rowid() и для какой таблицы функция last_insert_rowid() возвращает результат?

Добавлено через 27 минут
Интересно почему,как раз возвращаясь к вопросу о создании отдельных индексов,если есть составные.
А,понял,ведь для постов у нас первичный ключ ROWID,а значит для id нужно создать индекс. По поводу content,я,наверное,не буду делать отдельную таблицу для него,т.к происходит дублирование данных ведь,как я предполагал ранее,разве мы не можем объединить таблицу саму с собой,для достижения того же результата,или это ухудшит производительность?
0
919 / 292 / 58
Регистрация: 01.06.2023
Сообщений: 816
15.12.2023, 00:39
Обоснуйте пожалуйста почему,ведь если я уверен,что какие-то поля уникальные,значит я могу использовать ключевое слово unique и тогда создастся уникальный индекс,по крайней мере так почему-то заявляет гугл.
SQL
1
2
3
4
5
6
7
8
9
10
CREATE TABLE TagsOfPosts (
  idTag INTEGER,
  idPost INTEGER,
  PRIMARY KEY (idTag, idPost),
  FOREIGN KEY(idPost) REFERENCES favouritePosts(id) ON DELETE CASCADE ON UPDATE CASCADE,
  FOREIGN KEY(idTag) REFERENCES tags(id) ON DELETE CASCADE ON UPDATE CASCADE
);
 
--для поиска тегов поста
CREATE INDEX TagsOfPosts_post ON TagsOfPosts(idPost);
если создать по уникальному индексу на любое из полей idTag или idPost то он конечно будет создан, но это приведет к тому что можно будет привязать только один тег только к одному посту. т.е. связь один к одному через промежуточную таблицу что не имеет смысла.

А если внешний ключ у нас и primary key,причём составной,то индексы создавать не надо,или всё равно надо?
На том же примере так как есть составной первичный ключ то автоматически создается индекс (idTag, idPost). его достаточно что бы искать по idTag но не по idPost так как его "прикрывает" idTag. Поэтому что бы быстро работало например удаление постов нужен еще один обычный индекс по полю idPost.

Ну смотрите: Если у нас первичный ключ не rowid,но есть и rowid (будем считать,что она строго больше нуля и увеличивается на 1),то сортировки,наверное,не будет,поэтому чтобы она была,мы должны явно задать order by ROWID. Если же у нас ROWID первичный ключ,то нам не обязательно писать order by ROWID,т.к записи уже будут в нужном нам порядке,а значит запрос будет выполняться быстрее. Так вот я хочу уточнить,правильно ли я рассуждаю?
Вообще субд не гарантирует выдачу записей в порядке добавления, поэтому сортировка нужна.

Интересно почему,ведь я просто вставляю запись в бд,а не обновляю,к примеру,10000 записей,после удаления первой.
Там как минимум идет поиск последней записи, а затем обновление уже вставленной строки

Странно,просто я читал пост (в прошлом сообщении давал ссылку),в котором есть разница работы этой функции.
Потестил, Да sqlite умеет использовать индексы для max и min так что разницы в запросах нет, но нужен индекс

Интересно,почему Вы не поставили этому полю unique,ведь id постов будет разный?
Если поставить unique то это означает что в таблице TagsOfPosts может быть только одна запись на пост и как следствие только один тег на пост

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

Ещё непонятно зачем мы отдельным запросом пытаемся выполнить SELECT last_insert_rowid() и для какой таблицы функция last_insert_rowid() возвращает результат?
В режиме мультикоманды будет произведена вставка данных и тут же вернется id вставленной строки. last_insert_rowid() вернет postsContent.id, но он тот же что и favouritePosts.id
0
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
15.12.2023, 01:51  [ТС]
На том же примере так как есть составной первичный ключ то автоматически создается индекс (idTag, idPost). его достаточно что бы искать по idTag но не по idPost так как его "прикрывает" idTag.
А,я начинаю понимать. Если бы,к примеру,для категорий/тегов у нас тоже была бы ячейка rowid,как первичный ключ,то нам тоже не нужно было бы создавать для ассоциативной таблицы составной индекс,зато кроме поста нам надо было бы создать индекс в ассоциативной таблице для категории/тега т.е для id,который приходит для них с сервера. По поводу уникального индекса понял,правда ещё остался вопрос почему мы создаём индекс только для ассоциативной таблицы,ведь в запросах мы можем сравнивать id с этой таблице с id в таблице с постами/категориями,а значит для таблицы с постами тоже нужен индекс по полю id,т.к оно не является первичным ключом.
Потестил, Да sqlite умеет использовать индексы для max и min так что разницы в запросах нет, но нужен индекс
Но почему-то человек жалуется в посте,что в разных случаях он работает по-разному,к примеру если прибавлять к нему 1.
В режиме мультикоманды будет произведена вставка данных и тут же вернется id вставленной строки. last_insert_rowid() вернет postsContent.id, но он тот же что и favouritePosts.id
Очень жаль,что sqlite под android не поддерживает мультикомандный режим,т.е можно выполнить только один запрос в одном методе. Ну это,на мой взгляд,не так страшно. Можно будет выбрать max(ROWID),записать результат в java code,и вставить пост с этим значением,а затем вставлять категории/теги для этого поста,используя это значение. Конечно,наверное,можно было бы это значение получить в sqlite,но поскольку оно будет использоваться несколько раз,т.е мы будем делать выборку несколько раз,что может отрицательно сказаться на производительности,лучше,на мой взгляд,использовать java код,чтобы в sql один раз получить это значение и переиспользовать его.

Добавлено через 23 минуты
Если поставить unique то это означает что в таблице TagsOfPosts может быть только одна запись на пост и как следствие только один тег на пост
Так мы же можем поставить unique для id поста,категории и тега в соответствующих таблицах. Тогда и дополнительных индексов,наверное,создавать будет не нужно. Ведь я могу гарантировать,что с сервера придёт уникальный id,а в ассоциативных таблицах у нас будет составной первичный ключ. Кстати раз для primary key индексы не нужны,min и max для них будут работать без проблем?
0
919 / 292 / 58
Регистрация: 01.06.2023
Сообщений: 816
15.12.2023, 07:14
>сли бы,к примеру,для категорий/тегов у нас тоже была бы ячейка rowid,как первичный ключ,то нам тоже не нужно было бы создавать для ассоциативной таблицы составной индекс,зато кроме поста нам надо было бы создать индекс в ассоциативной таблице для категории/тега т.е для id,который приходит для них с сервера.

так как колонка id объявлена INTEGER primary key то теперь она является rowid. В таблице связи применяется составной первичный ключ что бы обеспечить уникальность тегов/категорий т.е. что бы не было двух одинаковых тегов у поста.

на самом деле если сделать запрос

SQL
1
SELECT rowid, * FROM TagsOfPosts
то можно увидеть что в таблице есть еще колонка rowid, которую мы не объявляли. и на ней так же есть индексный доступ из-за особенностей организации хранения


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

На колонки rowid или их заменители, которые объявлены с типом INTEGER primary key, уникальный индекс создается сам по себе тз-за особенностей хранения данных

>Но почему-то человек жалуется в посте,что в разных случаях он работает по-разному,к примеру если прибавлять к нему 1.

Попробовал на таблице с 2 млн записей, работает одинаково быстро.

>Очень жаль,что sqlite под android не поддерживает мультикомандный режим,т.е можно выполнить только один запрос в одном методе.

Ни чего страшного разбейте на 3 обращения к бд должно работать одинаково, главное что бы параллельно ни кто не пытался писать в бд.

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

primary key сразу предполагает что поле уникальное, т.е. однозначно определяет строку. В приведенном пример id поста который пришел с сервера это колонка idPost, ее можно сделать уникальной при необходимости. колонка id ведется в sqlite и нужна не только как первичный ключ но и маркер порядка добавления

>Кстати раз для primary key индексы не нужны,min и max для них будут работать без проблем?

Да, верно. Но я бы использовал last_insert_rowid(). Странно что ее нет в API отдельно.
0
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
15.12.2023, 14:22  [ТС]
так как колонка id объявлена INTEGER primary key то теперь она является rowid. В таблице связи применяется составной первичный ключ что бы обеспечить уникальность тегов/категорий т.е. что бы не было двух одинаковых тегов у поста.
Я понимаю,что у нас ROWID это id,кстати в sqlite под android её не так просто получить,т.е чтобы её получить по-моему надо обращаться к специальной таблице (сам это делал,но не помню деталей,но гуглением найти можно). Но я имею ввиду если бы для категорий/тегов мы бы тоже делали отдельную колонку ROWID,значит тогда для id нам тоже нужно было бы или создавать уникальный индекс,или писать ключевое слово unique,т.к в таблице с постами (как и с категориями/тегами) id приходит разный.
primary key сразу предполагает что поле уникальное, т.е. однозначно определяет строку. В приведенном пример id поста который пришел с сервера это колонка idPost, ее можно сделать уникальной при необходимости. колонка id ведется в sqlite и нужна не только как первичный ключ но и маркер порядка добавления
Я понимаю,но у нас primary key это ROWID (по крайней мере я её так назвал в базе),но поскольку id поста/категории/тега тоже уникальный,я хочу,чтобы в таблицах с постами (категориями/тегами,если у них будет тоже первичный ключ ROWID,а не id,приходящий с сервера) они были тоже уникальны,т.е с ключевым словом unique. В этом случае индекс для них не нужен?
Да, верно. Но я бы использовал last_insert_rowid(). Странно что ее нет в API отдельно.
Что значит нет в api? Это не задокументированная функция? Кстати я тут читал на stackoverflow,что для составных индексов работает первая колонка,или две сразу,а если нам надо делать сравнение по второй колонке,то надо по-любому делать второй индекс,т.е,к примеру,если в ассоциативной таблице у меня составной первичный ключ post_id и category_id,то для category_id нужно создавать отдельный индекс,т.к,как я сам понимаю,сама по себе эта колонка не первичный ключ,а только в паре со второй колонкой. Первичный ключ я привёл как пример. Просто суть в том,что если мы создаём составные индексы и хотим сравнивать только вторую колонку,то нам нужно для неё создавать отдельный индекс. Не понятно только,если это правда,почему для первой колонки в этом случае,если мы хотим ориентироваться по ней,отдельный индекс не нужен? Кстати не пострадает ли производительность,если мы объединим таблицу саму с собой (это я про хранение content в таблице с постами,чтобы избежать дублирования id постов). Ещё подскажите пожалуйста правильный и оптимальный запрос для получения категорий/тегов поста,зная его id (понимаю,что для получения постов определённой категории/тега запрос будет аналогичный,не считая имён колонок). Кстати насчёт max,я имею ввиду есть проблема,если к максимальному значению прибавлять 1 и на stackoverflow вроде пытались объяснить почему (https://stackoverflow.com/a/32453121/7160726).
0
919 / 292 / 58
Регистрация: 01.06.2023
Сообщений: 816
15.12.2023, 14:55
Но я имею ввиду если бы для категорий/тегов мы бы тоже делали отдельную колонку ROWID,значит тогда для id нам тоже нужно было бы или создавать уникальный индекс,или писать ключевое слово unique,т.к в таблице с постами (как и с категориями/тегами) id приходит разный.
Верно

Что значит нет в api?
я к тому что не нашел функции в андроид db.last_insert_rowid(), только через select и execute

Просто суть в том,что если мы создаём составные индексы и хотим сравнивать только вторую колонку,то нам нужно для неё создавать отдельный индекс. Не понятно только,если это правда,почему для первой колонки в этом случае,если мы хотим ориентироваться по ней,отдельный индекс не нужен?
Не понятно только,если это правда,почему для первой колонки в этом случае,если мы хотим ориентироваться по ней,отдельный индекс не нужен?
Попробую показать пример на пальцах. Представьте себе таблицу история измерения температуры, с столбцами
- Дата - в формате число
- Время в формте число
- само значение

Если отсортировать по колонкам дата затем время то получим аналог индекса по двум колонкам. Что бы найти значение по дате и времени мы быстро листаем в нужную позицию и берем значение. На компьютере используется двоичный поиск а мы визуально можем определить где в документе примерно находится интересующая точка.

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

Если же нам нужно будет найти все значения которые был с полуночи до 6 утра, не важно какой даты, то окажется что данные разбросаны по все таблице и проще просканировать всю таблицу с начала
Но, если мы отсортируем только по времени (индекс по второму столбцу) то интересующие значения опять будут сгруппированы в одном месте.



Кстати насчёт max,я имею ввиду есть проблема,если к максимальному значению прибавлять 1 и на stackoverflow вроде пытались объяснить почему (https://stackoverflow.com/a/32453121/7160726).
мб это косяк конкретного движка. единицу можно прибавлять и позже тогда не будет преград для оптимизации запроса

SQL
1
SELECT COALESCE((SELECT MAX(id) FROM myTable),0) + 1
Ещё подскажите пожалуйста правильный и оптимальный запрос для получения категорий/тегов поста,зная его id (понимаю,что для получения постов определённой категории/тега запрос будет аналогичный,не считая имён колонок)
SQL
1
2
3
4
5
6
SELECT 
  *
FROM 
  tags  
WHERE 
  id IN (SELECT idTag FROM TagsOfPosts WHERE idPost = :CurPost)
0
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
15.12.2023, 16:05  [ТС]
Но, если мы отсортируем только по времени (индекс по второму столбцу) то интересующие значения опять будут сгруппированы в одном месте.
Если этот столбец будет проиндексирован? А если бы у нас были два простых индекса,вместо одного составного,то тогда бы при сортировке по дате и времени тоже не нужно было пересканировать таблицу? Просто я хочу понять для себя,может лучше создавать два простых индекса,вместо одного составного и одного по второй колонке. Также если я буду делать выборку,используя оператор AND,для второй колонки тоже будет пересканирование,если нету составного индекса?
мб это косяк конкретного движка. единицу можно прибавлять и позже тогда не будет преград для оптимизации запроса
Вы читали объяснение поста,на которое я давал ссылку,просто согласны ли Вы с ним? Для меня просто тоже очень странно,что при прибавлении единицы происходит пересканирование таблицы,но вот такое я прочитал,при этом человек объясняет возможную причину,правда я это объяснение не понял.
SQL
1
2
3
4
5
6
SELECT 
  *
FROM 
  tags  
WHERE 
  id IN (SELECT idTag FROM TagsOfPosts WHERE idPost = :CurPost)
Огромное Вам спасибо. Просто похожую ситуацию советовали решить join,но,как я вижу,он здесь не нужен.
0
 Аватар для Аватар
5393 / 1465 / 513
Регистрация: 31.05.2012
Сообщений: 5,153
15.12.2023, 16:14
Цитата Сообщение от КАВ Посмотреть сообщение
Просто похожую ситуацию советовали решить join,но,как я вижу,он здесь не нужен
что за боязнь джоинов? не факт что джоин ниже будет хуже, возможно лучше. нужно по плану посмотеть
SQL
1
2
3
SELECT *
  FROM tags
  JOIN TagsOfPosts ON TagsOfPosts.idTag=tags.id AND TagsOfPosts.idPost = :CurPost
или так его перевернуть
SQL
1
2
3
4
SELECT tags.*
  FROM TagsOfPosts
  JOIN tags ON TagsOfPosts.idTag=tags.id
  WHERE TagsOfPosts.idPost = :CurPost
0
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
15.12.2023, 17:33  [ТС]
или так его перевернуть
Мне кажется,что второй вариант будет быстрее,т.к сравнение идёт по одной колонке,хотя не факт. Кстати я хочу удалить теги/категории,если не осталось не одной категории/тега у текущего поста. Поскольку с post_id я не смог придумать нормального запроса,то удаляю категории/теги,если их нет в ассоциативной таблице. Подскажите пожалуйста,правильно ли я делаю (таблицы и поля будут называтся так,как они у меня названы в базе),т.е насколько оптимальный этот запрос,или можно что-то улучшить (выполняю после удаления поста и рассчитываю,что в ассоциативной таблице он тоже удалится,т.к каскадное удаление):
SQL
1
DELETE FROM categories WHERE NOT EXISTS(SELECT id FROM post_category_relation WHERE id=categories.id)
0
 Аватар для Аватар
5393 / 1465 / 513
Регистрация: 31.05.2012
Сообщений: 5,153
15.12.2023, 18:00
если формально рассуждать то второй лучше. но и тот с in формально очень плохой - по каждой строке из tags выполняется подзапрос. но есть оптимизатор и он может перевернуть с ног на голову или наоборот. поэтому и смотреть план нужно. насколько оптимизатор хорош у sqlite не в курсе

Добавлено через 18 минут
Цитата Сообщение от КАВ Посмотреть сообщение
насколько оптимальный этот запрос,или можно что-то улучшить (выполняю после удаления поста и рассчитываю,что в ассоциативной таблице он тоже удалится,т.к каскадное удаление)
если это удаление из главной таблицы то в дочерней строки на автомате удалятся если при создании внешней связи указано каскадное удаление. иначе проверь в плане запроса
0
17 / 5 / 0
Регистрация: 16.04.2016
Сообщений: 344
15.12.2023, 19:01  [ТС]
Ну тут,на мой взгляд,хорошего запроса не будет т.к,если рассуждать с точки зрения обычных языков программирования,нужно проитерировать по ассоциативной таблице с постами/категориями/тегами и для каждого id проверить не равен ли,допустим,второй столбец,к примеру,id поста. Если равен,добавить в массив первый столбец. Затем проитерировать по таблице с категориями и выбрать те из них,в которых id равен какому-то из элементов массива,каждый раз итерируя по этому массиву. Кстати почему Вы вместо,к примеру,left join используете просто join?

Добавлено через 5 минут
Так я и рассчитываю,что в ассоциативной таблице строки удалятся,а значит не будет существовать запроса на выборку id категории из ассоциативной таблицы с нужным id поста,а значит категорию можно удалять.

Добавлено через 12 минут
Хотя нет,он работать не будет,т.к я уже сделал первичным ключом ROWID (на всякий случай для всех таблиц,хотя в тз должны показываться только посты в порядке добавления),а значит надо или в java коде хранить для каждого поста ROWID,или вначале выбирать rowid,а потом проделывать с ним соответствующие операции,т.к хоть я ещё не проверял,но не факт,что sqlite даст мне создать ассоциативную таблицу,если первое и второе поле не первичные ключи,т.к у меня была похожая проблема,когда я пытался создать ассоциативную таблицу с составным первичным ключом,но одно из полей главной таблицы не было первичным ключом.

Добавлено через 21 минуту
Хотя нет,всё норм,т.к я не использую id поста. Мне просто будет нужно заменить id на ROWID.
0
919 / 292 / 58
Регистрация: 01.06.2023
Сообщений: 816
18.12.2023, 08:58
Если этот столбец будет проиндексирован? А если бы у нас были два простых индекса,вместо одного составного,то тогда бы при сортировке по дате и времени тоже не нужно было пересканировать таблицу? Просто я хочу понять для себя,может лучше создавать два простых индекса,вместо одного составного и одного по второй колонке. Также если я буду делать выборку,используя оператор AND,для второй колонки тоже будет пересканирование,если нету составного индекса?
Два индекса по одному полю при поиске по двум полям даст хуже результат, так как в лучшем случае будет сделан поиск по двум индексам с последующим объединением результатов. В худшем случае будет скан по индексу который дает лучшую селиктовность. затем выбор из таблицы строк, а по второму полю будет просто фильтр (без использования индекса).
В том пример что я привел индекс по двум полям (idTag, idPost) создан автоматически так как первичный ключ составной. И в случае поиска по idTag будет просканирован только индекс без просмотра самой таблицы - что означает меньше операций чтения с диска.

Вы читали объяснение поста,на которое я давал ссылку,просто согласны ли Вы с ним? Для меня просто тоже очень странно,что при прибавлении единицы происходит пересканирование таблицы,но вот такое я прочитал,при этом человек объясняет возможную причину,правда я это объяснение не понял.
Да, смотрел. объяснение тут https://www.sqlite.org/optoverview.html (п. 13)

что за боязнь джоинов? не факт что джоин ниже будет хуже, возможно лучше. нужно по плану посмотеть
Ни какой боязни. Для простоты понимая автору. Что Join что in дадут одинаковый план.

Кстати я хочу удалить теги/категории,если не осталось не одной категории/тега у текущего поста.
Это потребует два полных прохода по обоим таблицам, но будет быстрее чем постоянное обращение к индексу на 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 
)
тот запрос что у Вас можно с небольшой доработкой использовать в триггере, в нем проверять если это была последняя ссылка на категорию то удали категорию

если формально рассуждать то второй лучше. но и тот с in формально очень плохой - по каждой строке из tags выполняется подзапрос.
Вы не правы, будет сначала выполнен подзапрос. Затем для каждой его строки сделана выборка из основной таблицы. Важно что бы были индексы tags(id) и TagsOfPosts(idPost, ...)

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

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

Кстати почему Вы вместо,к примеру,left join используете просто join?
Это разные операторы Join (или inner join) и left join. Применение зависит от задачи. Поищите по ключевым словам "sql join понимание"

Хотя нет,всё норм,т.к я не использую id поста. Мне просто будет нужно заменить id на ROWID.
Если ID объявить как integer primary key то он станет алиасом для rowid, т.е. физически у rowid будет два имени (rowid и id)
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
18.12.2023, 08:58

Как правильно обновить 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. Как сделать так, чтоб в списке были только названия файлов, без пути к...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
После бана на пикабу заблокировали авторизацию аккаунта с яндекса.
Hrethgir 09.07.2026
В частности https:/ / auto. ru/ logbook/ zapret-na-vozhdenie-posle-65-let_1cf28789-07b4-4c75-a2f7-4cd4edf9deeb тут хотел оставить комментарий такой Зрите в корень - монополизация на разработку программ. . .
GitDOS: Когда я решил скрестить MS-DOS, GitHub и Claude
MaGz GoLd 08.07.2026
Привет, Киберфорум! Хочу поделиться своим проектом, который, надеюсь, вас заинтересует. GitDOS — это веб-приложение, которое позволяет запускать DOS в браузере с тремя крутыми фичами: диски живут в. . .
Учебный мини "цифровой двойник" = (Сервер = Java + Flask_питон )+(3D = Гугловскаая 4T nvideo + google colab) +( WMS = Odoo на Docker)
anaschu 07.07.2026
Увидел в требованиях к вакансии создателя имитационных моделей связку (REst + Api + WMS + Anylogic + питон + 3D Nvideo), и решил посмотреть, что это такое. Из этого всего я работал более менее. . .
Море в объективе
kumehtar 06.07.2026
Хотел написать много проникновенных слов, но передумал. Оно само - говорит. Кто слышит тот слышит.
Doom для терминала без стрельбы и монстров. 3D Raycasting на ascii.
dcc0 05.07.2026
Попросил нейронную сеть deepai. org написать рейкастинг 3D с библиотекой ncurses для Linux. Чтобы можно было ходить на стрелочки. Чтобы стены были отрисованы символами. Справилась. Первый вариант. . .
Установка статуса документа по условию
Maks 05.07.2026
Алгоритм из решения ниже реализован на нетиповом документе "НарядПутевка" разработанного в КА2. Задача: в табличной части "Материалы" документа при записи автоматически устанавливать статус. . .
Сезонность и суточность закисления почв
anaschu 04.07.2026
200 часов это все равно моловато. Есть ситуации, но нестандартные, когда смена происходит за 5 лет. Но обычно это 50 лет и более. Наверное, закисление почвы происходит сезонно в средней. . .
В чем ценность человеческого опыта в глобальном смысле?
kumehtar 03.07.2026
Возможно, ценность человека не в том, что он однажды достигает мудрости, а в том, что он становится носителем карты пути. Он знает не только истину, но и последовательность внутренних изменений,. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru