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

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 13.05.2008, 14:26   #1  
kalex_a is offline
kalex_a
Участник
Аватар для kalex_a
Самостоятельные клиенты AX
MCBMSS
 
160 / 19 (1) ++
Регистрация: 26.11.2007
Адрес: Казахстан
! Проблема с SQL запросом
Здравствуйте, подскажите пожалуйста, как сделать такой запрос в Axapta? и что использовать лучьше View(это было бы лучьше), Query или какой нибудь while select, просто не знаю, знаю как простые запросы делать, а такие .
Код:
select VTr.AccountNum,VT.Name,VTr.RContractAccount,RT.RContractCode from VendTRans VTr
 join vendtable VT on VT.AccountNum=VTr.AccountNum and VT.DataAreaId='dat'
 left join RContractTable RT on RT.RContractAccount=VTr.RContractAccount and RT.DataAreaId='dat'
where VTr.DataAreaId='dat' and VTr.closed=''
group by VTr.AccountNum, VT.Name, VTr.RContractAccount,RT.RContractCode  having count(*)>1 and sum(VTr.AmountCur)=0
order by RT.RContractCode
Старый 13.05.2008, 14:37   #2  
Alexius is offline
Alexius
Участник
Аватар для Alexius
 
461 / 248 (9) ++++++
Регистрация: 13.12.2001
По "хорошему" никак
Старый 13.05.2008, 15:35   #3  
Владимир Максимов is offline
Владимир Максимов
Участник
КОРУС Консалтинг
 
1,701 / 1195 (43) ++++++++
Регистрация: 13.01.2004
Записей в блоге: 3
Насколько я знаю, опция HAVING в Axapta не поддерживается. Ни в каком виде. Единственное "штатное" решение - это сканировать всю выборку и вручную исключать записи удовлетворяющие условию HAVING.

Хотя, условие count(*)>1 еще можно попробовать решить через NOT EXISTS, но условие sum(VTr.AmountCur)=0 в общем случае без HAVING не решается...
Старый 13.05.2008, 15:50   #4  
NNB is offline
NNB
Участник
 
103 / 12 (1) ++
Регистрация: 31.08.2006
Первое - уберите проверку на компанию - Axapta сама сообразит
Второе - используйте while select c группировкой и при поиощи if проверяйте ограничения.
При этом можно заполнять какую-либо таблицу
Старый 13.05.2008, 15:51   #5  
kalex_a is offline
kalex_a
Участник
Аватар для kalex_a
Самостоятельные клиенты AX
MCBMSS
 
160 / 19 (1) ++
Регистрация: 26.11.2007
Адрес: Казахстан
Я еще вот накопал типа прямого обращения базе, но выдает такую ошибку.
Ошибка:
Сбой запроса на разрешение типа "SqlStatementExecutePermission".
(S)\Classes\SqlStatementExecutePermission\demand
(S)\Classes\Statement\executeQuery

вот код, мож кто знает в чем проблема

X++:
static void Job_VendReport()
{
    UserConnection  con = new UserConnection();
    Statement       stmt = con.createStatement();
    ResultSet  resultSet;
    SqlStatementExecutePermission ssep;
    str sqlExpression ="select VTr.AccountNum,VT.Name,VTr.RContractAccount,RT.RContractCode from VendTrans VTr "+
    "join vendtable VT on VT.AccountNum=VTr.AccountNum and VT.DataAreaId='dat' "+
    "left join RContractTable RT on RT.RContractAccount=VTr.RContractAccount and RT.DataAreaId='dat' "+
    "where VTr.DataAreaId='dat' and VTr.closed=''"+
    "group by VTr.AccountNum, VT.Name, VTr.RContractAccount,RT.RContractCode  having count(*)>1 and sum(VTr.AmountCur)=0 "+
    "order by RT.RContractCode";
    ;
    ssep =  new SqlStatementExecutePermission(sqlExpression);
    ssep.assert();
    resultSet = stmt.executeQuery(sqlExpression);
    while (resultSet.next())
    {
        info(resultSet.getString(1));
    }
}
Старый 13.05.2008, 15:56   #6  
kalex_a is offline
kalex_a
Участник
Аватар для kalex_a
Самостоятельные клиенты AX
MCBMSS
 
