Модераторы: LSD

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Дублирование стобцов с одинаковым именем, Дублировать или использовать много JOIN 
V
    Опции темы
fridkaratel
Дата 25.11.2012, 12:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 221
Регистрация: 22.10.2007
Где: Error connect to MySQL Da...

Репутация: нет
Всего: нет



Есть следующие таблицы:
Клиенты: ClientId | Name
Товары: CommId
Заказы: OrderId | ClientId
Заказанные товары: OrderCommId | OrderId | ClientId | CommId
Выданные товары: IssueCommId | OrderCommId | OrderId | ClientId | CommId

---------------------------------

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

Вариант 1
Так и дублировать столбцы в каждой таблице.

Плюсы:
1) Достаточно одного SELECT'а без использования JOIN'ов - вся нужная информация выбирается сразу одним запросом

Вариант 2
Использовать JOIN'ы.
Но в этом случае, чтобы из выданных товаров получить имя клиента, над сделать несколько JOIN'ов...

1. Сначала выбрать выданные товары;
2. Затем найти соответствующие им заказанные товары (по столбцу OrderCommId), чтобы
3. Найти заказ, в котором есть эти товары (OrderId) и уже только потом
4. Выбрать пользователя ClientId и узнать его имя.


Плюсы:
1) Меньше столбцов - меньше размер БД
2) Проще изменять данные, т.к. они не дублируются в других таблицах

---------------------------------

Если ещё учитывать того человека, который выдал товар, плюс ещё склад, откуда выдан товар и Id того товара на складе, то будет ещё +3 столбца в таблицы...
Получается,
- с одной стороны, будет много дубликатов данных в разных таблицах;
- с другой стороны, будет много JOIN'ов.

---------------------------------

Хотелось бы использовать JOIN'ы, но при 1 000 000 записей в каждой таблице не будет ли это медленней по сравнению с дублированием столбцов?

Это сообщение отредактировал(а) fridkaratel - 25.11.2012, 12:10
PM   Вверх
Zloxa
Дата 25.11.2012, 18:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Чо?
****


Профиль
Группа: Завсегдатай
Сообщений: 3473
Регистрация: 12.9.2008

Репутация: 11
Всего: 161



Цитата(fridkaratel @  25.11.2012,  13:05 Найти цитируемый пост)
2) Проще изменять данные, т.к. они не дублируются в других таблицах

Какие вам данные сложно изменять при первом подходе?
Заказ на этапе выдачи товара может вдруг внезапно поменять клиента?

Вобще обычно заказы это заказы, а расходные накладные это расходные накладные. Когда речь идет о документах, на всех этапах жизни документа, он должен печататься одинаково, не зависимо о того как поменялось окружение, справочники. Вплоть до того, что если по товару с id=100500 на этапе оформления и утвреждения значились "апельсины", а потом кто-то заменил дескрипшн для этого товара  на "магнитолы", документ, будучи распечатанным повторно, не должен при этом измениться.


--------------------
Достоверно известно, что 89% людей доверяют статистике взятой с потолка smile
PM   Вверх
fridkaratel
Дата 26.11.2012, 02:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 221
Регистрация: 22.10.2007
Где: Error connect to MySQL Da...

Репутация: нет
Всего: нет



@Zloxa:
Цитата
Какие вам данные сложно изменять при первом подходе?


Разрабатываю не для себя, и не для фирмы... а для группы людей...
Например, такой (выдуманный) случай:
1. Клиент позвонил по телефону и заказал яблоки.
2. Менеджер набил в заказ артикул яблок и зарезервировал их на складе.
3. Клиент пришёл получать яблоки, смотрит на них и говорит - я не эти красные хотел, я хотел те красные...
4. В итоге, надо в выдаче изменить одни яблоки на другие.

Или вообще убрать яблоки из выдачи.
Грузчик нёс яблоки клиенту и уронил их.
Всё, нет яблок...
А фактура на выдачу товара есть...
И в базе выдача этих яблок есть.
Надо удалить яблоки из выдачи.

Пример с яблоками привёл для наглядности.

----------

То есть, Zloxa, рекомендуешь использовать 1-й вариант?
PM   Вверх
Arantir
Дата 26.11.2012, 03:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Рыбак без удочки
**


Профиль
Группа: Участник
Сообщений: 960
Регистрация: 18.11.2012

