PDA

Просмотр полной версии : [Вопрос] Финансовая статистика в бизнесе, подкиньте идей)



StevenH
09.06.2017, 19:09
Здравствуйте. Собираюсь сделать финансовую статистику бизнеса (показывается последняя прибыль за 10 дней), так вот, не знаю как лучше сделать "переброс" финансовой статистики каждый день в 00:00.

Вот как в базе выглядит само поле:


4141,1474,4881,4756,9427,4000,4371,3138,5818,4918


В моде, это хранится в енаме, в виде одномерного массива, обращение по такому принципу:


gBusiness[id][busiFinStat][0] // выведет соответственно 4141$
gBusiness[id][busiFinStat][1] // выведет соответственно 1474$
..
gBusiness[id][busiFinStat][9] // выведет соответственно 4918$


Так вот, в чем сам вопрос, как сделать лучше и правильно переброс цены в 00:00 (интересует сам функционал), то есть когда наступит новый день, финка должна выглядить так:


4141,1474,4881,4756,9427,4000,4371,3138,5818,4918 // До 00:00 (now day)
0,4141,1474,4881,4756,9427,4000,4371,3138,5818 // После 00:00 (new day)


- - - Добавлено - - -

В голову приходит следующая только мысль:


gBusiness[id][busiFinStat][9] = gBusiness[id][busiFinStat][8];
gBusiness[id][busiFinStat][8] = gBusiness[id][busiFinStat][7];
...
gBusiness[id][busiFinStat][2] = gBusiness[id][busiFinStat][1];
gBusiness[id][busiFinStat][0] = 0;
// Ну и потом сохранить


Но, может это как то глупо, и есть какой то другой вариант, заранее спасибо!

DeimoS
09.06.2017, 19:53
Советую ознакомиться с понятием "Нормальная форма (https://ru.wikipedia.org/wiki/%D0%9D%D0%BE%D1%80%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F_%D1%84%D0%BE%D1%80%D0%BC%D0%B0)", ибо сейчас то, как ты работаешь с базой данных, далеко от идеала.
Собственно, если переписать всё под первую нормальную форму, то все изменения можно произвести одним запросом в БД и не мучиться.

Ну а вообще люди не просто так придумали циклы :3

for(new i = 9; i != 0; i--)
gBusiness[id][busiFinStat][i] = gBusiness[id][busiFinStat][i-1];

gBusiness[id][busiFinStat][0] = 0;

StevenH
10.06.2017, 00:04
Советую ознакомиться с понятием "Нормальная форма (https://ru.wikipedia.org/wiki/%D0%9D%D0%BE%D1%80%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F_%D1%84%D0%BE%D1%80%D0%BC%D0%B0)", ибо сейчас то, как ты работаешь с базой данных, далеко от идеала.
Собственно, если переписать всё под первую нормальную форму, то все изменения можно произвести одним запросом в БД и не мучиться.

Ну а вообще люди не просто так придумали циклы :3

for(new i = 9; i != 0; i--)
gBusiness[id][busiFinStat][i] = gBusiness[id][busiFinStat][i-1];

gBusiness[id][busiFinStat][0] = 0;

Можно подробнее про 1 запрос в БД?

TheMallard
12.06.2017, 13:19
Надо создать отдельную базу в БД, назвать её допустим bizstats. Структура:


id bizid time profit
ид финстаты ид бизнеса timestamp доход


В 00:00 инсертить в таблицу финстату всех бизнесов одним запросом
Информацию получать используя ORDER BY id DESC LIMIT 10
То есть запрос на получение будет примерно таким:


SELECT * FROM bizstats WHERE bizid = '%d' ORDER BY id DESC LIMIT 10

StevenH
13.06.2017, 00:22
Надо создать отдельную базу в БД, назвать её допустим bizstats. Структура:


id bizid time profit
ид финстаты ид бизнеса timestamp доход


В 00:00 инсертить в таблицу финстату всех бизнесов одним запросом
Информацию получать используя ORDER BY id DESC LIMIT 10
То есть запрос на получение будет примерно таким:


SELECT * FROM bizstats WHERE bizid = '%d' ORDER BY id DESC LIMIT 10


