AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: Программирование
DAX
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 18.02.2009, 15:04   #1  
Arahnid is offline
Arahnid
Участник
 
880 / 60 (4) ++++
Регистрация: 09.08.2005
Адрес: Moscow
Query - getNo(n) - не видит данных
Пишу запрос.
В нем создаю много источников.
Например, поставщик, договора, проводки.
Проблема в том, что он видит getNo(3) данные, а вот getNo(2), getNo(1) - не видит.

Т.е. он по какой - то причине теряет данные. Не могу понять в чем дело. Ниже примерная идея кода.

X++:
    QueryBuildDataSource    qbdsTable  = query.addDataSource(tableNum());
    QueryBuildDataSource    qbds2Trans = qbdsTable.addDataSource(tableNum()); 
.
.
.
            qrunVend = new QueryRun(qryVend);
            while (qrunVend.next())
            {
                _VendQ = qrunVend.getno(1);
                vendCode =  _VendQ.AccountNum;
                _ContrQ = qrunVend.getno(2);

..
}
Старый 18.02.2009, 15:07   #2  
lev is offline
lev
Ищущий знания...
Аватар для lev
Oracle
MCBMSS
Axapta Retail User
 
1,723 / 491 (20) +++++++
Регистрация: 18.01.2005
Адрес: Москва
а не проще использовать get ? наприме: _VendQ = qrunVend.get(tablennum(VendTable));

и можно посмотреть формирование самого запроса?
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с)
С Уважением,
Елизаров Артем
Старый 18.02.2009, 15:15   #3  
DSPIC is offline
DSPIC
Боец
 
1,077 / 1234 (44) ++++++++
Регистрация: 11.04.2008
ой, а что это такое ?
X++:
tableNum()
Оно компилится\работает?!
Старый 18.02.2009, 15:21   #4  
Arahnid is offline
Arahnid
Участник
 
880 / 60 (4) ++++
Регистрация: 09.08.2005
Адрес: Moscow
В оригинале порядок следующий: Отбор поставщика - Отбор договора - Отбор проводок по поставщику - Отбор в ledgerTrans. На ledgerTrans ставлю суммирование.
qbdsLT.orderMode(OrderMode::GROUPBY);
После этого он теряет связь с DS 1, 2, 3. Как только убираю группировку, то все работает.
Как же этого избежать?
Старый 18.02.2009, 15:29   #5  
Sada is offline
Sada
Программатор
Аватар для Sada
 
1,450 / 153 (8) ++++++
Регистрация: 29.03.2005
Адрес: Толи Барнаул, толи Москва
Цитата:
Сообщение от Arahnid Посмотреть сообщение
В оригинале порядок следующий: Отбор поставщика - Отбор договора - Отбор проводок по поставщику - Отбор в ledgerTrans. На ledgerTrans ставлю суммирование.
qbdsLT.orderMode(OrderMode::GROUPBY);
После этого он теряет связь с DS 1, 2, 3. Как только убираю группировку, то все работает.
Как же этого избежать?
Группировать по тем полям, которые вам нужны во всех таблах
Старый 18.02.2009, 15:33   #6  
ZVV is offline
ZVV
MCITP
MCP
Oracle
MCBMSS
 
1,006 / 246 (11) ++++++
Регистрация: 13.02.2004
Адрес: Минск
->
Цитата:
Сообщение от Arahnid Посмотреть сообщение
В оригинале порядок следующий: Отбор поставщика - Отбор договора - Отбор проводок по поставщику - Отбор в ledgerTrans. На ledgerTrans ставлю суммирование.
qbdsLT.orderMode(OrderMode::GROUPBY);
После этого он теряет связь с DS 1, 2, 3. Как только убираю группировку, то все работает.
Как же этого избежать?
Напишите такой запрос через while select и посмотрите что у вас будет в полях таблиц.
У вас будут заполнены только те поля по которым вы группируете и суммируете, всё остальное не заполнено.