160 / 19 (1) ++
Регистрация: 26.11.2007
Адрес: Казахстан
Цитата:
Сообщение от NNB Посмотреть сообщение
Первое - уберите проверку на компанию - Axapta сама сообразит
Второе - используйте while select c группировкой и при поиощи if проверяйте ограничения.
При этом можно заполнять какую-либо таблицу
Я в аксапте новичек не подскажете как через while select правильно написать? А то у меня постоянно "Синтаксическая ошибка", хотя кажется что правильно написал.
Старый 13.05.2008, 16:22   #7  
_scorp_ is offline
_scorp_
Участник
Аватар для _scorp_
MCBMSS
 
488 / 369 (13) ++++++
Регистрация: 25.07.2007
Адрес: Москва
X++:
static void Job_VendReport()
{
    UserConnection  con = new UserConnection();
    Statement       stmt = con.createStatement();
    ResultSet  resultSet;
    SqlStatementExecutePermission ssep;
    InteropPermission                   comPermission;
    str sqlExpression ="select VTr.AccountNum,VT.Name,VTr.RContractAccount,RT.RContractCode from VendTrans VTr "+
    "join vendtable VT on VT.AccountNum=VTr.AccountNum and VT.DataAreaId='dat' "+
    "left join RContractTable RT on RT.RContractAccount=VTr.RContractAccount and RT.DataAreaId='dat' "+
    "where VTr.DataAreaId='dat' and VTr.closed=''"+
    "group by VTr.AccountNum, VT.Name, VTr.RContractAccount,RT.RContractCode  having count(*)>1 and sum(VTr.AmountCur)=0 "+
    "order by RT.RContractCode";
    ;
    comPermission = new InteropPermission(InteropKind::ComInterop);
    comPermission.assert();
    ssep =  new SqlStatementExecutePermission(sqlExpression);
    ssep.assert();
    resultSet = stmt.executeQuery(sqlExpression);
    while (resultSet.next())
    {
        info(resultSet.getString(1));
    }
    CodeAccessPermission::revertAssert();
}

Последний раз редактировалось _scorp_; 13.05.2008 в 16:28.
Старый 13.05.2008, 16:22   #8  
Alexius is offline
Alexius
Участник
Аватар для Alexius
 
461 / 248 (9) ++++++
Регистрация: 13.12.2001
Попробуйте через класс Connection для соединения с текущей БД Аксапты:
X++:
static void Job_VendReport()
{
    Connection  con = new Connection();
    Statement       stmt = con.createStatement();
    ResultSet  resultSet;
    str sqlExpression ="select VTr.AccountNum,VT.Name,VTr.RContractAccount,RT.RContractCode from VendTrans VTr "+
    "join vendtable VT on VT.AccountNum=VTr.AccountNum and VT.DataAreaId='dat' "+
    "left join RContractTable RT on RT.RContractAccount=VTr.RContractAccount and RT.DataAreaId='dat' "+
    "where VTr.DataAreaId='dat' and VTr.closed=''"+
    "group by VTr.AccountNum, VT.Name, VTr.RContractAccount,RT.RContractCode  having count(*)>1 and sum(VTr.AmountCur)=0 "+
    "order by RT.RContractCode";
    ;
    resultSet = stmt.executeQuery(sqlExpression);
    while (resultSet.next())
    {
        info(resultSet.getString(1));
    }
}
За это сообщение автора поблагодарили: Poleax (1).
Старый 13.05.2008, 16:47   #9  
NNB is offline
NNB
Участник
 
103 / 12 (1) ++
Регистрация: 31.08.2006
Что-нибудь в таком духе. Писал впопыхах. Не перечитывал. Мог ошибиться.

while select AccountNum, RContractAccount, Sum(AmountCur) from VTr group by RContractAccount
join Name from VT group by AccountNum, Name where VT.AccountNum==VTr.AccountNum
join RContractCode from RT group by RContractCode where RT.RContractAccount==VTr.RContractAccount &&
VTr.closed {
// if .....
//Что-нибудь insert()
}
Старый 30.03.2010, 14:09   #10  
E S V is offline
E S V
Участник
 
5 / 12 (1) ++
Регистрация: 24.01.2005
Адрес: Санкт-Петербург
stmt.executeQuery(sqlExpression) должно выполняться на сервере
Старый 30.03.2010, 17:21   #11  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от _scorp_ Посмотреть сообщение
X++:
    UserConnection  con = new UserConnection();
...
        info(resultSet.getString(1));