Неплохая кстати идея)

Но возник вопрос, как сделать инсерт около 90 записей (столько примерно бизов), одним запросом?

DeimoS
13.06.2017, 02:17
В данном случае можно немножко "набыдлокодить" и сделать для каждого дня свой столбец. То бишь, структура будет такой:

id
bizid
day_1
day_2
day_3
day_4
day_5
day_6
day_7
Названия, естественно, для столбцов нормальные придумай. Я сделал такие для наглядности в запросе обновления.

Обновление будет таким:

UPDATE bizstats SET day_7 = day_6, day_6 = day_5, day_5 = day_4, day_4 = day_3, day_3 = day_2, day_2 = day_1, day_1 = 0
// Это весь запрос. То бишь, форматировать его и поставлять вручную значения не нужно. MySQL всё сделает за тебя
И всё. В теории, это запрос должен все значения перемешать как тебе нужно.
Только не забывай при всяком изменении счёта бизнеса обновлять значение "day_1", дабы там всегда была актуальная информация. Остальные столбцы обновлять вручную не нужно.

Именно про это я и говорил, когда упоминал один запрос. Можно, конечно, и более правильный вариант TheMallard преобразовать в один запрос, но он будет гораздо сложнее + в данном случае довольно бессмысленно делать "гибкую" структуру (как в примере TheMallard), ибо количество дней, информацию о которых мы будем хранить, вряд ли когда-то изменится.

StevenH
13.06.2017, 15:04
В данном случае можно немножко "набыдлокодить" и сделать для каждого дня свой столбец. То бишь, структура будет такой:

id
bizid
day_1
day_2
day_3
day_4
day_5
day_6
day_7
Названия, естественно, для столбцов нормальные придумай. Я сделал такие для наглядности в запросе обновления.

Обновление будет таким:

UPDATE bizstats SET day_7 = day_6, day_6 = day_5, day_5 = day_4, day_4 = day_3, day_3 = day_2, day_2 = day_1, day_1 = 0
// Это весь запрос. То бишь, форматировать его и поставлять вручную значения не нужно. MySQL всё сделает за тебя
И всё. В теории, это запрос должен все значения перемешать как тебе нужно.
Только не забывай при всяком изменении счёта бизнеса обновлять значение "day_1", дабы там всегда была актуальная информация. Остальные столбцы обновлять вручную не нужно.

Именно про это я и говорил, когда упоминал один запрос. Можно, конечно, и более правильный вариант StevenH преобразовать в один запрос, но он будет гораздо сложнее + в данном случае довольно бессмысленно делать "гибкую" структуру (как в примере StevenH), ибо количество дней, информацию о которых мы будем хранить, вряд ли когда-то изменится.

Мм, логику я понял. Но вот теперь дилемма, не могу определиться как сделать правильно, т.е. использовать вариант TheMallard (и доставать последние 10 записей нужного биза, и в 00:00 инсертить новую стату), либо использовать твой вариант, хотя по сути он тоже хорош, и да, ты прав, кол-во дней врятли когда изменится...

И да, ты ошибся, предлагал не я (StevenH), а TheMallard.. :)

DeimoS
13.06.2017, 16:30
Мм, логику я понял. Но вот теперь дилемма, не могу определиться как сделать правильно, т.е. использовать вариант TheMallard (и доставать последние 10 записей нужного биза, и в 00:00 инсертить новую стату), либо использовать твой вариант, хотя по сути он тоже хорош, и да, ты прав, кол-во дней врятли когда изменится...

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


И да, ты ошибся, предлагал не я (StevenH), а TheMallard.. :)

Точно, прошу прощения. Просто час ночи и "Ctrl + C/Ctrl + V" :)

StevenH
13.06.2017, 16:46
В твоём случае, как мне кажется, лучше мой вариант, ибо он в разы проще как в реализации, так и в работе с данными. Вариант, описанный TheMallard, больше подошёл бы для той же системы инвентаря, в которой большое количество столбцов и их количество может меняться.

Спасибо, думаю попробую сделать по твоему варианту

