Эта проблема может быть решена в Java или с использованием Oracle SQL. Проблема сформулирована как:
Дана таблица со столбцом первичного ключа (pkCol) и некоторыми другими столбцами (здесь только colA), например:
pkCol colA
---- ----
a 2
b 2
c 3
Я хотел бы вставить новые значения в эту таблицу, чтобы иметь что-то похожее на:
pkCol colA
---- ----
a 2
b 2
c 3
a_1 2
b_1 2
c_1 3
Новые значения первичного ключа должны выглядеть так же, как и исходные значения, которые они скопировали, тогда как остальные столбцы должны оставаться такими же, как были.
Ограничения для новых значений:
1. новое значение, которое будет вставлено, должно быть уникальным для pkCol
2. новое значение должно иметь максимальную длину, разрешенную пределом 'pkCol'
(заданным структурой таблицы).
Я пытался эмулировать эту проблему в Java, создать HashSet со значениями pkCol, а затем создать HashMap, имеющий ключ, старое значение из pkCOl и фактическое значение для карты, новый вычисляемый pkCol. Метод для получения нового значения делает это: если у него есть длина, оставшаяся для добавления новых символов, он сначала пытается добавить '0', если полученное значение не является уникальным, тогда он пытается с '1', затем с '2', затем .. . с '01' и т. д. (алгоритм грубой силы для всех возможных комбинаций строк находится в ветви 'else'). Если у него недостаточно места для добавления значения, он просто заменит символ на другое, пока не найдет новое уникальное значение.
Java
int keysSize = 5000000;
Random rand = new Random();
HashSet<String> keys = Sets.newHashSet();
for(int i = 0; i < keysSize; i++){
keys.add(RandomStringUtils.randomAlphanumeric(1 + rand.nextInt(100)));
}
System.out.println("a");
long startTime = System.nanoTime();
Map<String, String> pkTransition = Maps.newHashMapWithExpectedSize(keysSize);
System.out.println("b");
for(String key : keys){
String newValue = generateNextString(keys, key, 100);
assertTrue(!newValue.isEmpty());
pkTransition.put(key, newValue);
}
long endTime = System.nanoTime();
long totalTime = endTime - startTime;
System.out.println(totalTime/1000000);
private String generateNextString(
HashSet<String> usedValues, String currentValue, int maxSize){
char[] possibleNewCharacters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
int currentValueLength = currentValue.length();
int lengthLeft = maxSize - currentValueLength;
char[] result = new char[possibleNewCharacters.length];
int[] index = new int[possibleNewCharacters.length];
Arrays.fill(result, 0, result.length, possibleNewCharacters[0]);
Arrays.fill(index, 0, index.length, 0);
if(lengthLeft == 0){
for(int currentLength = 1; currentLength < maxSize; currentLength++){
for( int length = 1; length <= possibleNewCharacters.length; length++ ) {
StringBuilder computedValue = new StringBuilder(currentValue);
computedValue.setCharAt(currentLength, possibleNewCharacters[length]);
if(!usedValues.contains(computedValue.toString())){
return computedValue.toString();
}
}
}
}else{
for( int length = 1; length <= possibleNewCharacters.length && length < lengthLeft; length++ ) {
int updateIndex = 0;
do {
String newValue = currentValue + new String(result, 0, length);
if(!usedValues.contains(newValue)){
return newValue;
}
for(updateIndex = length-1;
updateIndex != -1 && ++index[updateIndex] == possibleNewCharacters.length;
result[updateIndex] = possibleNewCharacters[0], index[updateIndex] = 0, updateIndex--);
if( updateIndex != -1 ) result[updateIndex] = possibleNewCharacters[index[updateIndex]];
}
while(updateIndex != -1);
}
}
for(int extraTries = 0 ; extraTries < 10; extraTries ++){
String newValue = RandomStringUtils.randomAlphanumeric(maxSize);
if(!usedValues.contains(newValue)){
return newValue;
}
}
return "";
}
Consider this case also
pkCol colA
---- ----
a 2
a_1 2
a_1_1 3
if you try to simply append '_1' you will fail. You need to check for unicitty before.
The problem with the Java approach is that for 5 million records, it works in under 8 seconds. But when I try with 50 million it hangs because it won't have enough space to initialize the HashSet and the HashMap.
The idea is to be able to work with ~500 million records.
(Edited) For the case with 50 million records, the code will never print System.out.println("a");
My machine has 32GB RAM.