Контрольная сумма строки с использованием ora_hash дает разные результаты для разных пользователей

Попытка сгенерировать уникальную контрольную сумму для строки данных в таблице Oracle, чтобы убедиться, что строка не изменилась между двумя пользователями, которые их извлекают, и пытается обновить их одновременно.

 SELECT ora_hash( KY_REFUND_ID CD_STATUS || KY_CHECK_NUM || 
        COMMENT || CREATED_BY || TS_CREATED || TX_UPDATED_BY || TS_UPDATED) as checksum
 INTO p_checksum
 FROM REFUND_CHECKS r
 WHERE ROWID = p_rowid;

Странно то, что мы получаем другую контрольную сумму, если процедура вызывается в sqldeveloper при отладке, в отличие от вызова через веб-сайт. Это становится проблемой, когда контрольная сумма снова вычисляется внутри, чтобы сравнить с моим значением - у меня получилось 12345, но внутренне те же самые данные дают 78904, поэтому система говорит, что они не совпадают.

Насколько я могу видеть, единственный способ, когда два разных пользователя получают разные контрольные суммы при просмотре одних и тех же данных... это то, что они не смотрят на одни и те же данные. Я подозреваю, что между этими двумя вызовами есть нечто невидимое. И единственное, что я вижу, отличается тем, что используется учетная запись.

В sqldeveloper вызов выполняется с использованием имени схемы, но веб-сайт вызывает как dotnet_user.

Случайно ли имя учетной записи используется как дополнительное значение в математике при определении контрольной суммы? Если нет, то какие еще могут быть невидимые различия, которые могут привести к различным результатам, и, что более важно, как они могут быть стандартизированы, чтобы обе стороны получили одинаковый результат?

1 ответ

Решение

Проблема в том, что вы полагаетесь на неявное преобразование. Вы генерируете строку, объединяя несколько значений вместе, что означает, что столбцы меток времени (предположительно, из имен) неявно преобразуются в строки с использованием настроек NLS сеанса. И у вас есть разные настройки NLS в SQL Developer и через ваш веб-клиент.

В качестве простой демонстрации:

alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS';

select ora_hash(timestamp '2018-01-01 00:00:00') as ts_hash,
       'abc' || timestamp '2018-01-01 00:00:00' as str,
       ora_hash('abc' || timestamp '2018-01-01 00:00:00') as str_hash
from dual;

   TS_HASH STR                      STR_HASH
---------- ---------------------- ----------
1986439397 abc2018-01-01 00:00:00  588765268

alter session set nls_timestamp_format = 'DD-Mon-YYYY HH:MI:SS AM';

select ora_hash(timestamp '2018-01-01 00:00:00') as ts_hash,
       'abc' || timestamp '2018-01-01 00:00:00' as str,
       ora_hash('abc' || timestamp '2018-01-01 00:00:00') as str_hash
from dual;

   TS_HASH STR                          STR_HASH
---------- -------------------------- ----------
1986439397 abc01-Jan-2018 12:00:00 AM 2809284723

То же значение метки времени и тот же хэш самой метки времени в его собственном типе данных; но разные неявные преобразования в строки и разные хеши этих строк.

Измените свой код, чтобы явно преобразовать временные метки в определенный фиксированный формат, и он больше не будет полагаться на NLS и поэтому станет согласованным, например,

 SELECT ora_hash( KY_REFUND_ID CD_STATUS || KY_CHECK_NUM || 
          COMMENT || CREATED_BY ||
          to_char(TS_CREATED, 'SYYYYMMDDHH24MISSFF9') || TX_UPDATED_BY ||
          to_char(TS_UPDATED, 'SYYYYMMDDHH24MISSFF9')) as checksum
 INTO p_checksum
 FROM REFUND_CHECKS r
 WHERE ROWID = p_rowid;
Другие вопросы по тегам