Так что квери то работает, просто у вас в ваших остальных таблицах все поля пустые - скорее всего вам нужно добавлять необходимые поля в группировку, либо перечитывать их внутри цикла.
__________________
Zhirenkov Vitaly
Старый 18.02.2009, 17:49   #7  
sukhanchik is offline
sukhanchik
Administrator
Аватар для sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,277 / 3476 (122) ++++++++++
Регистрация: 13.06.2004
Адрес: Москва
Все гораздо проще.
Есть у датасорса в квери (объект QueryBuildDataSource) свойство fetchMode.
По умолчанию оно имеет значение QueryFetchMode::One2Many. Но его можно поставить в значение QueryFetchMode::One2One.

Смысл следующий.
Допустим у нас есть запрос из двух таблиц - шапки и строк. Если fetchMode = QueryFetchMode::One2Many, то getNo(шапки) возвратит курсор шапки только один (первый) раз для всех строк данной шапки.
Т.е. это будет выглядеть так:
X++:
while (queryRun.next())
{
      = queryRun.getNo(1);
     do
     {
           = queryRun.getNo(2);
          .................... // код
     }
     while (queryRun.next());
}
Если же поставить fetchMode в значение QueryFetchMode::One2One (или в 0 что тоже самое) - то Вы получите тот эффект, который ожидаете получить.
Т.е. при каждом вызове queryRun.next() оба курсора будут заполнены данными.

Я не знаю как это связано с производительностью и зачем такая логика. Но она (логика) заложена именно такая.
__________________
Возможно сделать все. Вопрос времени

Последний раз редактировалось sukhanchik; 18.02.2009 в 18:11.
За это сообщение автора поблагодарили: kashperuk (2), Logger (4), pbcio (0), AP-1055D (1).
Старый 18.02.2009, 18:08   #8  
ice is offline
ice
Участник
Аватар для ice
Лучший по профессии 2014
 
1,689 / 405 (17) +++++++
Регистрация: 23.03.2006
2 sukhanchik:
уж больно странная у вас в коде конструкция...
За это сообщение автора поблагодарили: sukhanchik (1).
Старый 18.02.2009, 18:12   #9  
sukhanchik is offline
sukhanchik
Administrator
Аватар для sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,277 / 3476 (122) ++++++++++
Регистрация: 13.06.2004
Адрес: Москва
Спасибо, не заметил - подправил исходное сообщение
__________________
Возможно сделать все. Вопрос времени
Старый 18.02.2009, 18:24   #10  
ZVV is offline
ZVV
MCITP
MCP
Oracle
MCBMSS
 
1,006 / 246 (11) ++++++
Регистрация: 13.02.2004
Адрес: Минск
->
Цитата:
Сообщение от sukhanchik Посмотреть сообщение
Все гораздо проще.
Есть у датасорса в квери (объект QueryBuildDataSource) свойство fetchMode.
По умолчанию оно имеет значение QueryFetchMode::One2Many. Но его можно поставить в значение QueryFetchMode::One2One.

Смысл следующий.
Допустим у нас есть запрос из двух таблиц - шапки и строк. Если fetchMode = QueryFetchMode::One2Many, то getNo(шапки) возвратит курсор шапки только один (первый) раз для всех строк данной шапки.
Т.е. это будет выглядеть так:
X++:
while (queryRun.next())
{
      = queryRun.getNo(1);
     do
     {
           = queryRun.getNo(2);
          .................... // код
     }
     while (queryRun.next());
}
Если же поставить fetchMode в значение QueryFetchMode::One2One (или в 0 что тоже самое) - то Вы получите тот эффект, который ожидаете получить.
Т.е. при каждом вызове queryRun.next() оба курсора будут заполнены данными.

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