Репутация: 1
Всего: 55



Цитата(fridkaratel @  26.11.2012,  01:50 Найти цитируемый пост)
3. Клиент пришёл получать яблоки, смотрит на них и говорит - я не эти красные хотел, я хотел те красные...
4. В итоге, надо в выдаче изменить одни яблоки на другие.
Цитата

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

Если частичная выдача товара не предусмотрена, то сделайте на подобии этого:

Цитата

...
Заказы: OrderId | ClientId | Status
Заказанные товары: OrderCommId | OrderId | ClientId | CommId
Выданные товары: IssueCommId | OrderCommId | OrderId | ClientId | CommId а этого не нужно
Где status - это одно из четырех значений, эквивалентных "выполнен", "не выполнен", "отменен заказчиком", "отменен продавцом".
А заказанные товары, в данном случае, изменению не подлежат. Если покупатель желает изменить заказ, если тот еще не принят к выполнению, он должен отменить старый и создать новый.
Если есть склад, то учет товаров должен вестись отдельно, с указанием доступного количества товара. Чтобы потом несуществующие яблоки не заказывали.

И ну и что, что на основе товара узнать клиента надо через 3 таблицы. Вам надо оформлять более 100000 заказов в секунду? Если нет, то JOIN никаких последствий не принесет.

Добавлено @ 03:30
Цитата

Вариант 1
Так и дублировать столбцы в каждой таблице.
Никогда так не делаете ни в коем случае ни для чего, кроме уникального идентификатора... Не хочу преувеличить, но при использовании такой мощной структуры как БД, это противоречит логике.
ID для того и нужен, чтобы логически связывать между собой данные в таблицах.

Это сообщение отредактировал(а) Arantir - 26.11.2012, 03:48


--------------------
interface Жопа {
    // ATTENTION: has to be implemented by every class of the project for proper project work
}
PM   Вверх
Arantir
Дата 26.11.2012, 03:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Рыбак без удочки
**


Профиль
Группа: Участник
Сообщений: 960
Регистрация: 18.11.2012

Репутация: 1
Всего: 55



Есть интересное правило построения структуры БД на примере графов. Если знаете теорию графов хоть немного, могу привести пример.

Это сообщение отредактировал(а) Arantir - 26.11.2012, 04:14


--------------------
interface Жопа {
    // ATTENTION: has to be implemented by every class of the project for proper project work
}
PM   Вверх
fridkaratel
Дата 26.11.2012, 04:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 221
Регистрация: 22.10.2007
Где: Error connect to MySQL Da...

Репутация: нет
Всего: нет



@Arantir, спасибо за развёрнутый ответ ;)

Возьмём не заказ и выдачу, а заказ и резерв...

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

--------------------

По теории графов...
Не знаю, но приведи, пожалуйста, пример...
Почитаю на досуге, изучу... ;)

--------------------

Что касается "недублирования" столбцов...
Получается, выборка через 3-4-5 JOIN'ов, чтобы докопаться до "истины" не будет существенно медленней, чем выборка одной строки?
Более 100000 заказов оформлять не надо, но вот просматривать-то надо...
И даже при просмотре 10-20-30 заказов надо дёргать несколько таблиц чере JOIN.
Вот это меня как бы "пугает"... smile

Например...
Требуется посмотреть, кому был выдан товар с артикулом таким-то...

Фактически, в этом случае (без дублирования данных) получится, что надо:
1. Сначала выбрать из заказанных товаров этот артикул
2. Затем через JOIN проверить, есть ли он в выданных
3. Затем через JOIN выбрать заказ, где хранится Id клиента
4. Затем из таблицы клиентов через JOIN выбрать его имя...

Получается, 3 JOIN'а.

В случае же дублирования достаточно только одного JOIN'а с таблицей `client`.

--------------------

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

Это сообщение отредактировал(а) fridkaratel - 26.11.2012, 04:17
PM   Вверх
Arantir
Дата 26.11.2012, 06:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Рыбак без удочки
**


Профиль
Группа: Участник
Сообщений: 960
Регистрация: 18.11.2012

Репутация: 1
Всего: 55