...
Цитата:
Сообщение от Alexius Посмотреть сообщение
Попробуйте через класс Connection для соединения с текущей БД Аксапты:
Зачем же вы человеку плохое советуете?

Цитата:
Сообщение от kalex_a Посмотреть сообщение
Здравствуйте, подскажите пожалуйста, как сделать такой запрос в Axapta? и что использовать лучьше View(это было бы лучьше), Query или какой нибудь while select, просто не знаю, знаю как простые запросы делать, а такие .
Во-первых, обязательно прочитайте про режимы кэширования таблиц в Аксапте. Из-за режимов кэширования в Аксапте зачастую писать вложенные И простые запросы выгоднее, нежели один навороченный супер-запрос с кучей join'ов.
Во-вторых, среди программистов Аксапты не принято сокращать суффиксы (типа Trans) и префиксы (типа Vend). Прочитайте хелп для разработчика на предмет соглашений по наименованию объектов.
В-третьих, обязательно старайтесь вникнуть в предметную область. Сумма по полю AmountCur не имеет никакого смысла, если вы не накладываете никаких условий на валюту. Сейчас ваш запрос суммирует рубли, тугрики, евро, доллары и т.д. и проверяет получившуюся сумму на ноль. Смысла в этом условии - никакого.
В-четвертых, обязательно поймите смысл полей. Так вы делаете выборку с условием (VTr.closed='') и добавляете having sum(VTr.AmountCur)=0. Вообще говоря, в Аксапте если проводка НЕ закрыта, то ее надо анализировать и выбирать даже если сумма оплат с накладными равна 0. Такова логика Аксапты. Система перестает анализировать только Закрытые записи. Ваш Having одним махом нарушает логику работы стандартного функционала и превращает Аксапту в 1С
В-пятых, поймите соотношение таблиц и данных в таблицах. Так в Аксапте для открытых проводок по клиентам и поставщикам есть специальная таблица VendTransOpen, которая содержит только открытую часть из проводок по поставщикам.
В-шестых, таблица RContractTable содержит договоры как с поставщиками, так и с клиентами. Поэтому делать выборку из RContractTable без указания типа нельзя!!! Ведь у вас могут быть клиенты и поставщики с одинаковым кодом.
В-седьмых, поскольку вы не разбирались с предметной областью и с таблицами, вы указали сортировку по полю, по которому нет индекса. В итоге ваша супер-оптимизация-в-один-запрос идет лесом из-за того, что сортировка будет происходить в temp-базе данных SQL-сервера.
В-восьмых, вы неправильно связали таблицу RContractTable и VendTrans - не все поля указали.
В-девятых, поле Closed имеет тип Date

Поэтому правильный ответ на ваш вопрос - ни в коем случае не нужно делать так как вы хотите. Если вам дал задачу некий "постановщик" или "аналитик" - четвертуйте его. Можно медленно.

Постараемся понять что же вы делаете и сформулировать на нормальном человеческом языке:
Вы хотите получить список различных поставщиков (код, наименование) с кодами их договоров по которым есть хоть какие-то незакрытые суммы (список должен быть отсортирован по коду договора)
При реверс-инжиниринге вашего запроса возникает неопределенность - каким образом вы трактуете незакрытость. Стандартная Аксапта использует только признак закрытости - этот признак устанавливается ПОСЛЕ операции сопоставления (никаких проверок сумм на ноль). Вы зачем-то вводите условие, что сумма должна быть ненулевой, что тут же отменяет функционал сопоставления (и тут же вносит кучу багов с курсовыми разницами и разными валютами). Поэтому, скорее всего, условие в Having - это неправильное условие (Я надеюсь, что вы не хотели сломать Аксапту сознательно).

Поэтому запрос, аналогичный вашему, в Аксапте должен быть таким:

X++:
static void Job11(Args _args)
{
    RContractTable  RContractTable;
    VendTrans       VendTrans;
    VendTransOpen   VendTransOpen;
    VendTable       VendTable;


    while select RContractTable
        index ContractTypeCodeAccountIdx
        where RContractTable.RContractPartnerType == RContractPartnerType::Vend
    {
        select firstonly VendTrans
            where VendTrans.RContractAccount == RContractTable.RContractAccount
        exists join VendTransOpen
            where VendTrans.AccountNum == VendTransOpen.AccountNum
               && VendTrans.RecId == VendTransOpen.RefRecId;

        if( !VendTrans )
            continue;

        VendTable = VendTable::find(RContractTable.RContractAccount);
        info(strfmt("%1 %2 %3 %4", VendTable.AccountNum, VendTable.Name,RContractTable.RContractAccount,RContractTable.RContractCode));
    }

}
Несмотря на то, что тут два вложенных запроса, этот запрос будет более оптимальным, чем ваш.