Я про ваше замечание про "странный эффект":
X++:
static void ZVVTestOne2OneMany(Args _args)
{
    Query query = New Query();
    QueryRun queryRun;

    QueryBuildDataSource qbds1;
    QueryBuildDataSource qbds2;

    CustTable       custTable;
    RContractTable  contractTable
    ;

    qbds1 = query.addDataSource(tableNum(CustTable));
    qbds2 = qbds1.addDataSource(tableNum(RContractTable));
    qbds2.relations(true);
    qbds2.fetchMode(QueryFetchMode::One2Many);

    qbds2.addRange(fieldnum(RContractTable,RContractPartnerCode)).value(queryValue("4100"));

    queryRun = New QueryRun(query);

    info(qbds1.toString());

    while (queryRun.next())
    {
        custTable = queryRun.getNo(1);
        contractTable = queryRun.getNo(2);

        info(strFmt("%1 %2", custTable.AccountNum, contractTable.RContractAccount));
    }
}
X++:
SELECT * FROM CustTable JOIN * FROM RContractTable WHERE 0 = RContractTable.RContractPartnerType AND CustTable.AccountNum = RContractTable.RContractPartnerCode AND (((RContractPartnerCode = '4100')))
4100 0080
4100 560/1431
4100 0014
4100 0122
4100 0120
4100 0026
4100 0035
4100 0036
4100 1277/1431
4100 689/1441
DAX30SP3KR3

UPD уточню:
- QueryFetchMode::One2Many можно ставить и у первого и у обоих ДС - ничего не меняется
- попробовал на 40сп2 - то же самое.
Вероятно ваши данные с каких-то старых версий? Или чего в моём примере не хватает чтоб добиться вашего эффекта?
__________________
Zhirenkov Vitaly

Последний раз редактировалось ZVV; 18.02.2009 в 18:35.
Старый 18.02.2009, 23:35   #11  
sukhanchik is offline
sukhanchik
Administrator
Аватар для sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,277 / 3476 (122) ++++++++++
Регистрация: 13.06.2004
Адрес: Москва
2Logger: Хм... А Вы ведь правы. Я прочел только первый пост, где ни слова не упоминалось про группировку. Тогда можно считать мой пост оффтопиком в данной теме

А по поводу джобика... Хм... Действительно отрабатывает. Но ведь помню - сталкивался! Лично на грабли наступал. На Ax 4.0 SP2. Найду ведь! Обязательно отпишу!
__________________
Возможно сделать все. Вопрос времени
Старый 19.02.2009, 00:11   #12  
ZVV is offline
ZVV
MCITP
MCP
Oracle
MCBMSS
 
1,006 / 246 (11) ++++++
Регистрация: 13.02.2004
Адрес: Минск
->
Цитата:
Сообщение от sukhanchik Посмотреть сообщение
2Logger: Хм... А Вы ведь правы. Я прочел только первый пост, где ни слова не упоминалось про группировку. Тогда можно считать мой пост оффтопиком в данной теме

А по поводу джобика... Хм... Действительно отрабатывает. Но ведь помню - сталкивался! Лично на грабли наступал. На Ax 4.0 SP2. Найду ведь! Обязательно отпишу!
Это вы меня "Логгером" назвали?

А вообще найдите, обязательно, будет интересно...
У меня есть кое-какие мысли по этому поводу, возможно поможет: дело в том что в режиме QueryFetchMode::One2Many Axapta иногда имеет свойство разбивать сложный запрос на несколько запросов. Т.е. вместо одного запроса с inner join на БД отсылается несколько запросов - один запрос к "шапке", и соответсвующие запросы к "строкам", примерно как "delay" на форме. Несколько раз на такое нарывался, из-за этого были проблемы с производительностью. Установка QueryFetchMode::One2One ситуацию "вылечивало".
Но я на данный момент не готов сказать в каких именно случаях такое бывает, если вспомню и\или найду - отпишусь.
Так вот есть подозрение что именно в таком случае и может иметь место описанная вами выше ситуёвина, т.к. на самом деле очередной "фетч" (внутри одной "шапки") из бд вытягивает только строку из таблицы-"дитёнка" а таблица-"шапка" при этом возможно как-то чистится...
Но это только мои догадки, отвечать за свои слова пока не берусь...