Для вариантов с JOIN и без него разница в одну секунду начинает проявляться при выборке нескольких тысяч строк за один раз. Вам в данном случае надо сделать всего лишь выбор одной строки из каждой таблицы: заказа из заказанных товаров, клиента из заказов и имя из клиентов. Ну будет с JOIN там 2 миллисекунды, а без него - 1 миллисекунда... "Не экономьте на спичках" =)

На счет построения таблиц:
Постройте графы связей между полями данных.
Например, "ClientID --> Name" означает, что каждый клиент указывает (имеет) имя. Для заказа такой граф будет таким "OrderID --> ClientID", каждый заказ имеет клиента. 
Стрелочки расставляем по правилу: стрелочка ведет из уникального поля в не уникальное. Если оба поля уникальные, то стоит задуматься о целесообразности связи между ними.
user posted image
user posted image

Теперь совместим все графы:
user posted imageВидите, какой ужас получился. 

Удалим все лишние ребра, оставив связь между всеми вершинами, пользуясь правилами:
1. На уникальные поля не должно указывать больше одной стрелочки. 
2. Должна быть одна и только одна вершина, у которой все ребра исходящие.
Такой граф называется деревом. Именно приведение графа к виду дерева было нашей целью.
user posted image

Теперь есть правило: если у вершины только одно ребро и оно ведет ИЗ нее, то его направление можно сменить, если это не нарушает правило 2 предыдущего пункта. Пользоваться правилом или нет - зависит от логики связей между этими вершинами. Можно было и оставить, но попробуем уменьшить количество таблиц:
user posted image

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

Как по нему строить таблицы? Очень просто:
1. Для любой вершины, ИЗ которой выходит ребро, нужно создать таблицу со всеми вершинами (полями), в которые она непосредственно ведет (путь равен одному ребру).
2. По желанию можно создать таблицы для тех вершин, что имеют только входящие ребра.

Итого 3 таблицы:
1. Клиенты: Номер Клиента, Имя
2. Заказы: Номер Заказа, Номер Клиента
3. Заказанные товары: Номер Заказанного Товара, Номер Заказа, Номер Товара, Выдача
В данном случае ассортимент товаров ограничен, по-этому добавим:
4. Товары: Номер Товара, Название # а можно только с названием, без номера. но вдруг разные апельсины попадутся =)

Ну и пример некоторых возможных данных в этих таблицах:
-- Товары:
1, Яблоки
2, Апельсины
3, Груши

-- Клиенты:
1, Степан
2, Андрей
256, Петр

-- Заказы:
134, 2
167, 2
345, 256

-- Заказанные товары: 
543, 134, 1, true # выдано
587, 134, 2, true 
645, 167, 2, true 
758, 345, 3, false # не выдано

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

Это сообщение отредактировал(а) Arantir - 26.11.2012, 06:19


--------------------
interface Жопа {
    // ATTENTION: has to be implemented by every class of the project for proper project work
}
PM   Вверх
Zloxa
Дата 26.11.2012, 08:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Чо?
****


Профиль
Группа: Завсегдатай
Сообщений: 3473
Регистрация: 12.9.2008

Репутация: 11
Всего: 161



Цитата(Arantir @  26.11.2012,  07:06 Найти цитируемый пост)
По-моему, идеальная структура для именно этого частного случая.

К сожалению о частном случае мы знаем только исходя из корявых потуг ТС его реализовать.

Мне в твоей схеме не нравится что ты объединил документы заказа и отгрузки. Обосновать это не могу. Озвученной ТС версии задачи это не противоречит, я видел системы где так поступают.... но все равно не нравится. Обычно у заказа и отгрузки свои циклы жизни. Да, отгрузку инициирует заказ, и закрытие отгрузки инициирует закрытие заказа, но на один заказ вполне может быть несколько отгрузок. Заказ изменяет статус(структуру) остатка, а отгрузка списыват остаток. Заказ может быть оформлен на более абстрактом уровне товара, отгузка всегда ведется на конкретном уровне, а если учет партионный, то на уровне партии а не товара.  

В общем не знаю... Думаю, если копнуть задачу поглубже, разделять эти сущности таки придется. smile


--------------------
Достоверно известно, что 89% людей доверяют статистике взятой с потолка smile
PM   Вверх
Arantir
Дата 26.11.2012, 10:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Рыбак без удочки
**


Профиль
Группа: Участник
Сообщений: 960
Регистрация: 18.11.2012

