Форум программистов, компьютерный форум, киберфорум
Базы данных
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.89/18: Рейтинг темы: голосов - 18, средняя оценка - 4.89
2 / 2 / 0
Регистрация: 22.03.2022
Сообщений: 225

Таблицы и их связи для месенджера на сервере

26.03.2023, 12:42. Показов 4904. Ответов 47

Студворк — интернет-сервис помощи студентам
Всем привет.
Пишу для себя в качестве тренировки клиент и сервер месенджера на Qt.
И та и другая часть работают, сервер регистрирует юзера, юзер заходит, все норм.
Но теперь нужны таблицы для сохранения сообщений(группового чата и чата между 2 людьми)
Таблицы:
User(id, name, pass, email)
Chat(id, name)
Message(user_id, chat_id, timestamp, message)

Таблица Chat по моему мнению хранит имена чатов для всех общений как для двух так и для групповых.
Вопрос в именовании. Например User1 и User5. 1 решил написать 5 юзеру, когда он выбрал его и написал мы создали имя их чата и внесли в таблицу(Как формировать имена для их чата и как определять их чат если потом 5 юзер выбрал 1 и пишет ему?).

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

И еще вопрос, на данном этапе я использую SQlite, стоит ли продолжать на нем в данном проекте работать или лучше перейти на MySQL или на PostgreSQL?
Заранее благодарю отозвавшихся за правки и советы.
0
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
26.03.2023, 12:42
Ответы с готовыми решениями:

Какая ОС должна быть на сервере для связи с приложением под iOS?
Вопрос. Прога которая роботает на IOS должна быть связана с сервером на OS X server или может быть связана с сервером на Linux ??

Как установить связи для подчиненной таблицы?
Здравствуйте. Проблема следующая: база данных в access, в ней три главных таблицы и одна подчиненная..в delphi на форму кинул...

Программное создание связаной таблицы для связи с источником данных
Доброго времени суток уважаемые форумчане! Подскажите пож-та с помощью какого оператора можно осуществить создание связаных таблиц из...

47
2 / 2 / 0
Регистрация: 22.03.2022
Сообщений: 225
09.04.2023, 19:54  [ТС]
Студворк — интернет-сервис помощи студентам
Я сейчас разбираюсь с этим запросом что вы очень хорошо описали. Могли бы только описать в каком порядке происходит раскрутка запроса? Если я правильно понимаю то два SELECT которые в INNER JOIN и LEFT JOIN выполняются первыми?

Добавлено через 10 минут
Попробовал я ваш запрос, и это действительно то что я (пытался) хотел получить. первые 17 записей в моем случаи то что нужно, а потом много раз дублируется, пока не понимаю почему.
0
 Аватар для index58
59 / 25 / 10
Регистрация: 01.07.2016
Сообщений: 153
11.04.2023, 19:32
Лучший ответ Сообщение было отмечено SektorCT как решение

Решение

Занят был, извините.
Цитата Сообщение от SektorCT Посмотреть сообщение
Я сейчас разбираюсь с этим запросом что вы очень хорошо описали.
Описал хорошо, а работает неправильно
Цитата Сообщение от SektorCT Посмотреть сообщение
Могли бы только описать в каком порядке происходит раскрутка запроса? Если я правильно понимаю то два SELECT которые в INNER JOIN и LEFT JOIN выполняются первыми?
Нет, в данном случае они выполняются по порядку, в котором написаны. В другом порядке запросы выполняются, например, если написано:
SQL
1
SELECT ... FROM (SELECT ... )
Цитата Сообщение от SektorCT Посмотреть сообщение
Попробовал я ваш запрос, и это действительно то что я (пытался) хотел получить. первые 17 записей в моем случаи то что нужно, а потом много раз дублируется, пока не понимаю почему.
Потому что я неправильно написал запрос
Вот теперь проверил, переписал.
И для удобства я:
1) изменил имена в таблицах,
2) создал псевдонимы для двух таблиц,
3) создал представление, связывающее таблицы.
Сначала покажу код, который у меня получился, а потом объясню, что это значит, и зачем.
Код создания таблиц:
SQL
1
2
3
4
5
6
7
8
9
10
11
12
CREATE TABLE "User" (
    "User_ID" BIGINT NOT NULL,
    "User_Name" VARCHAR(100) NOT NULL
);
CREATE TABLE "Chat" (
    "Chat_ID" BIGINT NOT NULL,
    "Chat_Name" VARCHAR(100) NOT NULL
);
CREATE TABLE "UserChat" (
    "UserChat_UserID" BIGINT NOT NULL,
    "UserChat_ChatID" BIGINT NOT NULL
);
Код создания псевдонимов таблиц:
SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- Таблица "User", выступающая под псевдонимом "Owner" (владелец аккаунта)
CREATE VIEW "Owner" AS
    SELECT
        "User_ID"   AS "Owner_ID",
        "User_Name" AS "Owner_Name"
    FROM
        "User"
