Вставьте новые значения в столбец первичного ключа, основываясь на существующих, сохраняя ограничение единственности и ограничение максимальной длины - PullRequest
0 голосов
/ 02 июля 2019

Эта проблема может быть решена в 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.

1 Ответ

3 голосов
/ 02 июля 2019

Не делай этого.

Цель первичных ключей (и ключей в целом) - обеспечить уникальность строк. Это внутренние идентификаторы, которые не должны быть сексуальными или красивыми.

Если вы хотите что-то подобное, это означает, что вы хотите раскрыть это в какой-то момент. Если вам это действительно нужно, создайте дополнительный столбец с нужным форматированием.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...