Репутация: 1
Всего: 55



Zloxa, ну что за данные должны были содержаться в "IssueCommId" так и осталось загадкой. А остальные 4 колонки - точная копия таблицы заказов. Выданы могут быть только заказанные, значит там просто дублируется четверка связанных значений из заказов. На первый взгляд выгоднее добавить все в одну таблицу.

Я схему привел как раз для того, чтобы когда ТС улучшит организацию своих данных саму по себе, то ему было проще организовать под нее БД. По-моему, сейчас его система не очень информативная =) По-любому потом какие-то новые поля придется добавить.

"Отгрузка" звучит немного по другому, чем "выдача" =) Выдача со склада доставщику? Выдача покупателю доставщиком? От этого и зависит структура таблиц.

Конечно же, для учета заказов и учета взятия товара со склада надо иметь 2 параллельных таблицы. Но тут не очень понятно было с этим =) Как минимум, значения там не должны бы были дублироваться. 

Это сообщение отредактировал(а) Arantir - 26.11.2012, 10:20


--------------------
interface Жопа {
    // ATTENTION: has to be implemented by every class of the project for proper project work
}
PM   Вверх
fridkaratel
Дата 27.11.2012, 06:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 221
Регистрация: 22.10.2007
Где: Error connect to MySQL Da...

Репутация: нет
Всего: нет



@Arantir:
Цитата
Вам в данном случае надо сделать всего лишь выбор одной строки из каждой таблицы

Мне действительно надо только одну строку выбрать.
А не будет JOIN по всей таблице проходиться, пытаясь найти все доступные значения?
Или установка UNIQUE индекса решит этот вопрос?

Большое спасибо за такой развёрнутый ответ по графам! ;-)
Не знал о таком способе построения.

@Zloxa, @Arantir:
По поводу выдач товаров, а также статуса "Выдано/Не выдано"...
Товар может быть выдан за два, за три раза...

Пример...
Клиент заказал... ну 10 айпадов smile
В наличии есть 3 штуки - он забрал их.
Следующая поставка через 5 дней.
Но таможня разрешает за раз ввести только 6 айпадов.
Получается, во второй выдаче он получит ещё 6 - и останется один в долге.
В итоге, 10 айпадов клиент получит за 3 выдачи.

Поэтому нужна доп. таблица, связанная с заказанными товарами через столбец OrderCommId.
А столбец IssueCommId, как я понял, мне уже не нужен, так как я узнал, что его можно заменить составным UNIQUE индексом.
(из созданной мной темы Использование CHAR в качестве PRIMARY, Стоит или не стоит?)

Это сообщение отредактировал(а) fridkaratel - 27.11.2012, 06:47
PM   Вверх
fridkaratel
Дата 27.11.2012, 11:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 221
Регистрация: 22.10.2007
Где: Error connect to MySQL Da...

Репутация: нет
Всего: нет



В дополнение решил ещё кое-что написать...

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

------------------------------------------------

Описание таблиц


Код

client:
- ClientId        = ID клиента (AUTO_INCREMENT)
- Name            = Имя клиента

client_booking:
- BookingId        = ID заказа (AUTO_INCREMENT)
- ClientId        = ID клиента, подавшего заказ
- BookingDate    = Дата заказа

client_commodity_booking:
- BookingCommodityId    = ID заказанного товара (AUTO_INCREMENT) - это не ID товара в каталоге (!)
- BookingId                = ID заказа, к которому относятся эти товары
- Sku                    = Артикул товара
- Qty                    = Заказанное количество

client_commodity_issue:
- BookingCommodityId    = Служит для того, чтобы через JOIN видеть, сколько выдано из того, сколько заказано
- IssueId                = ID выдачи
- Qty                    = Количество выданного товара

client_commodity_reserve:
- BookingCommodityId    = Служит для того, чтобы через JOIN видеть, сколько зарезервировано из того, сколько заказано
- ManagerId                = ID менеджера, который зарезервировал товары
- Qty                    = Количество в резерве

client_issue:
- IssueId        = ID выдачи
- UserIdFrom    = ID менеджера, кто выдал товар
- UserIdTo        = ID менеджера или клиента, кому выдан товар
- IssueDate        = Дата выдачи

catalog_commodity:
- Sku        = Артикул товара
- Title        = Наименование


------------------------------------------------