;
-- Таблица "UserChat", выступающая под псевдонимом "OwnerChat" (чат владельца)
CREATE VIEW "OwnerChat" AS
    SELECT
        "UserChat_UserID" AS "OwnerChat_UserID",
        "UserChat_ChatID" AS "OwnerChat_ChatID"
    FROM
        "UserChat"
;
Код создания связывающего представления:
SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- Создаём представление для связи таблиц
CREATE VIEW "v_OwnerUserChat" AS
    SELECT
        *
    FROM
        "Owner"
            CROSS JOIN
        "User"
            INNER JOIN
        "OwnerChat" ON "OwnerChat_UserID" = "Owner_ID"
            LEFT JOIN
        "UserChat" ON "UserChat_UserID" = "User_ID" AND "OwnerChat_ChatID" = "UserChat_ChatID"
            LEFT JOIN
        "Chat" ON "UserChat_ChatID" = "Chat_ID"
;
Код внесения данных:
SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- Заносим данные
INSERT INTO "User" ("User_ID", "User_Name") VALUES
     (1, 'UserTest1')
    ,(2, 'UserTest2')
    ,(3, 'UserTest3')
    ,(4, 'UserTest4')
;
INSERT INTO "Chat" ("Chat_ID", "Chat_Name") VALUES
     (1, 'Chat1')
    ,(2, 'Chat2')
    ,(3, 'Chat3')
    ,(4, 'Chat4')
;
INSERT INTO "UserChat" ("UserChat_UserID", "UserChat_ChatID") VALUES
     (1, 1)
    ,(2, 1)
    ,(1, 2)
    ,(3, 2)
    ,(4, 1)
;
Выполняем первый запрос:
SQL
1
2
3
4
5
6
7
8
9
-- В каких чатах пользователь 2 у пользователя 1
SELECT
    "Owner_Name", "User_Name", "Chat_Name"
FROM
    "v_OwnerUserChat"
WHERE
    "Owner_ID" = 1 AND "User_ID" = 2
ORDER BY
    "Owner_Name", "User_Name", "Chat_Name"
Результат:
Code
1
2
3
4
Owner_Name|User_Name|Chat_Name|
----------+---------+---------+
UserTest1 |UserTest2|Chat1    |
UserTest1 |UserTest2|         |
Выполняем второй запрос:
SQL
1
2
3
4
5
6
7
8
9
-- В каких чатах каждый пользователь у пользователя 1
SELECT
    "Owner_Name", "User_Name", "Chat_Name"
FROM
    "v_OwnerUserChat"
WHERE
    "Owner_ID" = 1
ORDER BY
    "Owner_Name", "User_Name", "Chat_Name"
Результат:
Code
1
2
3
4
5
6
7
8
9
10
Owner_Name|User_Name|Chat_Name|
----------+---------+---------+
UserTest1 |UserTest1|Chat1    |
UserTest1 |UserTest1|Chat2    |
UserTest1 |UserTest2|Chat1    |
UserTest1 |UserTest2|         |
UserTest1 |UserTest3|Chat2    |
UserTest1 |UserTest3|         |
UserTest1 |UserTest4|Chat1    |
UserTest1 |UserTest4|         |
Теперь объяснение, почему я так делаю.

