Умная выборка(4таблицы) + суммирование

Lord Max

Guest
Умная выборка(4таблицы) + суммирование

Она не такая уж и умная, если знать как ..
Есть таблицы :

USERS(uid,slot1,slot..N) – пользователи (N=10)
ITEMS(i_id,i_name,uid) – список всех предметов
ITEM_BONUSES(i_id,b_id,value) – список бонусов к этим предметам
BONUSES(b_id,b_name) – справочная таблица названий бонусов

В полях SLOT хранятся ключи i_id, SLOT может быть null
Нужно чтобы для каждой вещи, если она имеется в SLOT выводилась 1 строка с i_name + все бонусы которые есть, то есть поля должны быть b_name => ‘value’, а не b_name => ‘имя бонуса’ и value => ‘его значение’ Возможно ли такое ? или я опять что-то не так понял ? :(

PS Нужно для того, чтобы посчитать сумму всех бонусов в отдельности, то есть по каждому бонусу. Может быть можно сделать суммирование средствами sql а не выборкой + цикл.
 

alexhemp

Новичок
Lord Max

Вот у тебя и вылезли проблемы неверной организации БД - по мотивам предыдущего топика

Нормализуй отношение USERS
Разбей на 2 таблицы

USERS (uid, name....)
SLOTS (uid, N, i_id) - и храни ОДНУ СТРОКУ НА ОДИН СЛОТ ОДНОГО ЮЗЕРА.
Не бойся большого количества строк в базе - бойся вот таких схем как ты придумал с самого начала, ПРОНУМЕРОВАВ ИМЕНА полей, а не ЗАПИСИ.

Потом продолжим :)
 

Lord Max

Guest
alexhemp
1
ааа, дошло, избыточность в USERS у меня там из-за того, что поля slot_n может быть пустым? недостаток гибкости в том, что у разных пользователей не может быть разное количество слотов. :)
2
SLOTS (uid, N, i_id) - а если у меня освободится слот номер N то при такой схеме придётся удалить строку или записать в i_id null ??
3
Кстати у меня в инвентаре помимо SLOT1...N (то есть "рюкзака") есть ещё и поля head, body и другие места куда можно "положить" предмет, их тоже поместить в SLOTS ?
4
А если потребуется вставить новое свойство предмета, например ВЕС, но у меня уже создано 10,100,1000 предметов без этого свойства, то придется добвлять к каждому из этих предметов новое свойство `вручную` ???
5
запрос :
SELECT i.item_name,b.bonus_name,ib.value FROM
items AS i
JOIN i_bonuses AS ib ON i.item_id=ib.item_id
JOIN bonuses AS b ON ib.bonus_id=b.bonus_id
WHERE i.item_id=1
выводит мне 3 поля и количество строк равное количеству бонусов, но как посчитать в php value по группам я не знаю, и это только для 1 предмета.