Доп. примечания

По таблице "client_commodity_issue"
Товар может быть выдан не за один раз, а за два-три-четыре...
То есть, нельзя столбец IssueQty создать в таблице "client_commodity_booking".
Для учёта выданного товара должна быть отдельная таблица со связью через BookingCommodityId.

По таблице "client_commodity_reserve"
То же самое, что и выше.
Плюс... можно выдать только зарезервированный товар, то есть сначала в резерв, а потом на выдачу.
Дата резерва не нужна.


По таблице "client_issue"
Дело в том, что товар может быть не выдан сразу клиенту, может быть такая схема:
- Менеджер выдал другому менеджеру (в другой офис)
- Далее менеджер выдал опять менеджеру... (так может быть 2-3-4-5-6 раз... то есть, цепочка выдач от менеджера к менеджеру)
- Конечный менеджер уже выдал товар клиенту

------------------------------------------------

Запрос к БД
Это запрос на получение списка всех доступных для выдачи товаров, которые есть в резерве.
Выбираются все товары, зарезервированные менеджером с ID=7 для всех клиентов.

Потом через PHP-скрипт результат группируется на два массива - список клиентов и список товаров по каждому из клиентов.

Вот такой вот монстр... smile

Код

SELECT
    ccb.BookingId,    -- ID заказа

    (
        IFNULL(ccb.BookingQty,    0) -    -- Заказанное количество
        IFNULL(cci.IssueQty,    0) -    -- Выданное количество
        IFNULL(ccr.ReserveQty,    0)        -- Зарезервированное количество
    ) ReserveQtyMax,    -- Сколько можно макс. зарезервировать

    IFNULL(sc.StoreQty, 0) StoreQty,    -- Количество на складе

    c.ClientId,    -- Номер клиента
    c.Name,        -- ID клиента

    cc.Sku,        -- Артикул товара
    cc.Title    -- Название товара

FROM
    `client_commodity_booking` ccb    -- Таблица со списком заказанных товаров

    LEFT JOIN
        `client_booking` cb    -- Чтобы выбрать ID клиента, подавшего заказ
        ON
            cb.BookingId=ccb.BookingId

    LEFT JOIN
        `client_commodity_reserve` ccr    -- Чтобы выбрать, сколько товаров зарезервировано для этого заказа
        ON
            ccr.ManagerId=7
            AND
            ccr.BookingCommodityId=ccb.BookingCommodityId

    LEFT JOIN
        `client_issue` ci    -- Чтобы выбрать все выдачи от менеджера клиенту, чтобы выбрать выданные товары
        ON
            ci.ManagerId=7    -- Товар может быть выдан разными менеджерами, поэтому сначала выбираем выдачи, произведённые этим менеджером

            LEFT JOIN
                `client_commodity_issue` cci    -- Выбираем выданные товары
                ON
                    cci.IssueId=ci.IssueId
                    AND
                    cci.BookingCommodityId=ccb.BookingCommodityId

    RIGHT JOIN
        `store_commodity` sc    -- Выбираем количество на складе
        ON
            sc.StoreId=3
            AND
            sc.Sku=ccb.Sku
            AND
            sc.StoreQty>0

    RIGHT JOIN
        `client` с    -- Выбираем имя клиента
        ON
            c.ClientId=cb.ClientId

    LEFT JOIN
        `catalog_commodity` cc    -- Выбираем название товара
        ON
            cc.Sku=ccb.Sku

HAVING
    ReserveQtyMax<>0

ORDER BY
    с.Name


------------------------------------------------

Вопросы:
1. Не слишком ли громоздко получается?
2. Не будет ли это медленно при бОльших объёмах данных?

Это сообщение отредактировал(а) fridkaratel - 27.11.2012, 11:42
PM   Вверх
Arantir
Дата 27.11.2012, 15:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Рыбак без удочки
**


Профиль
Группа: Участник
Сообщений: 960
Регистрация: 18.11.2012

Репутация: 1
Всего: 55



Добавлено @ 15:08
Цитата

Вопросы:
1. Не слишком ли громоздко получается?
2. Не будет ли это медленно при бОльших объёмах данных?


Да не будет, не будет =) В вашем случае вообще трудно представить, чтобы все это еще как-то тормозило. Не загрузите вы БД так сильно.