Пооптимизируем:
Если честно, то я не вижу абсолютно никакого смысла сортировать результаты выдачи по КОДУ договора. Если убрать требование сортировки по коду и в цикле договоры не нужны, то запрос может быть гораздо простым и вполне Аксаптовским:
X++:
static void Job12(Args _args)
{
    //RContractTable  RContractTable; // Раскоментируйте, если внутри цикла нужны данные из договора
    VendTrans       VendTrans;
    VendTransOpen   VendTransOpen;
    VendTable       VendTable;


    while select count(recid) from VendTrans
        group by AccountNum, RContractAccount, RContractCode
    exists join VendTransOpen
        where VendTrans.AccountNum == VendTransOpen.AccountNum
           && VendTrans.RecId == VendTransOpen.RefRecId
    {
        //RContractTable = RContractTable::find(RContractPartnerType::Vend, VendTrans.RContractCode, VendTrans.RContractAccount);
        VendTable = VendTable::find(VendTrans.AccountNum);
        info(strfmt("%1 %2 %3 %4", VendTable.AccountNum, VendTable.Name,VendTrans.RContractAccount,VendTrans.RContractCode));
    }

}
Но вообще говоря, в проводках Аксапты есть и суммы, и признак закрытости. Поэтому, если подумать и посмотреть в метод open таблицы VendTrans (а также если внутри цикла не нужны данные из VendTransOpen), то запрос становится вообще тривиальным:
X++:
static void Job14(Args _args)
{
    VendTrans       VendTrans;
    VendTable       VendTable;

    while select sum(AmountMST), sum(SettleAmountMST) from VendTrans
        group by AccountNum, RContractAccount, RContractCode
        where VendTrans.amountCur != VendTrans.settleAmountCur // условие взято из метода Open() таблицы VendTrans
    {
        VendTable = VendTable::find(VendTrans.AccountNum);
        info(strfmt("%1 %2 %3 %4", VendTable.AccountNum, VendTable.Name,VendTrans.RContractAccount,VendTrans.RContractCode));
    }

}
И тут можно подумать над тем, сравнивать ли поле Closed c datenull() или все-таки пользоваться условием на неравенство сумм из метода open()... Что будет оптимальнее, надо смотреть в базу. Более правильным, с точки зрения Аксапты, является условие из метода open().


В общем, прежде всего - изучите предметную область.
И проштудируйте про кэширование.
__________________
полезное на axForum, github, vk, coub.
За это сообщение автора поблагодарили: alex55 (1), AP-1055D (1).
Старый 30.03.2010, 17:42   #12  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от mazzy Посмотреть сообщение
Поэтому запрос, полностью аналогичный вашему, в Аксапте должен быть таким:
Хорошенько подумав, можно и этот запрос (с сортировкой по коду договора) сильно соптимизировать:
X++:
static void Job11(Args _args)
{
    VendTrans       VendTrans;
    VendTable       VendTable;

    while select sum(AmountMST), sum(SettleAmountMST) from VendTrans
        group by RContractCode, RContractAccount, AccountNum
        where VendTrans.amountCur != VendTrans.settleAmountCur
    {
        VendTable = VendTable::find(VendTrans.AccountNum);
        info(strfmt("%1 %2 %3 %4", VendTable.AccountNum, VendTable.Name,VendTrans.RContractAccount,VendTrans.RContractCode));
    }

}
Другое дело, что в VendTrans нет индекса по RContractCode...
Но это уже совсем другая история, связанная с полным отсутствием оптимизации локального функционала...

===========
и если этот запрос делается внутри транзакции, то при отсутствии индекса по RContractCode может быть стоит VendTable внести в сам запрос, а не использовать метод find... Но это уже нужно смотреть в конкретный код и в план запроса...
__________________
полезное на axForum, github, vk, coub.