1) Видите, какие короткие два запроса получились в конце? Это благодаря тому, что я связываю таблицы не в каждом запросе, а связываю их один раз в представлении. То есть, я один раз подумал над связями, один раз правильно написал эти связи, и забыл о них. Всё, теперь в запросах мне не нужно думать над этими связями, а просто писать:
SQL
1
SELECT <всё что нужно> FROM <представление> WHERE <всё что нужно> ORDER BY <всё что нужно>
Это очень удобно.

2) Зачем полям таблиц такие имена типа "User_ID", "User_Name"? Что, разве нельзя просто "ID", "Name"? Можно. Но если в двух таблицах есть поля с одинаковым названием (например, в таблице user есть поле id и в таблице chat есть поле id), то когда мы их джойним простым способом user JOIN chat, и делаем из этого SELECT:
SQL
1
SELECT id FROM (SELECT USER CROSS JOIN chat)
, то СУБД выдаёт ошибку, смысл которой можно описать так: "Чувак, здесь два поля с названием "id", и я не знаю, какое из них тебе нужно, сорян". А если мы называем все поля таким способом "User_ID", "Chat_ID", то получается, что названия разные, и можно смело джойнить две разные таблицы простым способом "User" JOIN "Chat", и при селекте у СУБД не возникает проблем:
SQL
1
SELECT "User_ID" FROM (SELECT "User" CROSS JOIN "Chat")
И здесь такой ошибки не будет. Поэтому я всегда называю поля таким вот способом: "<имя сущности>_<имя свойства>" (через знак подчёркивания). И обязательно в кавычках, иначе большие буквы будут превращаться в маленькие внутри СУБД (PostgreSQL), из-за чего могут быть ошибки.

3) Что такое псевдоним таблицы? Это по сути та же самая таблица, но выступающая в запросах под другим именем. Хотя, технически в СУБД это не таблица, а представление, но по сути для меня это таблица под псевдонимом. В каких случаях таблице нужен псевдоним? В случае, например, когда пользователь может выступать в разной роли. Вот как в Вашем случае, где пользователь может выступать: 1) в роли владельца аккаунта, КОТОРЫЙ СМОТРИТ на свой экран, 2) в роли не-владельца, а участника системы, НА КОТОРОГО СМОТРЯТ. Когда такое происходит, то возникает потребность джойнить таблицу "User" саму с собой "User" JOIN "User", а так делать нельзя — СУБД выдаст ошибку. И чтобы СУБД не выдавала ошибку, нужно дать таблице псевдоним, например "Owner", чтобы получилось: "Owner" JOIN "User". И тогда такой ошибки не будет. Но ведь этого можно достичь и без создания представления? Да, можно. Зачем тогда создавать представление? Чтобы создать ещё и псевдонимы полей этой таблицы. Ведь если поля мы оставим без изменений, то получится, что и в таблице "Owner" есть поле "User_ID", и в таблице "User" тоже есть поле "User_ID", при этом мы их джойним, и возникает ошибка, рассмотренная в пункте 2 выше. И вот чтобы такой ошибки не возникало, я создаю псевдоним для каждой таблицы, которую нужно будет джойнить простым способом саму с собой.

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

И посмотрите, пожалуйста, на результаты выполнения запросов. Это то, что нужно, или не совсем?
1
2 / 2 / 0
Регистрация: 22.03.2022
Сообщений: 225
18.04.2023, 09:49  [ТС]
Цитата Сообщение от Annemesski Посмотреть сообщение
просматривает хранящиеся у него сообщения и отправляет пользователю те, которых нет в списке отправленном пользователем. Для хеширования сообщений посмотрите в сторону QUuid class.
Цитата Сообщение от Annemesski Посмотреть сообщение
Для хеширования сообщений посмотрите в сторону QUuid class.
У меня вот к этому 2 вопроса только.
1: на стороне клиентов в каком виде должны хранится сообщения, в виде такой же базы с одной таблицей Сообщения аналогичной серверной? Или в каком то другой виде?
2: могли бы описать примерно как это хеширование через QUuid должно работать? Первый раз с этим сталкиваюсь.