Модераторам - может в отдельную ветку?
__________________
Zhirenkov Vitaly
Старый 19.02.2009, 09:31   #13  
sukhanchik is offline
sukhanchik
Administrator
Аватар для sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,277 / 3476 (122) ++++++++++
Регистрация: 13.06.2004
Адрес: Москва
Цитата:
Сообщение от ZVV Посмотреть сообщение
Это вы меня "Логгером" назвали?
Извините, совсем плохим стал. Прошу великодушнейшего прощения.

Нашел тот запрос, в котором споткнулся в свое время с fetchMode. DAX 4.0 SP2 EE. Исходный запрос упростил для отображения работы именно fetchMode. Нюанс, с которым столкнулся вчера: сие не отрабатывает, если датасорса 2. Т.е. важно, чтобы датасорсов было больше двух.

Итак:
X++:
static void vsuh_testFetchMode(Args _args)
{
    CustTrans            custTrans;
    CustTable            custTable;
    Currency             currency;
    
    Query                query;
    QueryBuildDataSource qbds1, qbds2, qbds3;
    QueryRun             qr;
    
    Counter              cnt;
    ;
    query = new Query();
    qbds1 = query.addDataSource(tablenum(CustTrans));
    qbds2 = qbds1.addDataSource(tablenum(CustTable));
//    qbds2.fetchMode(QueryFetchMode::One2One);
    qbds2.relations(true);
    qbds3 = qbds1.addDataSource(tablenum(Currency));
//    qbds3.fetchMode(QueryFetchMode::One2One);
    qbds3.relations(true);
    
    qr = new QueryRun(query);
    while (qr.next() && cnt < 10)
    {
        custTrans = qr.getNo(1);
        custTable = qr.getNo(2);
        currency  = qr.getNo(3);
        
        info(strfmt("%1-%2-%3!", custTrans.Voucher, custTable.AccountNum, currency.CurrencyCode));
        cnt++;
    }
}
В этом режиме (по умолчанию, как я уже писал выше) fetchMode = QueryFetchMode::One2Many

В таблице CustTrans ваучеры уникальные - т.е. в двух записях всегда присутствуют разные ваучеры.
Итак, вариант 1 - как в джобе (fetchMode у всех QueryFetchMode::One2Many).
Название: fetchmodeOne2Many.JPG
Просмотров: 1378

Размер: 24.2 Кб
вариант 2 - включаем fetchMode One2One у датасорса №2
Название: fetchmodeOne2OneMany1.JPG
Просмотров: 1325

Размер: 24.5 Кб
вариант 3 - включаем fetchMode One2One у датасорса №3 и fetchMode One2Many у датасорса №2
Название: fetchmodeOne2OneMany2.JPG
Просмотров: 1355

Размер: 27.5 Кб
вариант 4 - включаем fetchMode One2One у датасорсов №2 и №3
Название: fetchmodeOne2One.JPG
Просмотров: 1315

Размер: 28.8 Кб
на датасорс №1 значение свойства fetchMode влияния не оказывает.

Из примера - видно - что строки выбираются всегда - а "шапки" (в моем примере они подчинены строкам) - в зависимости от параметра fetchMode
__________________
Возможно сделать все. Вопрос времени

Последний раз редактировалось sukhanchik; 19.02.2009 в 10:12.
За это сообщение автора поблагодарили: ZVV (3).
Старый 19.02.2009, 09:52   #14  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,877 / 3127 (112) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Цитата:
Сообщение от ZVV Посмотреть сообщение
Это вы меня "Логгером" назвали?
Обзываются тут, понимаешь ли, всякими логгерами
Старый 19.02.2009, 10:42   #15  
Eldar9x is offline
Eldar9x
MCTS
Аватар для Eldar9x
Oracle
MCBMSS
 