Последний раз редактировалось mazzy; 30.03.2010 в 18:00. Причина: здесь уже может быть стоит сделать join с VendTable...
Старый 30.03.2010, 18:25   #13  
Wamr is offline
Wamr
----------------
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
 
1,737 / 858 (32) +++++++
Регистрация: 15.01.2002
Адрес: Москва
Записей в блоге: 7
Сергей, красиво и поучительно...
но
Во-первых, за полтора года ответ, наверное, уже нашли
Во-вторых, нужны были поставщики\договора, по которым есть открытые проводки, но сумма по которым = 0
Старый 30.03.2010, 19:04   #14  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Да... Дату заметил только у последнего сообщения... Извините.

Цитата:
Сообщение от Wamr Посмотреть сообщение
Во-первых, за полтора года ответ, наверное, уже нашли
Да, я собственно хотел сказать участникам: "перестаньте советовать прямые запросы. executeQuery - на крайний случай". Но стер...

Цитата:
Сообщение от Wamr Посмотреть сообщение
Во-вторых, нужны были поставщики\договора, по которым есть открытые проводки, но сумма по которым = 0
Открытая проводка не может быть с нулевой суммой
Единственный вариант - курсовая разница, когда (AmountCur = 0) && (AmountMST != 0)

В этом как раз и был изначальный вопрос - что считать "открытой/закрытой" проводкой
С точки зрения Аксапты - открытая проводка, то та у которой (VendTrans.amountCur != VendTrans.settleAmountCur).

Поэтому формулировка "есть открытые проводки, но сумма по которым = 0" - не совсем точна.
__________________
полезное на axForum, github, vk, coub.
Старый 31.03.2010, 08:39   #15  
Wamr is offline
Wamr
----------------
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
 
1,737 / 858 (32) +++++++
Регистрация: 15.01.2002
Адрес: Москва
Записей в блоге: 7
"сумма по которым = 0" это having(sum(AmountCur)) = 0, а не where AmountCur = 0

то есть поставщик\договор, по которому примерно такая ситуация:
накладная -500руб
оплата 300руб
оплата 200руб
Старый 31.03.2010, 11:28   #16  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от Wamr Посмотреть сообщение
"сумма по которым = 0" это having(sum(AmountCur)) = 0, а не where AmountCur = 0

то есть поставщик\договор, по которому примерно такая ситуация:
накладная -500руб
оплата 300руб
оплата 200руб
Если эти движения не сопоставлены, то, с точки зрения Аксапты, накладная не оплачена.
Аксапта будет предлагать/создавать новую оплату для этой несопоставленной накладной.
правильное условие оплаченности сформулировано в методе open таблицы VendTrans.

Повторюсь:
Цитата:
Сообщение от mazzy Посмотреть сообщение
В-третьих, обязательно старайтесь вникнуть в предметную область. Сумма по полю AmountCur не имеет никакого смысла, если вы не накладываете никаких условий на валюту. Сейчас ваш запрос суммирует рубли, тугрики, евро, доллары и т.д. и проверяет получившуюся сумму на ноль. Смысла в этом условии - никакого.

В-четвертых, обязательно поймите смысл полей. Так вы делаете выборку с условием (VTr.closed='') и добавляете having sum(VTr.AmountCur)=0. Вообще говоря, в Аксапте если проводка НЕ закрыта, то ее надо анализировать и выбирать даже если сумма оплат с накладными равна 0. Такова логика Аксапты. Система перестает анализировать только Закрытые записи.
И еще:
Цитата:
Сообщение от mazzy Посмотреть сообщение
Ваш Having одним махом нарушает логику работы стандартного функционала и превращает Аксапту в 1С
__________________
полезное на axForum, github, vk, coub.
Теги
executequery, query, sql, vendtrans, vendtransopen, запрос (query), как правильно

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Dynamics AX: Dynamics AX 2009 & SQL Server 2008 Blog bot DAX Blogs 0 10.06.2008 21:08
Dynamics AX: SQL Server, Heart of Dynamics AX Blog bot DAX Blogs 0 13.07.2007 18:00
aEremenko: Диагностика проблем при установке Microsoft Dynamics Ax 4.0 на Microsoft SQL Server 2005 Blog bot DAX Blogs 0 28.10.2006 16:01
Проблема с запросом Protey DAX: Программирование 7 10.07.2006 09:32
Помогите с SQL запросом malex DAX: Программирование 8 26.07.2005 13:43
Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

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

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

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 08:52.