Но возникло два вопроса..
Пример того, что должно получится:
http://i.imgur.com/AajYNux.png

1. Как это все правильно взять из таблицы и куда записать

В голову приходит следующее:
Сделать запрос на нужный бизнес, создать 10 переменных, и сделать что то типа такого:


new profit[10];
cache_get_value_name_int(0, "day_1", profit[0]);
cache_get_value_name_int(0, "day_2", profit[1]);
...
cache_get_value_name_int(0, "day_10", profit[9]);


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

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

Например та же проверка, если сегодня 2 число нового месяца, а статистика выводится за последние 10 дней, как то узнать какой правильный месяц вывести..

DeimoS
13.06.2017, 19:15
Ну как-то так:

new taxation = 7;// Эту переменную замени на ту, где у тебя хранится значение налогообложения


#define COUNT_FINANC_STAT_DAYS 10

stock ShowFinancStatDialog(playerid, bizid)
{
static const
month_names[12][9] =
{
"января", "февраля", "марта", "апреля", "мая", "июня",
"июля", "августа", "сентября", "октября", "ноября", "декабря"
},
month_days[12] =// Для текущего года
{
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

new string[(32+2+7)+(20+11)+((6+2+sizeof(month_names[])-1+4+11)*COUNT_FINANC_STAT_DAYS)+(9+11)+1];
format(string, sizeof(string), "SELECT * FROM bizstats WHERE bizid = %d LIMIT 1", bizid);
new Cache:result = mysql_query(mysql_connection_ID, string, true);
if(!cache_num_rows())
{
cache_delete(result);
return 0;
}


string = "Статистика доходов за последние "#COUNT_FINANC_STAT_DAYS" дней:\n";//32+2+7
format(string, sizeof(string), "%sНалогообложение: %d%%\n\n", string, taxation);//20+11

new year,
month,
day,
total_buff;
getdate(year, month, day);

if(day >= COUNT_FINANC_STAT_DAYS)
{
for(new i, buff; i < COUNT_FINANC_STAT_DAYS; i++)
{
cache_get_value_index_int(0, i+2, buff);// +2 так как перед столбцами с нужными нам данными есть ещё столбец с ID строки и столбец с ID бизнеса, которые нужно пропустить
total_buff += buff;
format(string, sizeof(string), "%s%02d %s %04d\t\t%d$\n", string, day-COUNT_FINANC_STAT_DAYS+i, month_names[month-1], year, buff);//6+2+sizeof(month_names[])-1+4+11
}
}
else
{
if(month != 1)// Если месяц не январь
{
month-=1;// Просто перейдём к предыдущему месяцу
}
else// Если январь
{
month = 12;// Перейдём к декабрю
year-=1;// Отнимем один год
}

new i = 0,
d = month_days[month-1] + day-COUNT_FINANC_STAT_DAYS + 1,// Высчитаем первый день в нашем списке для предыдущего месяца по формуле "максимальное_количество_дней_в_предыдущем_месяце + (день_в_текущем_месяце-количество_дней_в_статистике)+1"
/*
То бишь, если представим, что сегодня второе июля 2017 (02.06.2017), то формула будет такой:
31+(2-10)+1
В итоге получаем: 31-8+1 (получаем 24 мая)
То бишь, вот тут "day-COUNT_FINANC_STAT_DAYS" всегда будет отрицательное число, именно поэтому перед ними стоит +, а не -
(с минусом значения наоборот прибавлялись бы, ибо минус на минус даёт плюс)
*/
last_iter = month_days[month-1]+1,// Ну а тут просто узнаём максимальное количество дней в текущем месяце (31+1 = 32)
buff;

for(; d < last_iter; i++, d++)// Условие цикла получится таким "24 < 32"
{// То бишь, выведет с 24 по 31 число
cache_get_value_index_int(0, i+2, buff);// Почему +2 - объяснял выше
total_buff += buff;// Вычисляем общую сумму
format(string, sizeof(string), "%s%02d %s %04d\t\t%d$\n", string, d, month_names[month-1], year, buff);//6+2+sizeof(month_names[])-1+4+11
}

if(month == 12)// Если в переменной хранится декабрь, значит мы брали данные для предыдущего года и
{
year++;// Возвращаем текущий год
month = 1;// Устанавливаем январь
}
else// Иначе
{
month++;// Восстанавливаем текущий месяц
}
d = 1;// Устанавливаем первый день (так как это начало месяца)
last_iter = day;// Устанавливаем текущий день (напомню, что мы в пример рассматриваем дату "02.06.2017")
for(; d <= last_iter; i++, d++)// Получается условие "1 <= 2"
{// То бишь, выведет с 1 по 2 число
cache_get_value_index_int(0, i+2, buff);
total_buff += buff;
format(string, sizeof(string), "%s%02d %s %04d\t\t%d$\n", string, d, month_names[month-1], year, buff);//6+2+sizeof(month_names[])-1+4+11
}
}
format(string, sizeof(string), "%s\nВсего: %d$", string, total_buff);// 9+11
ShowPlayerDialog(playerid, 1, DIALOG_STYLE_MSGBOX, "Доходы", string, "Ок", "");
cache_delete(result);
return 1;
}

У каждой строки для диалога указан свой размер в формуле.
Вот в этой формуле

6+2+sizeof(month_names[])-1+4+11
6 - размер самой строки (то бишь, пробелы между значениями + спецсимволы "\t" и "\n" + $)
2 - место для дня
sizeof(month_names[]) - и так понятно
4 - место для года
11 - место для прибыли

В остальных формулах принцип тот же: сначала указан размер строки без вставляемых значений и без учёта спецификаторов, а потом уже размер вставляемых значений.

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

StevenH
13.06.2017, 20:09
Ну как-то так:

new taxation = 7;// Эту переменную замени на ту, где у тебя хранится значение налогообложения


#define COUNT_FINANC_STAT_DAYS 10

stock ShowFinancStatDialog(playerid, bizid)
{
static const
month_names[12][9] =
{
"января", "февраля", "марта", "апреля", "мая", "июня",
"июля", "августа", "сентября", "октября", "ноября", "декабря"
},
month_days[12] =// Для текущего года
{
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

new string[(32+2+7)+(20+11)+((6+2+sizeof(month_names[])-1+4+11)*COUNT_FINANC_STAT_DAYS)+(9+11)+1];
format(string, sizeof(string), "SELECT * FROM bizstats WHERE bizid = %d LIMIT 1", bizid);
new Cache:result = mysql_query(mysql_connection_ID, string, true);
if(!cache_num_rows())
{
cache_delete(result);
return 0;
}


string = "Статистика доходов за последние "#COUNT_FINANC_STAT_DAYS" дней:\n";//32+2+7
format(string, sizeof(string), "%sНалогообложение: %d%%\n\n", string, taxation);//20+11

new year,
month,
day,
total_buff;
getdate(year, month, day);

if(day >= COUNT_FINANC_STAT_DAYS)
{
for(new i, buff; i < COUNT_FINANC_STAT_DAYS; i++)
{
cache_get_value_index_int(0, i+2, buff);// +2 так как перед столбцами с нужными нам данными есть ещё столбец с ID строки и столбец с ID бизнеса, которые нужно пропустить
total_buff += buff;
format(string, sizeof(string), "%s%02d %s %04d\t\t%d$\n", string, day-COUNT_FINANC_STAT_DAYS+i, month_names[month-1], year, buff);//6+2+sizeof(month_names[])-1+4+11
}
}
else
{
if(month != 1)// Если месяц не январь
{
month-=1;// Просто перейдём к предыдущему месяцу
}
else// Если январь
{
month = 12;// Перейдём к декабрю
year-=1;// Отнимем один год
}

new i = 0,
d = month_days[month-1] + day-COUNT_FINANC_STAT_DAYS + 1,// Высчитаем первый день в нашем списке для предыдущего месяца по формуле "максимальное_количество_дней_в_предыдущем_месяце + (день_в_текущем_месяце-количество_дней_в_статистике)+1"
/*
То бишь, если представим, что сегодня второе июля 2017 (02.06.2017), то формула будет такой:
31+(2-10)+1
В итоге получаем: 31-8+1 (получаем 24 мая)
То бишь, вот тут "day-COUNT_FINANC_STAT_DAYS" всегда будет отрицательное число, именно поэтому перед ними стоит +, а не -
(с минусом значения наоборот прибавлялись бы, ибо минус на минус даёт плюс)
*/
last_iter = month_days[month-1]+1,// Ну а тут просто узнаём максимальное количество дней в текущем месяце (31+1 = 32)
buff;

for(; d < last_iter; i++, d++)// Условие цикла получится таким "24 < 32"
{// То бишь, выведет с 24 по 31 число
cache_get_value_index_int(0, i+2, buff);// Почему +2 - объяснял выше
total_buff += buff;// Вычисляем общую сумму
format(string, sizeof(string), "%s%02d %s %04d\t\t%d$\n", string, d, month_names[month-1], year, buff);//6+2+sizeof(month_names[])-1+4+11
}

if(month == 12)// Если в переменной хранится декабрь, значит мы брали данные для предыдущего года и
{
year++;// Возвращаем текущий год
month = 1;// Устанавливаем январь
}
else// Иначе
{
month++;// Восстанавливаем текущий месяц
}
d = 1;// Устанавливаем первый день (так как это начало месяца)
last_iter = day;// Устанавливаем текущий день (напомню, что мы в пример рассматриваем дату "02.06.2017")
for(; d <= last_iter; i++, d++)// Получается условие "1 <= 2"
{// То бишь, выведет с 1 по 2 число
cache_get_value_index_int(0, i+2, buff);
total_buff += buff;
format(string, sizeof(string), "%s%02d %s %04d\t\t%d$\n", string, d, month_names[month-1], year, buff);//6+2+sizeof(month_names[])-1+4+11
}
}
format(string, sizeof(string), "%s\nВсего: %d$", string, total_buff);// 9+11
ShowPlayerDialog(playerid, 1, DIALOG_STYLE_MSGBOX, "Доходы", string, "Ок", "");
cache_delete(result);
return 1;
}

У каждой строки для диалога указан свой размер в формуле.
Вот в этой формуле

6+2+sizeof(month_names[])-1+4+11
6 - размер самой строки (то бишь, пробелы между значениями + спецсимволы "\t" и "\n" + $)
2 - место для дня
sizeof(month_names[]) - и так понятно
4 - место для года
11 - место для прибыли

В остальных формулах принцип тот же: сначала указан размер строки без вставляемых значений и без учёта спецификаторов, а потом уже размер вставляемых значений.

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

Ого, не ожидал что ты будешь заморачиваться над этим, огромное спасибо! :thank_you:

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



if(!cache_num_rows())
{
cache_delete(result);
return 0;
}



Тему пока не закрывай, я в конечном итоге попробую и отпишусь что получ

StevenH
13.06.2017, 20:46
В принципе почти то что и надо.. Можно как то день в месяце +1 сделать. То есть, в фин. статистике должен последний день быть сегодняшний (т.е. в нашем случае 13 июня 2017), а в таблице сейчас последний день получится 12 июня 2017

DeimoS
13.06.2017, 21:40
format(string, sizeof(string), "%s%02d %s %04d\t\t%d$\n", string, day-COUNT_FINANC_STAT_DAYS+i, month_names[month-1], year, buff);//6+2+sizeof(month_names[])-1+4+11
на

format(string, sizeof(string), "%s%02d %s %04d\t\t%d$\n", string, day-COUNT_FINANC_STAT_DAYS+i+1, month_names[month-1], year, buff);//6+2+sizeof(month_names[])-1+4+11
У тебя же значения в БД, фактически, не привязаны к конкретной дате, так что всё форматирование идёт на уровне сервера.

Можно выделить в таблице 1 столбец под дату, в котором будет хранится дата последнего изменения значений. И уже от неё плясать, когда будешь обновлять данные (то бишь, если ты обновил данные и потом на 3 дня выключил сервер, в текущем состоянии система перенесёт значения прошлых дней на эти 3 неактивных дня. Но можно при включении сервера проверять дату в столбце и если окажется, что 3 дня сервер был выключен, то, соответственно, 3 столбца нужно отчистить).