1,064 / 166 (8) ++++++
Регистрация: 29.09.2006
Адрес: Казань
Цитата:
Допустим у нас есть запрос из двух таблиц - шапки и строк. Если fetchMode = QueryFetchMode::One2Many, то getNo(шапки) возвратит курсор шапки только один (первый) раз для всех строк данной шапки.
Вдогонку - в этом случае, можно использовать qrunVend.Changed(tablenum()), как факт того, что курсор поменялся. То есть в первоначальном варианте можно проверить, можно ли брать курсор, перед его получением: if (qrunVend.Changed(Поставщик))
Старый 19.02.2009, 11:52   #16  
ZVV is offline
ZVV
MCITP
MCP
Oracle
MCBMSS
 
1,006 / 246 (11) ++++++
Регистрация: 13.02.2004
Адрес: Минск
->
Цитата:
Сообщение от sukhanchik Посмотреть сообщение
Итак:
Да, спасибо за пример, он подтвердил высказанные мною выше подозрения:
(в вашем джобе добавил
X++:
 info (qbds1.toString());
    info (qbds2.toString());
    info (qbds3.toString());
) для наглядности. Результаты:


оба One2Many:
X++:
SELECT * FROM CustTrans
SELECT * FROM CustTable WHERE CustTrans.AccountNum = CustTable.AccountNum
SELECT * FROM Currency WHERE CustTrans.CurrencyCode = Currency.CurrencyCode
 SQL: (CustTrans) SELECT ... FROM CUSTTRANS A WHERE (SUBSTR(NLS_LOWER(DATAAREAID),1,7)=NLS_LOWER(:in1)) 
ORDER BY SUBSTR(NLS_LOWER(A.DATAAREAID),1,7),SUBSTR(NLS_LOWER(A.INVOICE),1,41),SUBSTR(NLS_LOWER(A.ACCOUNTNUM),1,41)

000003_181 -  -  !
000003_181 - 000001_045 -  !
000003_181 -  - BYR !

CustTable One2One, Currency One2Many:
X++:
SELECT * FROM CustTrans JOIN * FROM CustTable WHERE CustTrans.AccountNum = CustTable.AccountNum
SELECT * FROM CustTable WHERE CustTrans.AccountNum = CustTable.AccountNum
SELECT * FROM Currency WHERE CustTrans.CurrencyCode = Currency.CurrencyCode
 SQL: (CustTrans,CustTable) SELECT ... FROM CUSTTRANS A,CUSTTABLE B 
WHERE (SUBSTR(NLS_LOWER(A.DATAAREAID),1,7)=NLS_LOWER(:in1)) AND ((SUBSTR(NLS_LOWER(B.DATAAREAID),1,7)=NLS_LOWER(:in2)) AND (SUBSTR(NLS_LOWER(A.ACCOUNTNUM),1,41)=SUBSTR(NLS_LOWER(B.ACCOUNTNUM),1,41))) 
ORDER BY SUBSTR(NLS_LOWER(A.DATAAREAID),1,7),SUBSTR(NLS_LOWER(A.ACCOUNTNUM),1,41),A.TRANSDATE,SUBSTR(NLS_LOWER(A.VOUCHER),1,41) 

000003_181 - 000001_045 -  !
000003_181 - 000001_045 - BYR !

CustTable One2Many, Currency One2One:
X++:
SELECT * FROM CustTrans JOIN * FROM Currency WHERE CustTrans.CurrencyCode = Currency.CurrencyCode
SELECT * FROM CustTable WHERE CustTrans.AccountNum = CustTable.AccountNum
SELECT * FROM Currency WHERE CustTrans.CurrencyCode = Currency.CurrencyCode
 SQL: (CustTrans,Currency) SELECT ... FROM CUSTTRANS A,CURRENCY B 
WHERE (SUBSTR(NLS_LOWER(A.DATAAREAID),1,7)=NLS_LOWER(:in1)) AND ((SUBSTR(NLS_LOWER(B.DATAAREAID),1,7)=NLS_LOWER(:in2)) AND (SUBSTR(NLS_LOWER(A.CURRENCYCODE),1,7)=SUBSTR(NLS_LOWER(B.CURRENCYCODE),1,7))) 
ORDER BY SUBSTR(NLS_LOWER(A.DATAAREAID),1,7),SUBSTR(NLS_LOWER(A.INVOICE),1,41),SUBSTR(NLS_LOWER(A.ACCOUNTNUM),1,41)0