Спасибо
0
 Аватар для Annemesski
2689 / 1346 / 485
Регистрация: 08.11.2016
Сообщений: 3,734
20.04.2023, 12:45
Цитата Сообщение от SektorCT Посмотреть сообщение
на стороне клиентов в каком виде должны хранится сообщения
Решительно не важно в каком виде, тут главное чтобы клиенту и серверу был доступен одинаковый набор данных о сообщении чтобы его идентифицировать.
Цитата Сообщение от SektorCT Посмотреть сообщение
описать примерно как это хеширование через QUuid должно работать?
А почитать? Коротко суть в том, что UUID - это по сути это просто очень большое число, которое можно генерировать по некоторому набору данных, при этом одни и теже исходные данные дают один и тот же UUID, а малейшие различия дают сильные различия в UUID.

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

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

Ваш алгоритм может выглядеть примерно так:
1. Сервер получает сообщение от пользователя и перед тем как принять решение о передаче его другим пользователям сохраняет сообщение у себя генерируя для него UUID, например, по тексту самого сообщения, временной метке получения и пары отправитель получатель, после чего пытается переслать это сообщение всем причастным.
2. Клиент, каждый раз подключаясь к серверу, запрашивает у него список UUID всех сообщений которые есть в БД сервера, затем сравнивает полученный с сервера список UUID со своим вычитая свой список из списка сервера и, если разница не пуста, значит на сервере есть сообщения которых нет у пользователя.
3. Пользователь докачивает к себе те сообщения которых у него нет.
0
2 / 2 / 0
Регистрация: 22.03.2022
Сообщений: 225
23.04.2023, 22:53  [ТС]
Цитата Сообщение от Annemesski Посмотреть сообщение
например, по тексту самого сообщения, временной метке получения и пары отправитель получатель, после чего пытается переслать это сообщение всем причастным
Спасибо, я теперь понял что uuid это именно хеш из совокупности этих данных.
И еще один вопрос, вот если у меня со стороны сервера в базе для всех юзеров и всех чатов только 1 таблица messages то из практической точки зрения стоит ли со стороны юзера под каждого создавать базу и у каждого юзера в ней будут таблицы под каждую чат комнату? Я просто подумал вдруг это было бы удобнее.
Или стоит иметь одну базы для любого юзера но таблицы уже буду под каждого юзера но со всеми его сообщениями?
Интересно просто вопрос со стороын продуктивности работы с хешированием и доступами.
Спасибо.
0
2 / 2 / 0
Регистрация: 22.03.2022
Сообщений: 225
07.05.2023, 13:41  [ТС]
Цитата Сообщение от Annemesski Посмотреть сообщение
Ваш алгоритм может выглядеть примерно так:
Вы не могли бы подсказать как правильно бы выгледела проверка и обновление базы?
Предположим у юзера пара чатов. Он подключился к серверу, сервер собрал для данного юзера все сообщения по всем доступным ему чатам. И посылает юзеру примерного вида QMap<int, QList<QUuid>> то есть номер чата и хеш каждого сообщения попорядку.
Предположим:
ChatRoom1
- message1
- message2
- message3
ChatRoom2
- message1

а вот у клиента в базе по чату 1 только 2 сообщения.
ChatRoom1
- message1
- message3

Получается мы каждый хеш что прилетел от сервера должны сравнить со всеми сообщениями у клиента? Я правильно понимаю?
Вопрос, как определять по uuid какое именно сообщение отсутствует и как его дописывать?
И если я правильно понимаю то клиент обнаружив отсутствие сообщения должен уже посылать запрос серверу и сервер найти нужное и уже послать обратно?
Спасибо.
0
 Аватар для Annemesski
