Контрольная сумма строки с использованием 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;