00003_181 -  - BYR !
000003_181 - 000001_045 - BYR !

оба One2One:
X++:
SELECT * FROM CustTrans JOIN * FROM CustTable WHERE CustTrans.AccountNum = CustTable.AccountNum JOIN * FROM Currency WHERE CustTrans.CurrencyCode = Currency.CurrencyCode
SELECT * FROM CustTable WHERE CustTrans.AccountNum = CustTable.AccountNum
SELECT * FROM Currency WHERE CustTrans.CurrencyCode = Currency.CurrencyCode
 SQL: (CustTrans,CustTable,Currency) SELECT ... FROM CUSTTRANS A,CUSTTABLE B,CURRENCY C 
WHERE (SUBSTR(NLS_LOWER(A.DATAAREAID),1,7)=NLS_LOWER(:in1)) AND ((SUBSTR(NLS_LOWER(B.DATAAREAID),1,7)=NLS_LOWER(:in2)) AND (SUBSTR(NLS_LOWER(A.ACCOUNTNUM),1,41)=SUBSTR(NLS_LOWER(B.ACCOUNTNUM),1,41))) 
AND ((SUBSTR(NLS_LOWER(C.DATAAREAID),1,7)=NLS_LOWER(:in3)) AND (SUBSTR(NLS_LOWER(A.CURRENCYCODE),1,7)=SUBSTR(NLS_LOWER(C.CURRENCYCODE),1,7))) 
ORDER BY SUBSTR(NLS_LOWER(A.DATAAREAID),1,7),SUBSTR(NLS_LOWER(A.ACCOUNTNUM),1,41),A.TRANSDATE,SUBSTR(NLS_LOWER(A.VOUCHER),1,41) 

000003_181 - 000001_045 - BYR !
Как видите, запросы к БД отсылаются в этих случаях по разному, как я говорил, вероятно Аксапта "оптимизирует" таким образом сложный запрос.
Насколько я понял, данный эффект происходит только в том случае, когда идёт непоследовательное соединение датасорсов! Только в этом случае Аксапта начинает "оптимизировать" (как в примере, естественно датасорсов при этом всегда больше двух, как вы верно подметили ) и тогда нужно либо указывать fetchMode=One2One, чтоб запрос к БД был один и тогда можно пользоваться стандартным циклом по квериРун, как обычно. Либо добавлять сложные конструкции с проверкой смены "папы" последством record.Changed(). Если же порядок соединения последовательный, то fetchMode никакого влияния не оказывает, можете сами проверить поменяв местами CustTable & CustTrans и прицепив, соответсвенно, Currency ко второму DS.

Вот! Так что будьте осторожны и учитывайте данную особенность!

ЗЫ Ещё из интересных особенностей данного поведения: Аксаптовский SQL-Trace не ловит подчинённые запросы к БД при такой оптимизации. Их можно увидеть только в трэйсе на уровне БД...
__________________
Zhirenkov Vitaly
За это сообщение автора поблагодарили: sukhanchik (7).
Теги
query, queryrun, выборка, запрос (query)

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Dynamics AX: QueryRun and Query Objects - Binding operation failed to allocate buffer space Blog bot DAX Blogs 0 03.04.2009 08:05
palleagermark: Sample union query from AX 2009 Blog bot DAX Blogs 0 11.07.2008 20:05
Dynamics AX Geek: Using query() Blog bot DAX Blogs 0 28.10.2006 16:40
Установка условия ИЛИ (OR) в Query Zeratul DAX: Программирование 3 22.06.2006 14:28
Древовидная структура источников данных в Query rumpleteazer DAX: Программирование 7 03.02.2003 15:52

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 20:27.
Powered by vBulletin® v3.8.5. Перевод: zCarot
Контактная информация, Реклама.