Это сообщение отредактировал(а) Arantir - 27.11.2012, 17:48


--------------------
interface Жопа {
    // ATTENTION: has to be implemented by every class of the project for proper project work
}
PM   Вверх
Arantir
Дата 27.11.2012, 17:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Рыбак без удочки
**


Профиль
Группа: Участник
Сообщений: 960
Регистрация: 18.11.2012

Репутация: 1
Всего: 55



Чет с утра в спешке не все внимательно прочел.
Теперь структура кажется более-мене нормальной.

Цитата

client:
- ClientId        = ID клиента (AUTO_INCREMENT)
- Name            = Имя клиента

Клиенты регистрируются на сайте? Тогда им следует выдать уникальный идентифицирующий ключ (проще говоря - логин =). Ну если имя - это логин, то тогда все ОК, но не забудьте обеспечить его уникальность (добавить и ClientId, и Name  в первичный ключ). Раскрывать во внешний мир внутренние идентификаторы - не очень безопасно.

Кстати, почему вы всегда пытаетесь получить всю информацию одним запросом? Не пробовали в php-скрипте разделить на несколько запросов?

Это сообщение отредактировал(а) Arantir - 27.11.2012, 17:57


--------------------
interface Жопа {
    // ATTENTION: has to be implemented by every class of the project for proper project work
}
PM   Вверх
Arantir
Дата 27.11.2012, 18:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Рыбак без удочки
**


Профиль
Группа: Участник
Сообщений: 960
Регистрация: 18.11.2012

Репутация: 1
Всего: 55



Код

client_commodity_issue:
- BookingCommodityId    = Служит для того, чтобы через JOIN видеть, сколько выдано из того, сколько заказано
- IssueId                = ID выдачи
- Qty                    = Количество выданного товара

У вас тут ни одного уникального поля нету, получается.
А почему же не сделать вот так:
Код

client_issue:
- IssueId        = ID выдачи
- IssueDate        = Дата выдачи
- UserIdFrom    = ID менеджера, кто выдал товар
- UserIdTo        = ID менеджера или клиента, кому выдан товар
- BookingCommodityId    = По сути указывает цель выдачи, связывает выдачу с заказом (заказанным товаром).
- Qty                    = Количество выданного товара

Теперь вы можете всего по одной таблице получить информацию о всем пути и количестве выданного товара. А всего по двум таблицам узнать, выдан ли какой-то заказанный товар полностью.


--------------------
Почему посты при разнице в 5 минут соединяются, а при разнице в 10 минут уже даблпостинг =///////

Это сообщение отредактировал(а) Arantir - 27.11.2012, 18:15


--------------------
interface Жопа {
    // ATTENTION: has to be implemented by every class of the project for proper project work
}
PM   Вверх
fridkaratel
Дата 28.11.2012, 01:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 221
Регистрация: 22.10.2007
Где: Error connect to MySQL Da...

Репутация: нет
Всего: нет



@Arantir:

Цитата
Да не будет, не будет =) В вашем случае вообще трудно представить, чтобы все это еще как-то тормозило. Не загрузите вы БД так сильно.

Это очень хорошо smile
А то я боялся...
Правда, смотрю я на такие большИе запросы и немного пугаюсь smile

Цитата
Клиенты регистрируются на сайте?

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

Цитата
Кстати, почему вы всегда пытаетесь получить всю информацию одним запросом? Не пробовали в php-скрипте разделить на несколько запросов?

Наверно, можно и одним...
Но что-то посмотрел я на вышеприведённого "монстра" - и не знаю, где там "распилить" запрос на два...

Цитата
У вас тут ни одного уникального поля нету, получается.

Оно, в принципе, и не нужно - не с чем связывать выданный товар. То есть, это конечная таблица, содержащая просто значения.
В этой таблице я сделал составной UNIQUE ключ по полям BookingCommodityId + IssueId

Цитата
А почему же не сделать вот так:

К выдаче ещё добавляется комментарий, например, "Забрал на синем седане" или "забрал после 19 часов", чтобы на что-то опираться, если возникнут вопросы.
Поэтому придётся этот комментарий дублировать для каждого выданного товара...

Я думал так сделать, но в таком случае получается, что четыре столбца будут дублировать значения: IssueDate, UserIdFrom, UserIdTo и Comment.
PM   Вверх
fridkaratel
Дата 29.11.2012, 05:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 221
Регистрация: 22.10.2007
Где: Error connect to MySQL Da...

