Does anyone have an idea of the distance between two Salesforce Ids of the same object and instance?
Example Account Id:
Object-Instance-Reserved-This Distance
001-30-0-0000xxxxx
Does anyone have an idea of the distance between two Salesforce Ids of the same object and instance?
Example Account Id:
Object-Instance-Reserved-This Distance
001-30-0-0000xxxxx
As per What are Salesforce ID's composed of?, the Ids are base-62 encoded. If you convert the base62 representation to a decimal representation then calculating the decimal "distance" between the two records would be easy enough.
Lets create a few test records to get a feel for how they run in a sequence:
List<Account> accountsToCreate = new List<Account>();
for(Integer i = 0; i < 62 * 4; i++) {
accountsToCreate.add(new Account(Name = 'Account:' + i));
}
insert accountsToCreate;
for(Account acc : accountsToCreate) {
System.debug(acc.Id);
}
delete accountsToCreate;
An abbreviated version of the output:
0017000001WXV8y
0017000001WXV8z
0017000001WXV90
0017000001WXV91
0017000001WXV92
... 3 to 8
0017000001WXV99
0017000001WXV9A
... B to X
0017000001WXV9Y
0017000001WXV9Z
0017000001WXV9a
0017000001WXV9b
... c to x
0017000001WXV9y
0017000001WXV9z
0017000001WXVA0
0017000001WXVA1
0017000001WXVA2
... 3 to 8
0017000001WXVA9
0017000001WXVAA
... B to Y
0017000001WXVAZ
0017000001WXVAa
... b to x
0017000001WXVAy
0017000001WXVAz
0017000001WXVB0
0017000001WXVB1
... 2 to 8
0017000001WXVB9
0017000001WXVBA
... B to X
0017000001WXVBY
0017000001WXVBZ
0017000001WXVBa
... b to x
0017000001WXVBy
0017000001WXVBz
0017000001WXVC0
...
0017000001WXVC1
...
The sequence is clear enough, run through 0
to 9
, then A
to Z
, then finally a
to z
before wrapping round and increment the higher characters.
What we now need is a way to do base62 decoding in Apex. Turns out I did something similar way back in 2011 except it was in T-SQL rather than Apex.
public class IdDistance {
// This is the order ID's were assigned in when tested
final static string base62Chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
public static long numericDistance(Id firstId, Id secondId) {
long firstNumericId = numericId(firstId);
long secondNumericId = numericId(secondId);
return secondNumericId - firstNumericId;
}
public static long numericId(Id input) {
return numericId((string)input);
}
public static long numericId(string input) {
string idAsString = (string)input;
if(idAsString.length() > 15) {
// Drop the case checking suffix for the last 3 characters of an 18 char ID.
idAsString = idAsString.substring(0, 15);
}
long returnValue = 0;
long multiplier = 1;
for(integer i = idAsString.length(); i > 1; i--) {
// The character being converted
string idChar = idAsString.substring(i-1, i);
System.debug(idChar);
// The index of the character being converted
long value = base62Chars.indexOf(idChar);
returnValue = returnValue + ( value * multiplier );
multiplier = multiplier * 62;
}
return returnValue;
}
}
Test class. Add more are required.
@IsTest
public class IdDistance_Test {
@IsTest
public static void testValues() {
System.assertEquals(0, IdDistance.numericId('000'));
System.assertEquals(1, IdDistance.numericId('001'));
System.assertEquals(10, IdDistance.numericId('00A'));
System.assertEquals(35, IdDistance.numericId('00Z'));
System.assertEquals(36, IdDistance.numericId('00a'));
System.assertEquals(61, IdDistance.numericId('00z'));
System.assertEquals(62, IdDistance.numericId('010'));
Id testId = '00Q7000001DsqIj';
System.assertEquals(911562501854070361L, IdDistance.numericId(testId));
}
@IsTest
public static void distance() {
System.assertEquals(1, IdDistance.numericDistance('00Q7000001DsqIj', '00Q7000001DsqIk'));
System.assertEquals(10, IdDistance.numericDistance('00Q7000001DsqI1', '00Q7000001DsqIB'));
}
}
Sample output:
'0' > 0
'1' > 1
'A' > 10
'Z' > 35
'a' > 36
'z' > 61
'10' > 62
Remember to truncate off any extra characters after the first 15, as we don't want the case checking suffix. Special consideration might also be required for Orgs that have gone through a pod migration.
Of course, even though you can now do this, the question still remains of why you would want to do this.