Hi guys,
I'm currently trying to join two objects in a same query or result.
My question is if it's possible to show or debug the sum of FIELD A FROM LEAD + sum of FIELD B FROM two different Objects.
Here's an example I'm working on:
Btw I really appreciate your time and comments, and if i'm making a mistake pls let me know, thank you.
public static void example() {
String sQueryOne;
String sQueryTwo;
AggregateResult[] objOne;
AggregateResult[] objTwo;
//I tried to save the following querys into a sObject List
List<SObject> bothObjects = new List<SObject>();
sQueryOne = 'Select Count(Id) records, Sum(FieldA) fieldNA From Lead';
objOne = Database.query(sQueryOne);
sQueryTwo = 'Select Count(Id) records, Sum(FieldA) fieldNB From Opportunity';
objTwo = Database.query(sQueryTwo);
bothObjects.addAll(objOne);
bothObjects.addAll(objTwo);
for(sObject totalRec : bothObjects) {
//There's a Wrapper(className) I created which contains some variables(totalSum)
className finalRes = new className();
finalRes.totalSum = (Integer.valueOf(fieldNA)) + (Integer.valueOf(fieldNB));
System.debug('The sum is: '+finalRes.totalSum);
For example if I call a System debug with the previous variable finalRes.totalSum it's just showing the first value(fieldNA) duplicated.
The following debug shows the current values of the sObject List which I want to sum for example FIELD0 = from leads, FIELD0 = from Opportunities.
}
}
You access the columns in AggregateResult by calling get('columnAlias'). If you didn't specify an alias they'll be autonumbered by SF as expr0, expr1... When in doubt you can always go System.debug(results);
Some more info: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_SOQL_agg_fns.htm
This might give you some ideas:
List<AggregateResult> results = new List<AggregateResult>{
[SELECT COUNT(Id) records, SUM(NumberOfEmployees) fieldA, SUM(AnnualRevenue) fieldB FROM Account],
[SELECT COUNT(Id) records, SUM(Amount) fieldA, SUM(TotalOpportunityQuantity) fieldB FROM Opportunity],
[SELECT COUNT(Id) records, SUM(NumberOfEmployees) fieldA, SUM(AnnualRevenue) fieldB FROM Lead]
/* hey, not my fault these are only 2 standard numeric fields on Lead.
It doesn't matter that they're identical to Account fields, what matters is what their SUM(...) aliases are
*/
};
List<Decimal> totals = new List<Decimal>{0,0,0};
for(AggregateResult ar : results){
totals[0] += (Decimal) ar.get('records');
totals[1] += (Decimal) ar.get('fieldA');
totals[2] += (Decimal) ar.get('fieldB');
}
System.debug(totals); // (636, 8875206.0, 9819762558.0) in my dev org
(I'm not saying it's perfect, your wrapper class sounds like better idea or maybe even Map<String, Decimal>. Depends what are you going to do with the results)
Related
My requirement is to process or build some logic around the result of sql query in flink. For simplicity lets say I have two sql query they are running on different window size and one event stream. My question is
a) how I will know for which query result is this
b) how I will know how many rows are the result of executed query? I need this info as I have to build a notification message with list of event those are part of the query result.
DataStream<Event> ds = ...
String query = "select id, key" +
" from eventTable GROUP BY TUMBLE(rowTime, INTERVAL '10' SECOND), id, key ";
String query1 = "select id, key" +
" from eventTable GROUP BY TUMBLE(rowTime, INTERVAL '1' DAY), id, key ";
List<String> list = new ArrayList<>();
list.add(query);
list.add(query1);
tabEnv.createTemporaryView("eventTable", ds, $("id"), $("timeLong"), $("key"),$("rowTime").rowtime());
for(int i =0; i< list.size(); i++ ){
Table result = tabEnv.sqlQuery(list.get(i));
DataStream<Tuple2<Boolean, Row>> dsRow = tabEnv.toRetractStream(result, Row.class);
dsRow.process(new ProcessFunction<Tuple2<Boolean, Row>, Object>() {
List<Row> listRow = new ArrayList<>();
#Override
public void processElement(Tuple2<Boolean, Row> booleanRowTuple2, Context context, Collector<Object> collector) throws Exception {
listRow.add(booleanRowTuple2.f1);
}
});
}
Appreciate your help. thanks Ashutosh
To sort out which results are from which query, you could include an identifier for each query in the queries themselves, e.g.,
SELECT '10sec', id, key FROM eventTable GROUP BY TUMBLE(rowTime, INTERVAL '10' SECOND), id, key
Determining the number of rows in the result table is trickier. One issue is that there is no final answer to the number of results from a streaming query. But where you are processing the results, it seems like you could count the number of rows.
Or, and I haven't tried this, but maybe you could use something like row_number() over(order by tumble_rowtime(rowTime, interval '10' second)) to annotate each row of the result with a counter.
I am kind of new to CPQ and have been trying to create a Quote Line Item with no success.
Opportunity OpportunityOne = [SELECT Id, PriceBook2Id FROM Opportunity WHERE
Id='0067F000008qOJzQAM' LIMIT 1];
Quote QuoteOne = [SELECT Id FROM Quote WHERE Name =:'TestQuote' LIMIT 1];
Product2 Product = [SELECT Id FROM Product2 WHERE Id
=: '01t7F0000032Y89QAE' LIMIT 1];
System.debug('Opportunity: '+OpportunityOne);
System.debug('Quote:'+QuoteOne);
System.debug('Product2:'+Product);
QuoteLineItem QuoteLineItemOne = new QuoteLineItem();
QuoteLineItemOne.QuoteId = QuoteOne.Id;
QuoteLineItemOne.Quantity = 100;
QuoteLineItemOne.UnitPrice = 2000;
QuoteLineItemOne.Product2Id = Product.Id;
insert QuoteLineItemOne;
Why does it keep saying price book entry is missing. It is annoying. Insert fails and I see no object requiring a price book entry field.
Please help me with this.
You'll need to roll the apex API version back to version 38 or earlier in order to create quote lines this way (using product2Id field). As you can see in the object reference from version 39 onwards you'll need to populate the pricebookentryId field instead and leave product2Id null. To get the pricebookEntryId you'll just need an extra query eg.
PriceBookEntry pbe = [SELECT id FROM PricebookEntry
WHERE Product2Id =: Product.id AND Pricebook2Id =: OpportunityOne.pricebook2Id LIMIT 1];
Then just set the pricebookentryId field on your quotelineitem instead of product2Id
I have a dynamic result from Dapper query that contains records like this:
{DapperRow, billing_currency_code = 'USD', count(*) = '6'}
I'm able to access 'USD' by using rowVariable.billing_currency_code
To get '6' value I tried rowVariable["count(*)"] and rowVariable.kv["count(*)"] and unfortunately nothing works...
I can't change the count(*) column name in my case
How to get the '6' value from the rowVariable of type DapperRow in such case?
If the column name genuinely is "count(*)", then you can cast the row to a dictionary:
var data = (IDictionary<string,object>)row;
object value = data["count(*)"];
For that to work (at least, in SQL Server), your query would need to be something like:
select count(*) as [count(*)]
However, in most cases the column doesn't have a name, in which case: fix your query ;p
Actually, I'd probably say fix your query anyway; the following would be much easier to work with:
select count(*) as [Count]
Suppose Your Data as below
var Details={DapperRow, billing_currency_code = 'USD', count(*) = '6'}
as The columns is coming dynamically
var firstRow= Details.FirstOrDefault();
To get the heading columns of the data
var Heading= ((IDictionary<string, object>)firstRow).Keys.ToArray();
To get the value of the data by using key
var details = ((IDictionary<string, object>)firstRow);
var vallues= details[Heading[0]];
I'm relatively new to Apex, but I have a question about a batch job that I am creating. I am trying to insert AccountTeamMember records based on my company's territory alignment. The code seems to be working fine, but with one flaw: it is only inserting 100 AccountTeamMember records per user (it should be closer to 400, as that is how many I have loaded in my dev sandbox). Does anyone know what I can do to get an AccountTeamMember record inserted for all accounts per user, rather than 100 of the ~400? Is it something to do with the query including parent-child relationships and governor limits since it is such an even number (100)?
Here is the relevant code:
//list to hold new account teams
List<AccountTeamMember> acctMembers = new List<AccountTeamMember>();
//list to hold new account sharing rules
List<AccountShare> acctSharingRules = new List<AccountShare>();
global Database.querylocator start(Database.BatchableContext BC){
String query = 'SELECT (SELECT User__c FROM Territory_Users__r), (SELECT Account__c FROM Territory_Accounts__r) FROM Territory_Master__c';
return Database.getQueryLocator(query);}
global void execute(Database.BatchableContext BC, List<sObject> scope){
for (sObject s : scope) {
Territory_Master__c tm = (Territory_Master__c) s;
Territory_User__c[] userList = tm.getSObjects('Territory_Users__r');
Territory_Account__c[] accountList = tm.getSObjects('Territory_Accounts__r');
if (userList != null && accountList != null){
for(Territory_User__c uu : userList){
for(Territory_Account__c aa: accountList){
AccountTeamMember addRecord = new AccountTeamMember();
addRecord.AccountId = aa.Account__c;
addRecord.TeamMemberRole = 'Sales Rep';
addRecord.UserId = uu.User__c;
acctMembers.add(addRecord);
AccountShare addSharing = new AccountShare();
addSharing.AccountId = aa.Account__c;
addSharing.OpportunityAccessLevel = 'Read';
addSharing.CaseAccessLevel = 'Read';
addSharing.AccountAccessLevel = 'Edit';
addSharing.UserOrGroupId = uu.User__c;
acctSharingRules.add(addSharing);
}
}
}
}
//DML
if(acctMembers.size() > 0){
insert acctMembers;
}
if(acctSharingRules.size() > 0){
insert acctSharingRules;
}
}
Thanks,
Trey
FYI: This is the final result based on the answer to the question:
global Database.querylocator start(Database.BatchableContext BC){
String query = 'SELECT Id FROM Territory_Master__c';
return Database.getQueryLocator(query);}
global void execute(Database.BatchableContext BC, List<sObject> scope){
for(sObject s : scope){
Territory_Master__c tm = (Territory_Master__c) s;
List<Territory_User__c> userList = [SELECT User__c FROM Territory_User__c WHERE Territory_Master__c = :tm.Id];
List<Territory_Account__c> accountList = [SELECT Account__c FROM Territory_Account__c WHERE Territory_Master__c = :tm.Id];
if (userList != null && accountList != null){
for(Territory_User__c uu : userList){
for(Territory_Account__c aa: accountList){
AccountTeamMember addRecord = new AccountTeamMember();
addRecord.AccountId = aa.Account__c;
addRecord.TeamMemberRole = 'Sales Rep';
addRecord.UserId = uu.User__c;
acctMembers.add(addRecord);
acctSharingRules.add(new AccountShare(
AccountId = aa.Account__c,
OpportunityAccessLevel = 'Read',
CaseAccessLevel = 'Read',
AccountAccessLevel = 'Edit',
UserOrGroupId = uu.User__c)
);
}
}
}
}
//DML
if(acctMembers.size() > 0){
insert acctMembers;
}
if(acctSharingRules.size() > 0){
insert acctSharingRules;
}
}
I suspect that your subqueries got limited and it's your job to make sure you've finished with this record before moving on to the next one.
http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_soql_relationships.htm
Subquery results are like regular query results in that you might need
to use queryMore() to retrieve all the records if there are many
children. For example, if you issue a query on accounts that includes
a subquery, your client application must handle results from the
subquery as well
(the example is for Java calling Salesforce so don't copy-paste the code, try to understand the concept. As far as I know Apex doesn't have this queryMore method, it's for integrations only)
Option 1
Funny enough, it should in theory work if you'd change the for loops to this:
for(Territory_User__c uu : tm.getSObjects('Territory_Users__r')){
for(Territory_Account__c aa: tm.getSObjects('Territory_Accounts__r'))
That's because for loops written in that way should automatically call their internal queryMore().
If it won't work (I haven't tested it), you'll have to make a bit more complex changes.
Option 2
Remove the subqueries from the main query, you'll have to put them in the execute(). Something like this:
for(sObject s : scope){
Territory_Master__c tm = (Territory_Master__c) s;
for(Territory_User__c uu : [SELECT User__c FROM Territory_User__c WHERE Territory_Master__c = :tm.Id]){
for(Territory_Account__c aa: [SELECT Account__c FROM Territory_Account__c WHERE Territory_Master__c = :tm.Id]){
acctMembers.add(new AccountTeamMember(
AccountId = aa.Account__c,
TeamMemberRole = 'Sales Rep',
UserId = uu.User__c)
);
acctSharingRules.add(new AccountShare(
AccountId = aa.Account__c,
OpportunityAccessLevel = 'Read',
CaseAccessLevel = 'Read',
AccountAccessLevel = 'Edit',
UserOrGroupId = uu.User__c)
);
}
}
}
Side notes
Is there a chance you'll hit a limit of 50K rows retrieved across all objects (Territory Master/User/Account? If that's the case you might have to limit the scope of your batch job (optional second parameter passed to the Database.executeBatch()).
This trick can make your script execute bit faster and use less statements (so you won't hit another governor limit):
acctSharingRules.add(new AccountShare(
AccountId = aa.Account__c,
OpportunityAccessLevel = 'Read',
CaseAccessLevel = 'Read',
AccountAccessLevel = 'Edit',
UserOrGroupId = uu.User__c)
);
There's nothing obviously wrong with your code there, so if I were you I'd try to narrow down the cause of the problem. The source of the problem is going to be one of:
Your query is only returning 100 items in accountList or userList or both
The insert can only handle 100 items at a time and is silently failing to insert the rest
The insert is failing for the items after 100
If you haven't already done so, you should familiarize yourself with using Debug Logs in Salesforce.com (Setup | Monitoring | Debug Logs). Turn them on, then run this code with some System.debug calls in there to figure out:
How many items are in userList and accountList?
If it's more than 100, when you go to insert the share rows, are some of those inserts failing? If you use Database.insert() instead of just the insert statement then you'll get a SaveResult[] back that will tell you for each row you attempted to insert whether it was successful or errored out.
So I can't say off the top of my head why this is failing, and I don't see any particular limit that should apply here, but the above should help you debug it at least.
In my app I display a list of the current users. I request it like this:
Attempt 1
List<User> Following = [SELECT Id, Name, SmallPhotoUrl
FROM User
WHERE Id IN (
SELECT ParentId
FROM EntitySubscription
WHERE SubscriberId = :UserInfo.getUserId())
AND Id != :UserInfo.getUserId()
LIMIT 96];
This does exactly what it's supposed to when logged in as an admin but I found out that non-admins get an error:
Implementation restriction: EntitySubscription only allows security
evaluation for non-admin users when LIMIT is specified and at most
1000
OK, no big deal, I'll just slap a LIMIT on there like so:
Attempt 2
List<User> Following = [SELECT Id, Name, SmallPhotoUrl
FROM User
WHERE Id IN (
SELECT ParentId
FROM EntitySubscription
WHERE SubscriberId = :UserInfo.getUserId()
LIMIT 1000)
AND Id != :UserInfo.getUserId()
LIMIT 96];
Easy, right? WRONG. This gives the following:
expecting a right parentheses, found 'LIMIT'
OK...
I then tried breaking it out like so:
Attempt 3
List<EntitySubscription> sub = [SELECT ParentId
FROM EntitySubscription
WHERE SubscriberId = :UserInfo.getUserId()
LIMIT 1000];
List<Id> ids = new List<Id>();
for(EntitySubscription s : sub){
ids.add(s.ParentId);
}
List<User> Following = [SELECT Id, Name, SmallPhotoUrl
FROM User
WHERE Id IN (:ids)
AND Id != :UserInfo.getUserId()
LIMIT 96];
I crossed my fingers and...
Invalid bind expression type of LIST<Id> for column of type Id
Hmm, I'd seen examples where this seemed to be possible, such as on the developerforce boards, so I'm at a bit of a loss now.
So here we are. I need to select a list of user names and pictures that a particular user is following on Chatter. If there is a completely different way to go about it I'm open to it.
Try removing the parenthesis around the bind, i.e. change:
WHERE Id IN (:ids)
to:
WHERE Id IN :ids
And also simplify the query by just making sure that your list of IDs doesn't contain the current user's ID:
List<EntitySubscription> sub = [SELECT ParentId
FROM EntitySubscription
WHERE SubscriberId = :UserInfo.getUserId() AND ParentId != :UserInfo.getUserId()
LIMIT 1000];
Set<Id> ids = new Set<Id>();
for(EntitySubscription s : sub){
ids.add(s.ParentId);
}
Hope that helps!