Репутация: нет
Всего: нет



А как быть в таком случае?

Код

client:
- ClientId    = ID клиента (AUTO_INCREMENT)
- Name        = Имя клиента
- ManagerId    = Пользователь, чей это клиент (кто его обслуживает)

client_booking_commodity
- BookingCommodityId    = ID для связывания между другими таблицами
- ClientId                = ID клиента, заказавшего товар
- Sku                    = Артикул товара
- BookingQty            = Заказанное количество
- ReserveQty            = Количество, которое зарезервировано со склада
- IssueQty                = Выданное количество


Менеджеру необходимо сформировать заявку на основной склад.
Для этого необходимо получить список долгов его клиентам: DebtQty = BookingQty - ReserveQty - IssueQty

Получается, если не дублировать данные в другой таблице, то выходит такой запрос:
Код

SELECT
    SUM(cbc.BookingQty - cbc.ReserveQty - cbc.IssueQty) AS DebtQty,        -- Считаем количество долгов
    cbc.Sku

FROM
    `client_booking_commodity` cbc        -- Заказанные клиентами товары

RIGHT JOIN
    `client` c            -- Чтобы выбрать только клиентов менеджера с ID=10
    ON
        c.ClientId = cbc.ClientId
        AND
        c.ManagerId = 10

WHERE
    cbc.BookingQty > cbc.ReserveQty + cbc.IssueQty        -- Если товар есть в долгах

GROUP BY
    cbc.Sku        -- Группируем по артикулу, чтобы получить общую заявку


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

С ходу я вижу, что тут надо добавить столбец Status = TINYINT(1) unsigned, который будет содержать 1, если нет долгов и 0, если есть...
Тогда не придётся просчитывать DebtQty...

Как вариант, не добавлять столбец Status, а сделать столбец DebtQty, в который будет записываться количество долга.
Но в этом случае, надо будет при изменении количества заказа (или резерва) изменять ещё и количество долга.

К тому же, меня смущает то, что мне надо делать LEFT JOIN на таблицу `client`, чтобы узнать, относится ли этот клиент к этому менеджеру.
А если менеджеров будет тысяч 5-10 и клиентов на каждого менеджера тоже тысяч по 5-10?

----------------------------------------------------------------

Попутно возник ещё вопрос...

1. Мне надо отобразить список долгов, сгруппированных по артикулу, чтобы оформить заявку на основной склад.
2. Но мне так же надо получить BookingCommodityId и DebtQty, чтобы выставить на резерв товары, которые есть на складе.

Получается такой запрос...
Постарался в SQL-комментариях расписать, что да как...

Код

SELECT
    cbc.BookingCommodityId,    -- Надо только в случае 2
    (cbc.BookingQty - cbc.IssueQty - cbc.ReserveQty) DebtQty,    -- Надо в случаях 1 и 2

    cc.Sku,        -- Надо только в случае 1
    cc.Title,    -- Надо только в случае 1
    cc.Price,    -- Надо только в случае 1
    cc.Pack        -- Надо только в случае 1

FROM
    `client_booking_commodity` cbc

RIGHT JOIN
    `client` c
    ON
        c.ClientId=cbc.ClientId
        AND
        c.ManagerId = 10

RIGHT JOIN                    -- RIGHT, чтобы отсеить товары, которые нельзя получить с основного склада - сняты с продажи
    `catalog_commodity` cc
    ON
        cc.Sku = cbc.Sku
        AND
        cc.ObsoleteDate = 0        -- Дата снятия товара с продажи

WHERE
    cbc.BookingQty > cbc.IssueQty + cbc.ReserveQty

GROUP BY cbc.Sku    -- Надо только в случае 1, т.к. в случае 2 мне надо получить BookingCommodityId, который я не получу при группировке


Имеет ли смысл делать два конкретных запроса?
Или можно обойтись одним, просто группируя в PHP-скрипте по артикулу?

Это сообщение отредактировал(а) fridkaratel - 29.11.2012, 05:52
PM   Вверх
Arantir
Дата 29.11.2012, 08:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Рыбак без удочки
**


Профиль
Группа: Участник
Сообщений: 960
Регистрация: 18.11.2012

Репутация: 1
Всего: 55



Цитата(fridkaratel @  29.11.2012,  04:46 Найти цитируемый пост)
Не медленный он будет?

Если вы не будете пробовать получить все пару сотен тысяч записей сразу, то разницы с JOIN и без него вы не заметите.
В базах данных логарифмические алгоритмы посика. Видели график логарифма? Вот, увеличиваете количество на очень-очень много, а время увеличивается на чуть-чуть. И сортировка тоже логарифмическая. Невозможно сделать логарифмической только выборку. Так как N штук меньше чем за N действий не выберешь.

У вас на пинг между серверами времени больше будет тратится, чем у БД на обработку запросов. Посмотрите на этот форум: [ Время генерации скрипта: 0.3409 ]. А страница появляется далеко не моментально из-за пинга и загрузки информации.

Если вас беспокоит, придется ли менеджеру ждать несколько секунд после нажатия на кнопочку - БД тут точно будет ни при чем.
Если вас беспокоит, не упадет ли БД от множества неэффективных запросов - даже средний домашний компьютер вытянул бы 1тыс. INSERT'ов в секунду, в SELECT'ов и того больше.
Посчитайте: если 15000 менеджеров будут каждые 15 минут оформлять заказ и выполнять при этом по 10 запросов, то это выйдет в среднем 160 запросов в секунду. Если не считать утрированности этой ситуации, то это вообще очень мало.


Цитата(fridkaratel @  28.11.2012,  00:16 Найти цитируемый пост)
Я думал так сделать, но в таком случае получается, что четыре столбца будут дублировать значения: IssueDate, UserIdFrom, UserIdTo и Comment. 

Далеко не все должно иметь вид лога =) Можно изменять и уже имеющиеся записи. Впрочем да, если надо к каждому изменению комментарий, то надо отдельную таблицу для этого.

Цитата(fridkaratel @  29.11.2012,  04:46 Найти цитируемый пост)
Имеет ли смысл делать два конкретных запроса?

Вот и спрашиваю, почему вы все в один запрос пытаетесь вместить...
Посмотрите на это форум: [ Использовано запросов: 35 ]223 гостей, 15 пользователей. И сервер же не падает.

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

Это сообщение отредактировал(а) Arantir - 29.11.2012, 08:26


--------------------
interface Жопа {
    // ATTENTION: has to be implemented by every class of the project for proper project work
}
PM   Вверх
fridkaratel
Дата 29.11.2012, 08:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 221
Регистрация: 22.10.2007
Где: Error connect to MySQL Da...

Репутация: нет
Всего: нет



@Arantir, спасибо за развёрнутый ответ ;)

Буду делать smile
PM   Вверх
Страницы: (2) [Все] 1 2 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Общие вопросы по базам данных"
LSD
Zloxa

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

  • вопросам по СУБД для которых нет отдельных подфорумов
  • вопросам которые затрагивают несколько разных СУБД (например проблема выбора)
  • инструменты для работы с СУБД
  • вопросы проектирования БД
  • теоретически вопросы о СУБД

Данный форум не предназначен для:

  • вопросов о поиске разлиных БД (если не понимаете чем БД отличается от СУБД то: а) вам не сюда; б) Google в помощь)
  • обсуждения проблем с доступом к СУБД из различных ЯП (для этого есть соответсвующие форумы по каждому ЯП)
  • обсуждения проблем с написание SQL запросов, для этого есть форум Составление SQL-запросов
  • просьб о написании курсовой, реферата и т.п., для этого есть Центр помощи или фриланс биржа
  • объявлений о найме специалистов, для этого есть раздел Объявления о найме специалистов

Если вы не соблюдаете эти правила, не удивляйтесь потом не найдя свою тему/сообщение. ;)


Полезные советы:

При написании сообщения постарайтесь дать теме максимально понятное название. В теме максимально подробно опишите проблему. Если применимо укажите: название базы данных и версии (MySQL 4.1, MS SQL Server 2000 и т.п.); используемых язык программирования; способа доступа (ADO, BDE и т.д.); сообщения об ошибках.

Для вставки кода используйте теги [code=sql] [/code].

Литературу по базам данных можно поискать здесь.

Действия модераторов можно обсудить здесь.


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, LSD, Zloxa.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | СУБД, общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1090 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.