PS Моя проблема ещё и в том, что я пока что не могу провернуть систему выборки данных (все пересечения по условию, join`ы и так далее) в уме, но надеюсь, что с Вашей помощью + чтением мануалов я избавлюсь от этого недуга :)
 

alexhemp

Новичок
1. избыточность - это не главное. Главное - отсутствие нормализации, и как следствие невозможность делать нормалные запросы.

2. Это уж как тебе удобнее обрабатывать будет. Я бы просто удалил.

3. Конечно, это спец. слоты. Я бы их тоже пронумеровал в общем порядке. А потом уже в программе бы сопоставлял.

4. У каждого предмета же свой вес? Так какая разница в куда его писать - в значение нового атрибута или в соотв. поле таблицы предметов. Значение атрибута удобнее в использовании. Я понимаю что тебя смущает - то что идентификатор атрибута - числовой, а имя поля - строка, тебе удобнее так просто. Сделай у атрибутов поле NAME туда пиши название для дальнейших ссылок например "weight".

5. Представь себе что таблицы декартово умножаются друг на друга, т.е. составляется комбинация каждой строки с каждой. Затем условия сокращают количество строк, джойны - те-же условия, только они записаны по другому, для удобства. Они выбирают те строки значения в которых в разных таблицах равны.
Группировка берет итоговую таблицу и выполняет заданную операцию над всеми строками у которых заданный атрибут одинаков - сокращает количество строк.

Могу посоветовать лишь экспериментировать.
 

Lord Max

Guest
alexhemp
Постоянно экспериментирую, во многом продвинулся, относительно того, что было неделю назад )).
2
я бы тоже хотел просто удалить, но вот смотрите : если есть SLOT1 SLOT2 и SLOT3 в них i_id - есть, но вот персонаж освобождает SLOT2 - система его удаляет, затем персонаж снимает предмет(нужно «положить» его в 1й попавшийся пустой SLOT), как найти первое попавшееся значение IS NULL(если обнулять а не удалять) я понимаю , но вот каким образом заставить сервер "понять" что первая "дырка" между SLOT1 и SLOT3 и создать SLOT2 ?
4
Насчёт веса(да и насчёт любого свойства предмета), у меня – то отношения стоят 1к1 1 предмет – 1 свойство, уникальные для каждого предмета они потому что, над любым предметом можно произвести ряд действий, скажем отрезать часть ствола и он станет легче… хотя стоп я же могу создать все шаблоны для любых изменений предметов и использовать их, если пользователь изменит предмет, я более-менее понял, поправьте меня, если в чём-то ошибся.
Если обобщить всё выше сказанное по предметам, то мне нужно хранить все схожие свойства в таблице с отношением 1 свойство ко многим предметам, а уникальные, такие как текущая прочность предмета или количество «убитых» им бойцов в отдельной таблице с отношением 1 к 1, я правильно понял ? :)

-~{}~ 30.11.05 10:17:

кстати есть ли способ тут :
-----------------------
SELECT i.item_name,b.bonus_name,ib.value FROM
items AS i
JOIN i_bonuses AS ib ON i.item_id=ib.item_id
JOIN bonuses AS b ON ib.bonus_id=b.bonus_id
WHERE i.item_id=1
-----------------------
чтобы ib.value выводилось как b.bonus_name то есть AS 'b.bonus_name'
иначе говоря итоговая таблица вида :
_________________________________
имя предм.|имя бонуса1|имя бонуса2.......
-----------------------------------------------------
ствол |а тут не имя а value этого бонуса|......
-----------------------------------------------------------------
может всякая логика и отсутсвует, но было бы удобнее обрабатывать

-~{}~ 01.12.05 18:31:

alexhemp

Структура нынешних отношений :

USERS (user_id,user_name)
ATTRIBS(atr_id, atr_name, atr_def_value)
USER_ATRIBS(user_id, atr_id, atr_value)
ITEMS(item_id, user_id, item_name_id, type_id)
ITEM_NAMES(item_name_id, item_name) – таблица имён предметов
TYPES(type_id, type) – типы предмета (оружие, бронь и тд.)
ITEM_PROPERTIES(item_id, prop_id, value) атрибуты предметов
PROPERTIES(prop_id, prop_name) – названия атрибутов для предметов
ITEM_BONUSES(item_id, bonus_id, value) – бонусы предметов
BONUSES(bonus_id, bonus_name) названия бонусов
SLOTS(user_id, slot_id, item_id) здесь slotnum5…b далее – голова туловище и тд а slotnum1…5 - slot1 slot2 …
SLOT_NAMES(slot_id, slot_name)


Запрос (выводит все бонусы)
SELECT b.bonus_name AS BN,SUM(ib.value) AS TB FROM
i_bonuses AS ib
JOIN bonuses AS b ON ib.bonus_id=b.bonus_id
JOIN slots AS s ON s.user_id=2 //(или любой другой)
WHERE ib.item_id=s.item_id
GROUP BY b.bonus_name
HAVING min(s.slotNum) > 6

Вопросы :
1
обязателен ли суррогатный(id) примори в ITEM_PROPERTIES и ITEM_BONUSES и SLOTS ???
2
Есть ли на Ваш взгляд ошибки в запросе (с точки зрения производительности, так-то он всё рисует :) ) или всё нормально и можно придерживаться этого примера ?
3
Всё ли ок с отношениями ?
PS если у Вас есть время ответьте плиз.
 

alexhemp

Новичок
Lord Max

Если у тебя слоты пронумерованные, тогда проще всего использовать понятие "шаблон описания пользователя" где указать какие вообще слоты возможны. Я бы хранил специального юзера с ID=0 (да-да именно 0 :)) и использовал эту запись как шаблон при создании новых. Насчет слотов - авнивая с шаблоном (на самом деле делаешь LEFT JOIN с шаблоном) - поймешь какой слот пуст. Номера слотов можешь хранить в SLOT_NAMES - собственно чем "1" или "5" не имя? Особо ничем не отличается от "Голова" или "Левая рука". :)

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

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

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

Lord Max

Guest
alexhemp
у нас прям диалог, а не топик форума :)
Возникли непредвиденные трудности при заполнении шаблона : дело в том что атрибуты персонажа у меня разношёрстные, будь то боевой уровень или дата последнего входа, но большая часть всё таки целочисленные, как мне быть ? создавать много таблиц атрибутов пользователей не охота, а хранить всё в varchar`e как-то странно и наводит на мысль, что не правильно. :(
 

alexhemp

Новичок
Lord Max

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

Есть вариант - хранить все значения в связанных таблицах
типа value_type, value_id

Если value_type = 0 то это int, и value_id - содержит значение
если value_type = 1 то это строка, value_id содержит id записи в таблице values_string

Ну если другие типы будут их тоже вынести, они кстати могут быть составными как ты понимаешь.

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

Или еще что-нить в этом духе...
 

Lord Max

Guest
мама-не-горюй сколько таблиц получается ... ну да ладно , спасибо за совет - уже сделано.
только вот одно в толк не возьму что есть разного между словами "пользователь" и "персонаж" ??? аааа то есть Вы имеете в виду как какой-нить Syndicate или S.W.A.T.? нет у меня ролевая игра, 2х и более не будет точно, это тогда уже будет другая игра.
если дата не атрибут, то что ? :) у меня есть понятие данные персонажа - это всё что с ним связанно, и я по Вашему совету вынес их в отдельные таблицы или вернуть назад? Мне лично до сих пор не понятно чем пароль персонажа отличается от его боевого уровня или даты последнего входа или времени последнего экшена, кроме частоты обновления? это же всё данные персонажа - его свойства атрибуты и тд.
 
Сверху