Всем привет,
как известно в СФ мы имеем максимальный лимит в 50к записей в одной квери. Далеко не во всех приложениях есть необходимость или реальность работы с 50к записей за раз, но бывает.
Мой случай один из самых простых: создается клон одной записи со всеми дочерними записями.
ТО есть вначале это одна запись у которой 500 дочерних, и у каждой дочерней может быть по 150 дочерних. Т.е. весь процесс клонирования падает при попытке выкверить за раз все "внучатые" записи, которых более чем 50к.
Процесс клонирования вызывается с кастомноой кнопки, которая ведет на ВФ страницу, чей контроллер выполняет клонирование и возвращает пользователю стандарный вид клонированной записи.
Вот думаю, как теперь все это оптимизировать. И перед тем как начать, решил обсудить тему с вами, так как одна голова хорошо, а много - еще лучше.
Всем привет, как известно в СФ мы имеем максимальный лимит в 50к записей в одной квери. Далеко не во всех приложениях есть необходимость или реальность работы с 50к записей за раз, но бывает. Мой случай один из самых простых: создается клон одной записи со всеми дочерними записями. ТО есть вначале это одна запись у которой 500 дочерних, и у каждой дочерней может быть по 150 дочерних. Т.е. весь процесс клонирования падает при попытке выкверить за раз все "внучатые" записи, которых более чем 50к. Процесс клонирования вызывается с кастомноой кнопки, которая ведет на ВФ страницу, чей контроллер выполняет клонирование и возвращает пользователю стандарный вид [i]клонированной[/i] записи. Вот думаю, как теперь все это оптимизировать. И перед тем как начать, решил обсудить тему с вами, так как одна голова хорошо, а много - еще лучше.
Если нужно получить более 50к записей можешь воспользоваться ReadOnly Annotation, учти только то, что в данном случае нельзя делелать DML операции.
Процесс же клонирования можно разбить на части, воспользовавшишь батчами.
Если нужно получить более 50к записей можешь воспользоваться [url=https://developer.salesforce.com/docs/atlas.en-us.200.0.apexcode.meta/apexcode/apex_classes_annotation_ReadOnly.htm]ReadOnly Annotation[/url], учти только то, что в данном случае нельзя делелать DML операции. Процесс же клонирования можно разбить на части, воспользовавшишь батчами.
Процесс же клонирования можно разбить на части, воспользовавшишь батчами.
Ну строго говоря, DML можно выполнять, но через одно место.
[quote="Gres"]Если нужно получить более 50к записей можешь воспользоваться [url=https://developer.salesforce.com/docs/atlas.en-us.200.0.apexcode.meta/apexcode/apex_classes_annotation_ReadOnly.htm]ReadOnly Annotation[/url], учти только то, что в данном случае нельзя делелать DML операции. Процесс же клонирования можно разбить на части, воспользовавшишь батчами.[/quote] Ну строго говоря, DML можно выполнять, но через одно место.
Не спорю, можно)
[quote="wilder"]Ну строго говоря, DML можно выполнять, но через одно место.[/quote] Не спорю, можно)
с выкверинными записями, но можно с клонированными? Или вообще нельзя в пределах класса\контроллера, ран контекста? и каков фактический лимит?
сейчас буду изучать батчи, думаю это единственный путь.
[quote="Gres"]нельзя делелать DML операции.[/quote] с выкверинными записями, но можно с клонированными? Или вообще нельзя в пределах класса\контроллера, ран контекста? и каков фактический лимит? сейчас буду изучать батчи, думаю это единственный путь.
да, такое нужно выводить в асинхронный контекст, ибо рано или поздно начнет вылетать "10 concurrent requests limit".
Сам форс так делает для больших операций типа переименовывания значений пиклистов - пытается обновить синхронно, понимает что их слишком много и переводит на "операция слишком большая, мы оповестим когда она закончится".
да, такое нужно выводить в асинхронный контекст, ибо рано или поздно начнет вылетать "10 concurrent requests limit". Сам форс так делает для больших операций типа переименовывания значений пиклистов - пытается обновить синхронно, понимает что их слишком много и переводит на "операция слишком большая, мы оповестим когда она закончится".
В таком случае можно обойтись чисто контроллером и страницей - процесс может проходить следующим образом:
- в контроллере реализуется флаг который даёт понять закончился процесс клонирования или нет и например один метод с именем proceed, который внутри себя знает или определяет на каком этапе клонирования он находится
- метод proceed может быть организован следующим образом - на первый вызов он клонирует root запись, на второй - n чайлд записей root записи, на следующий вызов - следующие n чайлд записей и т.д. (думаю смысл понятен)
- а далее всё просто - после загрузки страницы начинается пинг-понг между клиентом и сервером - пока флаг о том что всё склонировалось не true - клиент говорит серверу proceed, сервер proceed'ит - возвращает контроль на клиент и цепочка повторяется
Из плюсов:
- можно оповещать пользователя в режиме реального времени что происходит
- не плодится кол-во батчей
Из минусов:
- если пользователь закроет страницу в процессе обработки - клонирование будет не полным, хотя и в этом случае можно предусмотреть некий механизм (например добавить в объект флаг, который будет симафорить что клон "грязный" и потом чистить такие записи)
[quote="Den Brown"]Процесс клонирования вызывается с кастомноой кнопки, которая ведет на ВФ страницу, чей контроллер выполняет клонирование и возвращает пользователю стандарный вид клонированной записи.[/quote] В таком случае можно обойтись чисто контроллером и страницей - процесс может проходить следующим образом: - в контроллере реализуется флаг который даёт понять закончился процесс клонирования или нет и например один метод с именем proceed, который внутри себя знает или определяет на каком этапе клонирования он находится - метод proceed может быть организован следующим образом - на первый вызов он клонирует root запись, на второй - n чайлд записей root записи, на следующий вызов - следующие n чайлд записей и т.д. (думаю смысл понятен) - а далее всё просто - после загрузки страницы начинается пинг-понг между клиентом и сервером - пока флаг о том что всё склонировалось не true - клиент говорит серверу proceed, сервер proceed'ит - возвращает контроль на клиент и цепочка повторяется Из плюсов: - можно оповещать пользователя в режиме реального времени что происходит - не плодится кол-во батчей Из минусов: - если пользователь закроет страницу в процессе обработки - клонирование будет не полным, хотя и в этом случае можно предусмотреть некий механизм (например добавить в объект флаг, который будет симафорить что клон "грязный" и потом чистить такие записи)
Как альтернатива, но только если минусы не смущают, лучше таким транзакциям полностью передаваться в бэкэнд.
[quote="ilya leshchuk"][/quote] Как альтернатива, но только если минусы не смущают, лучше таким транзакциям полностью передаваться в бэкэнд.
Batch в данном случае не панацея, а я бы даже сказал что ещё большее зло
Тут надо будет либо организовывать очередь из записей которые надо склонировать, либо надеяться на то что n-пользователей не захотят склонировать одновременно n-записей, потому как по описанию процесс клонирования может затянуться. И конечно же в любом случае никак это всё в одну транзакцию не завернуть.
Batch в данном случае не панацея, а я бы даже сказал что ещё большее зло :) Тут надо будет либо организовывать очередь из записей которые надо склонировать, либо надеяться на то что n-пользователей не захотят склонировать одновременно n-записей, потому как по описанию процесс клонирования может затянуться. И конечно же в любом случае никак это всё в одну транзакцию не завернуть.
Очередь батчей, не?
FlexQueue позволяет решению достаточно неплохо масштабироваться, когда как "небольшие" транзакции можно все еще пускать асинхронно. Сделать простейший гуй для мониторинга состояния джоба как бы тоже не очень сложно - пометил записи для клонирования, засабмиттил джоб, получил его идентификатор и пуллишь статус с определенным интервалом. Да, пользователь в итоге может некоторое время прождать пока он пройдет, зато он спокойно может покинуть страницу и быть уверенным в результате.
Если n-клиентов одновременно захотят склонировать записи и инсерт очередной пачки займет больше 5 секунд, то 11-й одновременный юзверь получит эксепшен, в традициях https://developer.salesforce.com/blogs/engineering/2013/05/force-com-concurrent-request-limits.html
Очередь батчей, не? FlexQueue позволяет решению достаточно неплохо масштабироваться, когда как "небольшие" транзакции можно все еще пускать асинхронно. Сделать простейший гуй для мониторинга состояния джоба как бы тоже не очень сложно - пометил записи для клонирования, засабмиттил джоб, получил его идентификатор и пуллишь статус с определенным интервалом. Да, пользователь в итоге может некоторое время прождать пока он пройдет, зато он спокойно может покинуть страницу и быть уверенным в результате. Если n-клиентов одновременно захотят склонировать записи и инсерт очередной пачки займет больше 5 секунд, то 11-й одновременный юзверь получит эксепшен, в традициях https://developer.salesforce.com/blogs/engineering/2013/05/force-com-concurrent-request-limits.html
Flex Queue как бы тоже не резиновый.
Простейший, да не простейший - в данном случае прийдётся как минимум чейнить батчи, т.е. каким-то образом надо будет расчехлять какой новый батч подхватил задачу.
Подобрать размер пачки достаточно небольшим, чтобы этого не происходило.
PS: у любого решения есть за и против, так что вряд ли какое-то одно будет панацеей, тут просто надо взвесить риски/затратность и сложность решения/время решения и прочее, да и выбрать
PPS: я кстати и сам преверженец asynchronous apex'а для таких задач, просто как раз в данной ситуации с VF и кнопкой можно попробовать предложенный метод потмоу как на мой взгляд он проще и быстрее в реализации.
[quote="cidr8n"]Очередь батчей, не? [/quote] Flex Queue как бы тоже не резиновый. [quote="cidr8n"]Сделать простейший гуй для мониторинга состояния джоба как бы тоже не очень сложно - пометил записи для клонирования, засабмиттил джоб, получил его идентификатор и пуллишь статус с определенным интервалом.[/quote] Простейший, да не простейший - в данном случае прийдётся как минимум чейнить батчи, т.е. каким-то образом надо будет расчехлять какой новый батч подхватил задачу. [quote="cidr8n"]Если n-клиентов одновременно захотят склонировать записи и инсерт очередной пачки займет больше 5 секунд, то 11-й одновременный юзверь получит эксепшен, в традициях https://developer.salesforce.com/blogs/engineering/2013/05/force-com-concurrent-request-limits.html[/quote] Подобрать размер пачки достаточно небольшим, чтобы этого не происходило. PS: у любого решения есть за и против, так что вряд ли какое-то одно будет панацеей, тут просто надо взвесить риски/затратность и сложность решения/время решения и прочее, да и выбрать :) PPS: я кстати и сам преверженец asynchronous apex'а для таких задач, просто как раз в данной ситуации с VF и кнопкой можно попробовать предложенный метод потмоу как на мой взгляд он проще и быстрее в реализации.
Да, на этом и согласимся.
Очередь за автором, плюсы/минусы обрисованы :)
[quote="ilya leshchuk"][/quote] Да, на этом и согласимся. Очередь за автором, плюсы/минусы обрисованы :)
спасибо всем за помощь.
пока план такой.
изначально страница показывает пользователю текст:
сейчас будет клонирована родительская и дочерние записи, и вы будите переведены на клонированную родительскую запись. Копирование внучатых записей может занять дополнительное время. При полном завершени клоинрования вы получите уведомление на емейл и поле "Как дела?" на родительской записи получит значение "Полностью клонировано". Не начинайте работу с клонированной родительской записью пока копирование не завершится полностью"
и кнопка "Начать клонирование".
кнопоный метод клонирует родительскую и дочерние записи, запускат батч по внучатым записям, и делает редирект пользователя на стандартный вид клонированной родительской записи.
вроде батч класс написал правильно, сейчас контроллер отработал, "выстрелил" батч и "с приветом" - внучатые записи не были откопированы. прям сейчас нет времени разбираться, но чуть позже буду дебажить батч...
спасибо всем за помощь. пока план такой. изначально страница показывает пользователю текст: сейчас будет клонирована родительская и дочерние записи, и вы будите переведены на клонированную родительскую запись. Копирование внучатых записей может занять дополнительное время. При полном завершени клоинрования вы получите уведомление на емейл и поле "Как дела?" на родительской записи получит значение "Полностью клонировано". Не начинайте работу с клонированной родительской записью пока копирование не завершится полностью" и кнопка "Начать клонирование". кнопоный метод клонирует родительскую и дочерние записи, запускат батч по внучатым записям, и делает редирект пользователя на стандартный вид клонированной родительской записи. вроде батч класс написал правильно, сейчас контроллер отработал, "выстрелил" батч и "с приветом" - внучатые записи не были откопированы. прям сейчас нет времени разбираться, но чуть позже буду дебажить батч...
люди добрые, шош это твориться-то,
если я кверю вот так:
List<Childs__c> childsList = [SELECT id, (SELECT id from GrandChilds__r) FROM Childs__c WHERE Parent__c = :parentID];
то получаю ошибку 50001, т.к. кол-во внучатых записей у меня 75к, не считая 500 дочерних.
А вот если я кверю вот так:
for(Childs__c c : [SELECT id, (SELECT id from GrandChilds__r) FROM Childs__c WHERE Parent__c = :parentID])
{ do something }
то все работает?!
а какие лимиты есть в этом случае?
(а можно ли insert лист с более чем 50к записей или придется его "рубить" на мелкие куски и делать несколько ДМЛ, что "светит" еще более жесткими лимитами, но тем не менее как вариант...)
люди добрые, шош это твориться-то, если я кверю вот так: [code]List<Childs__c> childsList = [SELECT id, (SELECT id from GrandChilds__r) FROM Childs__c WHERE Parent__c = :parentID];[/code] то получаю ошибку 50001, т.к. кол-во внучатых записей у меня 75к, не считая 500 дочерних. А вот если я кверю вот так: [code]for(Childs__c c : [SELECT id, (SELECT id from GrandChilds__r) FROM Childs__c WHERE Parent__c = :parentID]) { do something }[/code] то все работает?! а какие лимиты есть в этом случае? (а можно ли insert лист с более чем 50к записей или придется его "рубить" на мелкие куски и делать несколько ДМЛ, что "светит" еще более жесткими лимитами, но тем не менее как вариант...)
А что происходит внутри этого самого {do something}?
Кол-во внучатых записей в 75K по-идее не должно влиять на ошибку 50001, потому как считается в отдельный лимит, втрое больший чем number of SOQL Query records.
50K записей в DML не пролезут как пить дать, да и порезав их на части тоже не пролезут в одной транзакции.
А что происходит внутри этого самого [i]{do something}[/i]? Кол-во внучатых записей в 75K по-идее не должно влиять на ошибку 50001, потому как считается в отдельный лимит, втрое больший чем number of SOQL Query records. [quote="Den Brown"](а можно ли insert лист с более чем 50к записей или придется его "рубить" на мелкие куски и делать несколько ДМЛ, что "светит" еще более жесткими лимитами, но тем не менее как вариант...)[/quote] 50K записей в DML не пролезут как пить дать, да и порезав их на части тоже не пролезут в одной транзакции.
это отдельный разговор, дойдем до этого
вот здесь выпадает именно из-за внучатых записей в вложенной квери
возникло два вопроса:
как "приходят" записи в подобный цикл (Childs__c записи):
по одной или пачками по 200?
и соответсвенно, если они приходят "по одной", то сколько можно сделать итераций цикла? снова 50к? ведь обратите внимание, что в моем случае только 500 Childs__c и поэтому никакой лимит "не схвачен".
и соответсвенно, если они приходят в тот цикл "пачками по 200", то опять сколько можно обработать всего записей? и самое главное я опять могу не вписаться в лимит 50к на общее кол-во записей в квери, так как в таком случае в каждую итерацию цикла приходит 200 дочерних и около 30к внучатых записей, а было бы последних немного больше - и возможно этот квери бы "упал" на 50001.
[quote="ilya leshchuk"]А что происходит внутри этого самого {do something}? [/quote] это отдельный разговор, дойдем до этого [quote="ilya leshchuk"]Кол-во внучатых записей в 75K по-идее не должно влиять на ошибку 50001[/quote] вот здесь выпадает именно из-за внучатых записей в вложенной квери [quote="Den Brown"]List<Childs__c> childsList = [SELECT id, (SELECT id from GrandChilds__r) FROM Childs__c WHERE Parent__c = :parentID];[/quote] возникло два вопроса: как "приходят" записи в подобный цикл (Childs__c записи): [quote="Den Brown"]for(Childs__c c : [SELECT id, (SELECT id from GrandChilds__r) FROM Childs__c WHERE Parent__c = :parentID]) [/quote] [b]по одной или пачками по 200?[/b] и соответсвенно, если они приходят "по одной", то сколько можно сделать итераций цикла? снова 50к? ведь обратите внимание, что в моем случае только 500 Childs__c и поэтому никакой лимит "не схвачен". и соответсвенно, если они приходят в тот цикл "пачками по 200", то опять сколько можно обработать всего записей? и самое главное я опять могу не вписаться в лимит 50к на общее кол-во записей в квери, так как в таком случае в каждую итерацию цикла приходит 200 дочерних и около 30к внучатых записей, а было бы последних немного больше - и возможно этот квери бы "упал" на 50001.
Ааа, я затупил, не записей они разрешают втрое больше, а самих подзапросов разрешают втрое больше чем SOQL statements. Тогда вообще становится нихрена не понятно :)
Ааа, я затупил, не записей они разрешают втрое больше, а самих подзапросов разрешают втрое больше чем SOQL statements. Тогда вообще становится нихрена не понятно :)
попробовал вот так "промотать" в цикле все 75к внучатых записей:
for(GrandChilds__c gc : [SELECT id FROM GrandChilds__c]){}
но нет, опять ошибка 50001. а счастье было так близко, а счастье было так возможно...
так что пока могу обратиться к более чем 50к внучатых записей только так:
for(Childs__c c : [SELECT id, (SELECT id from GrandChilds__r) FROM Childs__c WHERE Parent__c = :parentID])
{работа с внучатыми записями}
но какая работа может быть с таким кол-вом записей? получение каких то метриков, как просто кол-во записей, или вычисление какого значения. Или просто сбор\подготовка записей для пагинации\отправки на фронт в виде таблицы.
а если нужно что-то менять в таком кол-ве записей или клонировать их, то там уже батч интерфейс нужен, но в таком случае не требуется немедленный результат (как с вычислением метриков).
но такой подход (со вложенным квери) возможен если там есть родительские записи, к которым можно вложить суб-квери. и, во-вторых, если в такой цикл записи приходят пачками по 200, то еще нужно смотреть, не превысит ли кол-во вложенных в эти 200 записей более чем 50к.
если у кого-то есть примеры/ссылки того как работать с более 50к записей без батч интерфейса, думаю, что это самое время ими поделиться...
попробовал вот так "промотать" в цикле все 75к внучатых записей: [code] for(GrandChilds__c gc : [SELECT id FROM GrandChilds__c]){}[/code] но нет, опять ошибка 50001. а счастье было так близко, а счастье было так возможно... так что пока могу обратиться к более чем 50к внучатых записей только так: [code]for(Childs__c c : [SELECT id, (SELECT id from GrandChilds__r) FROM Childs__c WHERE Parent__c = :parentID]) {работа с внучатыми записями}[/code] но какая работа может быть с таким кол-вом записей? получение каких то метриков, как просто кол-во записей, или вычисление какого значения. Или просто сбор\подготовка записей для пагинации\отправки на фронт в виде таблицы. а если нужно что-то менять в таком кол-ве записей или клонировать их, то там уже батч интерфейс нужен, но в таком случае не требуется немедленный результат (как с вычислением метриков). но такой подход (со вложенным квери) возможен если там есть родительские записи, к которым можно вложить суб-квери. и, во-вторых, если в такой цикл записи приходят пачками по 200, то еще нужно смотреть, не превысит ли кол-во вложенных в эти 200 записей более чем 50к. если у кого-то есть примеры/ссылки того как работать с более 50к записей без батч интерфейса, думаю, что это самое время ими поделиться...
for(GrandChilds__c gc : [SELECT id FROM GrandChilds__c]){}
Может я ошибаюсь но вроде у меня в таком случае валилось на 101 SQOL, то есть на 20000?
[quote="Den Brown"]попробовал вот так "промотать" в цикле все 75к внучатых записей: for(GrandChilds__c gc : [SELECT id FROM GrandChilds__c]){}[/quote] Может я ошибаюсь но вроде у меня в таком случае валилось на 101 SQOL, то есть на 20000?
Тебе надо устроить как сказал Илья пинг понг на клиенте,в твоем случае подходит <apex:actionFunction> with oncomplete который например поднимает другой actionFunction, или Итеративано вызвать из js @remoteAction проверяя состояние.
Тебе надо устроить как сказал Илья пинг понг на клиенте,в твоем случае подходит <apex:actionFunction> with oncomplete который например поднимает другой actionFunction, или Итеративано вызвать из js @remoteAction проверяя состояние.
да, это интересная идея. фактически предлагается многократный, "автоматизированный" вызов метода контроллера с кастомной разбивкой работы на "пачки", которые нужно "обработать", и это продолжать до тех пор пока все записи не будут "обработаны".
но как быть с другой задачей (это другой случай), где требуется вычисление данных с записей кол-вом более 50к (50к-150к)? Например вычисление среднего значения поля "Дата поступления заявки".
у меня есть идея по этому поводу, но она требует изменения структуры БД, то есть сейчас все эти >50к записей не имеют никаких родительских записей, а вот если бы они имели... например родительской записью была запись "Группа заявок за такое-то число" и все "Заявки" за такое -то число были дочерними, то во первых, я мог кверить родительские записи со вложенными Заявками более эффективно, а главное, я бы мог переложить вычисление метриков на СФ, то есть на родительской записи сделать формульное поля, вычисляющее среднее значение поля "Дата поступления заявки" дочерних записей-заявок, а тут и "сказочке конец" - тогда мне пришлось бы делать вычисление только на родительских записях, которых на два порядке меньше, чем самих "Заявок". Т.е. изменяется сама структура БД, создается "техническая" иерархия записей, с перекладыванием вычислений на СФ и переносом вычисленных данных на вышестоящий, родительский пояс, группирующий целевые записи по какому-то признаку. Но вероятно, это усложнение всего на свете, вероятно есть более простые, стандартные решения.
[quote="Sergey Prichepo"]Тебе надо устроить как сказал Илья пинг понг на клиенте[/quote] да, это интересная идея. фактически предлагается многократный, "автоматизированный" вызов метода контроллера с кастомной разбивкой работы на "пачки", которые нужно "обработать", и это продолжать до тех пор пока все записи не будут "обработаны". но как быть с другой задачей (это другой случай), где требуется вычисление данных с записей кол-вом более 50к (50к-150к)? Например вычисление среднего значения поля "Дата поступления заявки". у меня есть идея по этому поводу, но она требует изменения структуры БД, то есть сейчас все эти >50к записей не имеют никаких родительских записей, а вот если бы они имели... например родительской записью была запись "Группа заявок за такое-то число" и все "Заявки" за такое -то число были дочерними, то во первых, я мог кверить родительские записи со вложенными Заявками более эффективно, а главное, я бы мог переложить вычисление метриков на СФ, то есть на родительской записи сделать формульное поля, вычисляющее среднее значение поля "Дата поступления заявки" дочерних записей-заявок, а тут и "сказочке конец" - тогда мне пришлось бы делать вычисление только на родительских записях, которых на два порядке меньше, чем самих "Заявок". Т.е. изменяется сама структура БД, создается "техническая" иерархия записей, с перекладыванием вычислений на СФ и переносом вычисленных данных на вышестоящий, родительский пояс, группирующий целевые записи по какому-то признаку. Но вероятно, это усложнение всего на свете, вероятно есть более простые, стандартные решения.
Не совсем понимаю, почему батч не способен и склонировать и подсчитать что угодно ?
Не совсем понимаю, почему батч не способен и склонировать и подсчитать что угодно ?
Очень сильно решение зависит от контекста - если опять же это происходит на клиенте, то тут - connection.js и палочка-выручалочка в виде queryMore - лопатьтье себе записи до посинения и собирайте статистику, если где-то внутри apex'а - только асинхронно через тот же самый batch.
Кстати, немаловажная деталь, которую почему-то забывают при работе с батчами, но о которой стоит помнить - The maximum number of asynchronous Apex method executions (batch Apex, future methods, Queueable Apex, and scheduled Apex) per a 24-hour period, которых кстати 250K или 200xUsers, величина не такая уж и большая учитываю то, как сейчас все любят использовать batch'и, да и future методами грешат по страшному.
[quote="Den Brown"]но как быть с другой задачей (это другой случай), где требуется вычисление данных с записей кол-вом более 50к (50к-150к)? Например вычисление среднего значения поля "Дата поступления заявки". [/quote] Очень сильно решение зависит от контекста - если опять же это происходит на клиенте, то тут - connection.js и палочка-выручалочка в виде queryMore - лопатьтье себе записи до посинения и собирайте статистику, если где-то внутри apex'а - только асинхронно через тот же самый batch. [quote="wilder"]Не совсем понимаю, почему батч не способен и скорнировать и подсчитать что угодно ?[/quote] Кстати, немаловажная деталь, которую почему-то забывают при работе с батчами, но о которой стоит помнить - [b]The maximum number of asynchronous Apex method executions (batch Apex, future methods, Queueable Apex, and scheduled Apex) per a 24-hour period[/b], которых кстати 250K или 200xUsers, величина не такая уж и большая учитываю то, как сейчас все любят использовать batch'и, да и future методами грешат по страшному.
Ну все лимиты за деньги можно увеличить если этого захочет клиент.
[quote="ilya leshchuk"]которых кстати 250K или 200xUsers, величина не такая уж и большая учитываю то, как сейчас все любят использовать batch'и, да и future методами грешат по страшному.[/quote] Ну все лимиты за деньги можно увеличить если этого захочет клиент.
запустил Батч, и знаете что, оказывается если метод execute "падает", то finish все равно выполняется, отправляя пользоватлю письмо, что все в порядке.
Ну ладно создаем переменную класса в качестве флага, работаем с ней в execute и проверяем в finish: не работает.
Хорошо, объявляем ее статичной и продолжаем попытки: не работает. Т.е. инициализация класса, каждый execute вызов и finish выполняются в разных, независимых ран контекстах и между ними нет связи. Но как в finish узнать как отработали executы? да неужели через Кастом сеттинги? уверен, что вы знаете правильный ответ, я пока даже гуглить его не стал...
запустил Батч, и знаете что, оказывается если метод execute "падает", то finish все равно выполняется, отправляя пользоватлю письмо, что все в порядке. Ну ладно создаем переменную класса в качестве флага, работаем с ней в execute и проверяем в finish: не работает. Хорошо, объявляем ее статичной и продолжаем попытки: не работает. Т.е. инициализация класса, каждый execute вызов и finish выполняются в разных, независимых ран контекстах и между ними нет связи. Но как в finish узнать как отработали executы? да неужели через Кастом сеттинги? уверен, что вы знаете правильный ответ, я пока даже гуглить его не стал...
Стэйтфул батч интерфейс вроде для этого как раз, нет?
Стэйтфул батч интерфейс вроде для этого как раз, нет?
спасибо огромное, наш форум работает быстрее гугла, всего 4 минуты потребовалось.
вот здесь в разделе Using State in Batch Apex все черным по англискому написано, то что я уже успел понять экспериментальным путем.
сейчас буду пробовать
PS: да, класные переменные сохраняют состояние между вызовами.
[quote="cidr8n"]Стэйтфул батч интерфейс вроде для этого как раз, нет?[/quote] спасибо огромное, наш форум работает быстрее гугла, всего 4 минуты потребовалось. вот [url=https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_batch_interface.htm]здесь[/url] в разделе Using State in Batch Apex все черным по англискому написано, то что я уже успел понять экспериментальным путем. сейчас буду пробовать PS: да, класные переменные сохраняют состояние между вызовами.
еще вот нашел такой способ посчитать кол-во более чем 50к записей:
for (AggregateResult result : [
SELECT COUNT(Id)
FROM SomeObject__c
]) total += (Integer)result.get('expr0');
эти СЕЛЕКТО-циклы просто творят чудеса, но данный вариант описан там как "Ultimately undocumented behavior", но есть ли у вас возражения по поводу такого подхода?
еще вот нашел такой способ посчитать кол-во более чем 50к записей: [code]for (AggregateResult result : [ SELECT COUNT(Id) FROM SomeObject__c ]) total += (Integer)result.get('expr0');[/code] [url=http://salesforce.stackexchange.com/questions/102030/how-to-query-more-than-50000-records-from-sobject]здесь[/url] эти СЕЛЕКТО-циклы просто творят чудеса, но данный вариант описан там как "Ultimately undocumented behavior", но есть ли у вас возражения по поводу такого подхода?
Без цикла запрос должен сгенерить рантаймовую ошибку при наличие более чем 50000 записей
[quote="Den Brown"] есть ли у вас возражения по поводу такого подхода?[/quote] Без цикла запрос должен сгенерить рантаймовую ошибку при наличие более чем 50000 записей
Спасибки. Реально нужно было как-то победить этот лимит. Я правда использовал для этого вызов вебсервиса.
Спасибки. Реально нужно было как-то победить этот лимит. Я правда использовал для этого вызов вебсервиса.
так:
@ReadOnly
@RemoteAction
или REST WS по Гресу с ВФ страницей объявленной как ReadOnly?
я пока решил так:
в клонирующей странице я выстреливаю клонирование в батч и юзер ждет (а может и не ждать - закрыть страницу) на клонированной родительской записи пока все не закончится, до этого момента ВалРул не даст запускать ту запись в бизнес процесс.
а страницу, показывающую метрики, просто объявить как ReadOnly и все. Но там еще есть новый функционал, который все-таки делает ДМЛ, так вот эту часть можно просто вывести в новую ВФ страницу и заАйФреймить в основную ReadOnly страницу.
[quote="wilder"]Я правда использовал для этого вызов вебсервиса.[/quote] так: [code] @ReadOnly @RemoteAction[/code] или REST WS по Гресу с ВФ страницей объявленной как ReadOnly? я пока решил так: в клонирующей странице я выстреливаю клонирование в батч и юзер ждет (а может и не ждать - закрыть страницу) на клонированной родительской записи пока все не закончится, до этого момента ВалРул не даст запускать ту запись в бизнес процесс. а страницу, показывающую метрики, просто объявить как ReadOnly и все. Но там еще есть новый функционал, который все-таки делает ДМЛ, так вот эту часть можно просто вывести в новую ВФ страницу и заАйФреймить в основную ReadOnly страницу.
@ReadOnly
@RemoteAction
К сожалению в моем случае это все не возможно было использовать.
[quote="Den Brown"]так: @ReadOnly @RemoteAction[/quote] К сожалению в моем случае это все не возможно было использовать.
ого, схватил новый challenge на тему "работаем более чем с 50к записями"!
есть ВФ страница, которая возвращает contentType = 'application/vnd.ms-excel'
страница объявлена как ReadOnly
а таблица на этой странице создается следующим образом
родительская запись создает ее строку, а содержание дочерних - ячейки в этой строке.
в одном случае получилось много колонок (их число вариабельно) и много строк, в результате в работе много ячеек - дочерних записей - более 50к. Но это не беда, так как страница то ReadOnly,
но страница начала падать на System.LimitException: Apex heap size too large: 17580604 и иногда на CPU тайм превышено.
что делать? заказчину нужно вывести все эти записи в виде единой таблицы (да еще ms-excel файлом).
пока надеюсь оптимизировать код, но это может и не помочь в должной степени.
печалька
ого, схватил новый challenge на тему "работаем более чем с 50к записями"! есть ВФ страница, которая возвращает contentType = 'application/vnd.ms-excel' страница объявлена как ReadOnly а таблица на этой странице создается следующим образом родительская запись создает ее строку, а содержание дочерних - ячейки в этой строке. в одном случае получилось много колонок (их число вариабельно) и много строк, в результате в работе много ячеек - дочерних записей - более 50к. Но это не беда, так как страница то ReadOnly, но страница начала падать на System.LimitException: Apex heap size too large: 17580604 и иногда на CPU тайм превышено. что делать? заказчину нужно вывести все эти записи в виде единой таблицы (да еще ms-excel файлом). пока надеюсь оптимизировать код, но это может и не помочь в должной степени. печалька
По-честному сгенерить xml и отдать, как xls, можно даже не 1 потоком.
[quote="Den Brown"]что делать?[/quote] По-честному сгенерить xml и отдать, как xls, можно даже не 1 потоком.
Что значит
?
собрать в контролере Стринг содержащий xml с данными и отдать на фронт, то есть не "разматывать" массив на ВФ странице как это сейчас происходит? а это много вызволит ресурсов?
про
Что значит [quote="Gres"]сгенерить xml[/quote] ? собрать в контролере Стринг содержащий xml с данными и отдать на фронт, то есть не "разматывать" массив на ВФ странице как это сейчас происходит? а это много вызволит ресурсов? про [quote="Gres"]можно даже не 1 потоком[/quote] пока даже спрашивать не буду...
собрать в контролере Стринг содержащий xml с данными и отдать на фронт, то есть не "разматывать" массив на ВФ странице как это сейчас происходит? а это много вызволит ресурсов?
про можно даже не 1 потоком
Можешь даже не xml, можно и простой html. Но правда я не уверен что одна строка не повалит ХИП. Я бы все же закинул это все в какой-то асинхронный процесс.
[quote="Den Brown"]Что значит [quote="Gres"]сгенерить xml[/quote] ? собрать в контролере Стринг содержащий xml с данными и отдать на фронт, то есть не "разматывать" массив на ВФ странице как это сейчас происходит? а это много вызволит ресурсов? про [quote="Gres"]можно даже не 1 потоком[/quote] пока даже спрашивать не буду...[/quote] Можешь даже не xml, можно и простой html. Но правда я не уверен что одна строка не повалит ХИП. Я бы все же закинул это все в какой-то асинхронный процесс.
Можно попробовать и со строкой, но чутье подсказывает что это упадет еще раньше, спасибо immutability стринг в апексе.
Если сериализовать разом, то этого не должно произойти, но по моему скудному опыту, xml райтеры в апексе весьма прожорливые на память.
В случае необходимости генерить файлы можно глянуть в сторону переноса этого на клиент - собирать контект в JS-e, по remoteAction/веб-сервису, потом уже конвертить в файл и отдавать на скачивание.
Можно попробовать и со строкой, но чутье подсказывает что это упадет еще раньше, спасибо immutability стринг в апексе. Если сериализовать разом, то этого не должно произойти, но по моему скудному опыту, xml райтеры в апексе весьма прожорливые на память. В случае необходимости генерить файлы можно глянуть в сторону переноса этого на клиент - собирать контект в JS-e, по remoteAction/веб-сервису, потом уже конвертить в файл и отдавать на скачивание.
переноса этого на клиент - собирать контект в JS-e, по remoteAction/веб-сервису, потом уже конвертить в файл и отдавать на скачивание
это первая пришедшая мысль - "докачать" контентом таблицу на клиенте, но я не понимаю как браузерное окно с html доком в какой то момент можно "превратить" в загружающийся документ (фактически уже загруженный и собранный, но как его "отдать")?
[quote="cidr8n"]переноса этого на клиент - собирать контект в JS-e, по remoteAction/веб-сервису, потом уже конвертить в файл и отдавать на скачивание[/quote] это первая пришедшая мысль - "докачать" контентом таблицу на клиенте, но я не понимаю как браузерное окно с html доком в какой то момент можно "превратить" в загружающийся документ (фактически уже загруженный и собранный, но как его "отдать")?
Когда-то делал это чем-то вроде этого: http://jsfiddle.net/uselesscode/qm5ag/
Сейчас бы попробовал это, если список поддерживаемых браузеров подходит: https://github.com/eligrey/FileSaver.js
Когда-то делал это чем-то вроде этого: http://jsfiddle.net/uselesscode/qm5ag/ Сейчас бы попробовал это, если список поддерживаемых браузеров подходит: https://github.com/eligrey/FileSaver.js
immutability
я вроде где то видел реализации стрингБилдера,какие то обходные варианты там были.
[quote="cidr8n"] immutability[/quote] я вроде где то видел реализации стрингБилдера,какие то обходные варианты там были.
если список поддерживаемых браузеров подходит
там будут старые "дубовые" IE, можно и не сомневаться.
еще возник вариант "отдачи" клиенту большого ms-excel файла: а что если отправить его емэйлом?
Это что-то для нас меняет/улучшает?
получается что такую отправку можно "отстрелить" в Future процесс, которые по легенде имеют бОльшие лимиты (но насколько большие предание умалчивает)
PS:кроме того, если это аттач к письму, то почему бы его не сделать более легким CSV файлом?
PSS: если на то уж пошло, то почему бы и в браузер не отдавать CSV файл - он же будет заметно легче?
[quote="cidr8n"]если список поддерживаемых браузеров подходит[/quote] там будут старые "дубовые" IE, можно и не сомневаться. еще возник вариант "отдачи" клиенту большого ms-excel файла: а что если отправить его емэйлом? Это что-то для нас меняет/улучшает? получается что такую отправку можно "отстрелить" в Future процесс, которые по легенде имеют бОльшие лимиты (но насколько большие предание умалчивает) PS:кроме того, если это аттач к письму, то почему бы его не сделать более легким CSV файлом? PSS: если на то уж пошло, то почему бы и в браузер не отдавать CSV файл - он же будет заметно легче?
еще возник вариант "отдачи" клиенту большого ms-excel файла: а что если отправить его емэйлом?
Email services heap size is 36 MB. как вариант ты можешь там делать свои операции.
вот краткая анотация по Future.
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_enhanced_future_overview.htm
[quote="Den Brown"]еще возник вариант "отдачи" клиенту большого ms-excel файла: а что если отправить его емэйлом? [/quote] Email services heap size is 36 MB. как вариант ты можешь там делать свои операции. вот краткая анотация по Future. https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_enhanced_future_overview.htm
Cтранно раньше была документаци по Future что все лимиты у величины в два раза.
Cтранно раньше была документаци по Future что все лимиты у величины в два раза.
но насколько большие предание умалчивает
Там можно ручками лимиты выставлять в частности размер хипа.
[quote="Den Brown"]но насколько большие предание умалчивает[/quote] Там можно ручками лимиты выставлять в частности размер хипа.
Это не так давно появилось насколько я понял,меня смущает это Futre We provide this feature to selected customers through a pilot program that requires agreement to specific terms and conditions. To be nominated to participate in the program, contact Salesforce. Because pilot programs are subject to change, we can’t guarantee acceptance. This pilot feature isn’t generally available, as referenced in this document or in press releases or public statements. We can’t guarantee general availability within any particular time frame or at all. Make your purchase decisions only on the basis of generally available features.
Это не так давно появилось насколько я понял,меня смущает это Futre We provide this feature to selected customers through a pilot program that requires agreement to specific terms and conditions. To be nominated to participate in the program, contact Salesforce. Because pilot programs are subject to change, we can’t guarantee acceptance. This pilot feature isn’t generally available, as referenced in this document or in press releases or public statements. We can’t guarantee general availability within any particular time frame or at all. Make your purchase decisions only on the basis of generally available features.
Это не так давно появилось насколько я понял,меня смущает это Futre We provide this feature to selected customers through a pilot program that requires agreement to specific terms and conditions. To be nominated to participate in the program, contact Salesforce. Because pilot programs are subject to change, we can’t guarantee acceptance. This pilot feature isn’t generally available, as referenced in this document or in press releases or public statements. We can’t guarantee general availability within any particular time frame or at all. Make your purchase decisions only on the basis of generally available features.
Эта фича в бете уже больше года точно. Высокая вероятность что ее вообще уберут, ибо с Future они резко переключились за развитие Queueable интерфейса.
[quote="Sergey Prichepo"]Это не так давно появилось насколько я понял,меня смущает это Futre We provide this feature to selected customers through a pilot program that requires agreement to specific terms and conditions. To be nominated to participate in the program, contact Salesforce. Because pilot programs are subject to change, we can’t guarantee acceptance. This pilot feature isn’t generally available, as referenced in this document or in press releases or public statements. We can’t guarantee general availability within any particular time frame or at all. Make your purchase decisions only on the basis of generally available features.[/quote] Эта фича в бете уже больше года точно. Высокая вероятность что ее вообще уберут, ибо с Future они резко переключились за развитие Queueable интерфейса.
Опять на тему JS-a.
Короткое гугление показывает, что можно делать c filesaver-ом и на IE9: http://stackoverflow.com/questions/2897619/using-html5-javascript-to-generate-and-save-a-file
Не думаю что стоит беспокоиться о совместимости с восьмеркой, которую сам форс уже официально не поддерживает.
Опять на тему JS-a. Короткое гугление показывает, что можно делать c filesaver-ом и на IE9: http://stackoverflow.com/questions/2897619/using-html5-javascript-to-generate-and-save-a-file Не думаю что стоит беспокоиться о совместимости с восьмеркой, которую сам форс уже официально не поддерживает.
пока даже спрашивать не буду...
У excel есть свой формат xml, который он прекрасно читает, редактирует и сохраняет (не то, что html), тебе нужно сформировать этот файлик и дать возможнсоть пользователю его скачать. В одном контексте у тебя это сделать не получится, так как скорее всего не хватит хипа, ты можешь использовать батч/etc для формирования этого файлика, причем также можно использовать промежуточное сохранение файла, а не хранить его все время строкой в Stateful батче.
Так же можешь джаваскриптом генерить файла, а грузить данные пачками через веб сервис.
[quote="Den Brown"]пока даже спрашивать не буду...[/quote] У excel есть свой формат xml, который он прекрасно читает, редактирует и сохраняет (не то, что html), тебе нужно сформировать этот файлик и дать возможнсоть пользователю его скачать. В одном контексте у тебя это сделать не получится, так как скорее всего не хватит хипа, ты можешь использовать батч/etc для формирования этого файлика, причем также можно использовать промежуточное сохранение файла, а не хранить его все время строкой в Stateful батче. Так же можешь джаваскриптом генерить файла, а грузить данные пачками через веб сервис.
Пользуясь случаем, и на ночь глядя, разрешите поинтересоваться,
а можно ли использовать ОРДЕР БАЙ во вложенном квери вот так:
[SELECT...(SELECT ... ORDER BY...) ...ORDER BY...]
и можно ли использовать для ОРДЕР БАЙ поле полученное по связи LookUpFiel__r.FieldForOrdering__c?
(если "нет", то можно сделать "родное" но формульное поле, которое просто тянет FieldForOrdering__c по связи)
Пользуясь случаем, и на ночь глядя, разрешите поинтересоваться, а можно ли использовать ОРДЕР БАЙ во вложенном квери вот так: [code][SELECT...(SELECT ... [b]ORDER BY[/b]...) ...ORDER BY...][/code] это бы очень многое решило... и можно ли использовать для ОРДЕР БАЙ поле полученное по связи [b]LookUpFiel__r[/b].FieldForOrdering__c? (если "нет", то можно сделать "родное" но формульное поле, которое просто тянет FieldForOrdering__c по связи)
а можно ли использовать ОРДЕР БАЙ во вложенном квери вот так:
Можно.и можно ли использовать для ОРДЕР БАЙ поле полученное по связи LookUpFiel__r.FieldForOrdering__c?
Можно.
[quote="Den Brown"]а можно ли использовать ОРДЕР БАЙ во вложенном квери вот так: [/quote] Можно.[quote="Den Brown"]и можно ли использовать для ОРДЕР БАЙ поле полученное по связи LookUpFiel__r.FieldForOrdering__c? [/quote] Можно.
Можно
ну вот и прекрасно,
больше всего на свете я люблю секс, хорошую музыку и "перекладывать" работу на БД!
завтра буду пробовать,
спасибо
[quote="ilya leshchuk"]Можно[/quote] ну вот и прекрасно, больше всего на свете я люблю секс, хорошую музыку и "перекладывать" работу на БД! завтра буду пробовать, спасибо
пока результаты с contentType = 'application/vnd.ms-excel' страницей такие:
вчера я почистил "гавнокод" вроде замены вложенного цикла на Мапу, прекратил кверить и отправлять на фронт массу ненужной инфы, и код работающий с 85к записями перестал падать по CPU time and Heap size limits. Но CPU time все равно доходил до 8000мс.
сегодня я переложил всю работу по упорядоченванию ячеек таблицы на ОРДЕР БАЙ и моему коду стало просто вообще нет чего делать. Если раньше CPU time было "3101" то теперь оно упало "0", также как и хип. Получается если в коде, в котором квериться 30к записей, ничего с ними не делать, а просто немного "пожонглировать" ссылками, то никакие ресурсы вообще не тратяться (несмотря что там "сидит" 30к записей).
но осталась проблема с длительной загрузкой файла в браузер. Требуется минута и более на 1Мб. Очень чувствительно. На больших файлах более 4мб браузерная страница иногда уходит в тайм-аут...
Пришлось "оптимизировать" ВФ страницу. Пока я не отказался от идеи использовать HTML-based EXCEL файл, но убрал все пробелы в разметке (которые умножаются тысячекратно в большой таблице) и размер файла сократился на треть.
Так что пока обошелся просто оптимизацией того что есть, а дальше жизнь покажет. Спасибо
пока результаты с contentType = 'application/vnd.ms-excel' страницей такие: вчера я почистил "гавнокод" вроде замены вложенного цикла на Мапу, прекратил кверить и отправлять на фронт массу ненужной инфы, и код работающий с 85к записями перестал падать по CPU time and Heap size limits. Но CPU time все равно доходил до 8000мс. сегодня я переложил всю работу по упорядоченванию ячеек таблицы на ОРДЕР БАЙ и моему коду стало просто вообще нет чего делать. Если раньше CPU time было "3101" то теперь оно упало "0", также как и хип. Получается если в коде, в котором квериться 30к записей, ничего с ними не делать, а просто немного "пожонглировать" ссылками, то никакие ресурсы вообще не тратяться (несмотря что там "сидит" 30к записей). но осталась проблема с длительной загрузкой файла в браузер. Требуется минута и более на 1Мб. Очень чувствительно. На больших файлах более 4мб браузерная страница иногда уходит в тайм-аут... Пришлось "оптимизировать" ВФ страницу. Пока я не отказался от идеи использовать HTML-based EXCEL файл, но убрал все пробелы в разметке (которые умножаются тысячекратно в большой таблице) и размер файла сократился на треть. Так что пока обошелся просто оптимизацией того что есть, а дальше жизнь покажет. Спасибо
такой нюанс с ORDER BY:
[SELECT...(SELECT ... FROM Childs__c ORDER BY Parent_2__r.Ordering_field__c) FROM Parent_1__c WHERE SuperParent__c =:SuperParent.ID]
[SELECT... FROM Parent_2__c WHERE SuperParent__c =:SuperParent.ID ORDER BY Ordering_field__c]
здесь можно ожидать, что если у каждого Parent_2__c есть один дочерний Childs__c, то упорядочевание в обоих случаях будет одинаковым, так как используется одно и тоже поле. И это верно. Но если Ordering_field__c у нескольких записей оказывается пустым, то БД ищет другой признак для упорядочивания таких записей. Вероятно, этим "другим признаком" станет другое произвольное поле у выбираемого объекта. Но дело в том в первом случае это будет поле с Childs__c , а во втором с Parent_2__c. Т.е. в случаях когда Ordering_field__c имеет одинакое значение, то упорядочивание может получится разным...
PS: и вот для таких случаев нужно указываеть второе дополнительное поле для ORDER BY, я использовал (Parent_2__r.)Name
такой нюанс с ORDER BY: [code][SELECT...(SELECT ... FROM Childs__c ORDER BY Parent_2__r.[b]Ordering_field__c[/b]) FROM Parent_1__c WHERE SuperParent__c =:SuperParent.ID][/code] и [code][SELECT... FROM Parent_2__c WHERE SuperParent__c =:SuperParent.ID ORDER BY [b]Ordering_field__c[/b]][/code] здесь можно ожидать, что если у каждого Parent_2__c есть один дочерний Childs__c, то упорядочевание в обоих случаях будет одинаковым, так как используется одно и тоже поле. И это верно. Но если [b]Ordering_field__c[/b] у нескольких записей оказывается пустым, то БД ищет другой признак для упорядочивания таких записей. Вероятно, этим "другим признаком" станет другое произвольное поле у выбираемого объекта. Но дело в том в первом случае это будет поле с Childs__c , а во втором с Parent_2__c. Т.е. в случаях когда [b]Ordering_field__c[/b] имеет одинакое значение, то упорядочивание может получится разным... PS: и вот для таких случаев нужно указываеть второе дополнительное поле для ORDER BY, я использовал (Parent_2__r.)Name
Никакого особого нюанса тут нет, просто надо знать две вещи:
1. По умолчанию (когда не указан никакой ORDER BY) применяется ORDER BY ID
2. В случае применения сортировки, по умолчанию применяется NULLS FIRST
В случаях когда Ordering_Field__c имеет одинаковое значение, отличное от NULL - упорядочивание будет одинаковым, в противном случае упорядочивание подчинится Id соответствующего объекта.
Никакого особого нюанса тут нет, просто надо знать две вещи: 1. По умолчанию (когда не указан никакой ORDER BY) применяется [b]ORDER BY ID[/b] 2. В случае применения сортировки, по умолчанию применяется [b]NULLS FIRST[/b] В случаях когда Ordering_Field__c имеет одинаковое значение, отличное от NULL - упорядочивание будет одинаковым, в противном случае упорядочивание подчинится Id соответствующего объекта.
Никакого особого нюанса тут нет, просто надо знать две вещи:
1. По умолчанию (когда не указан никакой ORDER BY) применяется ORDER BY ID
2. В случае применения сортировки, по умолчанию применяется NULLS FIRST
В случаях когда Ordering_Field__c имеет одинаковое значение, отличное от NULL - упорядочивание будет одинаковым, в противном случае упорядочивание подчинится Id соответствующего объекта.
все так и есть, просто проблема вкрадывается неявно, так как в качестве "дополнительного" поля для упорядочивания система выбирает ID Child__c в одном случае и ID Parent_2__c во втором.
пришлось указать явно, я выбрал Parent_2__r.Name и Name соответсвенно
[quote="ilya leshchuk"]Никакого особого нюанса тут нет, просто надо знать две вещи: 1. По умолчанию (когда не указан никакой ORDER BY) применяется ORDER BY ID 2. В случае применения сортировки, по умолчанию применяется NULLS FIRST В случаях когда Ordering_Field__c имеет одинаковое значение, отличное от NULL - упорядочивание будет одинаковым, в противном случае упорядочивание подчинится Id соответствующего объекта.[/quote] все так и есть, просто проблема вкрадывается [i]неявно[/i], так как в качестве "дополнительного" поля для упорядочивания система выбирает ID Child__c в одном случае и ID Parent_2__c во втором. пришлось указать явно, я выбрал Parent_2__r.Name и Name соответсвенно
еще такая идея про страницу с ReadOnly(для увеличения лимита) и необходимостью все-таки выполнять ДМЛ операции.
первая идея была разделить страницу на ReadOnly страницу и еще одну выполняющую ДМЛ.
а что если ДМЛ операции вызывать с фронт-энда ReadOnly страницы через REST api? AJAX Toolkit? RemoteAction?
еще такая идея про страницу с ReadOnly(для увеличения лимита) и необходимостью все-таки выполнять ДМЛ операции. первая идея была разделить страницу на ReadOnly страницу и еще одну выполняющую ДМЛ. а что если ДМЛ операции вызывать с фронт-энда ReadOnly страницы через REST api? AJAX Toolkit? RemoteAction?
а что если ДМЛ операции вызывать с фронт-энда ReadOnly страницы через REST api? AJAX Toolkit? RemoteAction?
Вызывайте, вам никто не мешает это делать.
[quote="Den Brown"]а что если ДМЛ операции вызывать с фронт-энда ReadOnly страницы через REST api? AJAX Toolkit? RemoteAction?[/quote] Вызывайте, вам никто не мешает это делать.