2689 / 1346 / 485
Регистрация: 08.11.2016
Сообщений: 3,734
08.05.2023, 23:16
Цитата Сообщение от SektorCT Посмотреть сообщение
QMap<int, QList<QUuid>>
Плохая идея. Стройте uuid на основе полной информации о сообщении: в каком оно чате, от какого пользователя кому адресовано, когда пришло на сервер и само сообщение, все эти атрибуты хранятся на сервере, при пересылке сообщения клиенту, также пересылайте и атрибуты, а на стороне пользователя программа пусть раскладывает сообщения по категориям согласно атрибутам.
0
2 / 2 / 0
Регистрация: 22.03.2022
Сообщений: 225
09.05.2023, 23:51  [ТС]
Цитата Сообщение от Annemesski Посмотреть сообщение
Плохая идея.
Не совсем вас понял в чем плохая идея.
Я на стороен сервера при подключении клиента и по его запросу формирую контейнер с номером чата и всеми сообщениями данного чата, QMap<int, QList<QUuid>> и в таком виде посылаю клиенту. uuid как раз и формирую из всех перечисленных вами параметров.
Я просто не могу придумать как сравнивать лучше и определять по uuid а потом еще и сообщить серверу какого сообщения нету у клиента.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
09.05.2023, 23:51

Схема связи в SQL сервере 2008
Столкнулся с проблемой &quot;Конфликт инструкции ALTER TABLE с ограничением FOREIGN KEY.. &quot; при попытке сохранить схему связи. Нужно...

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

Таблицы и их связи
Доброго времени суток! Есть задание: &quot;Разработать информационную систему «Система учета материальных ценностей на складе». Администратор...

Связи таблицы
Добрый день! Нужен ваш совет, помощь. Есть такая схема данных на картинке приложена. И есть несколько вопросов 1. Верна ли она? 2. В...

Запросы и связи таблицы
«Автомашины» имеет структуру: номер автобазы, номер машины, ФИО водителя. «Автоперевозки» содержит следующую информацию: номер машины, дата...


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

Или воспользуйтесь поиском по форуму:
48
Ответ Создать тему
Новые блоги и статьи
Запрет пометки на удаление и отмены проведения
Maks 16.07.2026
Алгоритм из решения ниже реализован на нетиповом документе "ПутевойЛист" разработанного в КА2. Задача: запретить помечать на удаление и отменять проведения путевого листа при условии, если он сдан. . .
Как надо было делать правильно - вообще не слушать ИИ, его дело - текст.
Hrethgir 15.07.2026
А вообще надо было делать не так, чтобы не исправлять ошибку https:/ / www. cyberforum. ru/ blogs/ 223907/ 10996. html , правильно было : удалил кнопку, а потом добавил её. Всё это стресс конечно. Почему?. . .
Знаете, от чего появляется стресс?
kumehtar 15.07.2026
Стресс появляется оттого, что вы, зная как поступить правильно, делаете наоборот. Ларри Уингет
Нас бьют, а мы крепчаем? Какая чушь!
kumehtar 15.07.2026
Вот это вот бытующее мнение, что человек меняется только под действием проблем, всегда считал вредоносным. Поверь, от проблем у людей появляются не изменения, а нервное расстройство и ПТСР. А. . .
сукцессия 29. Переход от одних деревьев на другие делать более или менее вероятностным?
anaschu 12.07.2026
Насколько смена типов микоризы — исключительное событие в двухвековой сукцессии? Оценка вероятности в пространстве параметров В текущей версии модели успешно реализован ключевой механизм. . .
сукцессия 27. Думаю, как переделывать уже написанную статью с планами на сукцессию.
anaschu 12.07.2026
Анализ соответствия модели требованиям Реализованные компоненты: Механизм закисления почвы через протонную помпу Конкуренция между типами микориз pH как триггер сукцессии C/ P соотношение. . .
Сукцессия 26. Мат модель создана.
anaschu 12.07.2026
Модель смены растительных сукцессий посредством управления грибами работает внутри небольшой ячейки почвы, восстанавливающейся после пожара, где ненадолго бывшее царство хвойных снова захватили. . .
Решил проблему с ошибкой пагинации сообщений с сервера на алгоритме обхода дерева "Эстафета хвоста".
Hrethgir 12.07.2026
Проблема была в том, что удалялась именно новая кнопка, а не старая. Ни один ИИ не обнаружил это, а сам я смог только когда с работой стало попроще и когда заставил работать будущее автономное. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru