Мне нужно написать компонент, который воссоздает таблицы SQL Server (структуру и данные) в базе данных Oracle. Этот компонент также должен принимать новые данные, введенные в базу данных Oracle, и копировать их обратно в SQL Server.
Преобразование типов данных из SQL Server в Oracle не проблема. Однако критическая разница между Oracle и SQL Server вызывает большую головную боль. SQL Server считает, что пустая строка ("") отличается от значения NULL, поэтому столбец char может быть определен как NOT NULL и при этом по-прежнему содержать пустые строки в данных.
Oracle считает, что пустая строка совпадает со значением NULL, поэтому, если столбец char определен как NOT NULL, вы не можете вставить пустую строку. Это приводит к тому, что мой компонент ломается всякий раз, когда столбец символов NOT NULL содержит пустую строку в исходных данных SQL Server.
До сих пор мое решение заключалось в том, чтобы не использовать NOT NULL ни в одном из моих определений зеркальных таблиц Oracle, но мне нужно более надежное решение. Это должно быть кодовое решение, поэтому ответ не может быть «используйте такой-то продукт SQL2Oracle».
Как бы вы решили эту проблему?
Обновлено: вот единственное решение, которое я придумал до сих пор, и оно может помочь проиллюстрировать проблему. Поскольку Oracle не допускает "" в столбце NOT NULL, мой компонент может перехватить любое такое значение, поступающее от SQL Server, и заменить его на "@" (только для примера).
Когда я добавляю новую запись в свою таблицу Oracle, мой код должен писать «@», если я действительно хочу вставить «», и когда мой код копирует новую строку обратно в SQL Server, он должен перехватить «@» и вместо этого напишите "".
Я надеюсь, что есть более элегантный способ.
Изменить 2: возможно ли, что есть более простое решение, например, какой-то параметр в Oracle, который заставляет его обрабатывать пустые строки так же, как и все другие основные базы данных? И будет ли этот параметр также доступен в Oracle Lite?
@WW .: Вы совершенно правы - это ужасное решение.
Святая корова. В SQL Server есть своя доля глупости, но это полная чушь. (Это все еще с Oracle 2014+ и добавлен ли для него параметр «ANSI NULL»? У SQL Server были некоторые проблемы, подобные этой обработке до «ANSI NULL».)
@ user2864740 - Я не думаю, что с тех пор он изменился. Документ Oracle 12c R1 (docs.oracle.com/database/121/SQLRF/…) по-прежнему утверждает следующее: «Oracle Database в настоящее время обрабатывает символьное значение нулевой длины как нулевое. Однако это может измениться в будущих выпусках, и Oracle рекомендует не обрабатывать пустые строки так же, как нули»., та же фраза, по крайней мере, из Oracle 8 - :)
@SimonMourier Весело, что они говорят вам относиться к ним по-другому, но не могут предоставить инструменты для этого разумным способом ... и теперь им, вероятно, будет трудно изменить это из-за обратной совместимости, поскольку много dbs используют хаки для решения этой проблемы, которая, вероятно, сломается, когда она будет исправлена ...





Нужно ли разрешать пустые строки в системе SQL Server? Если вы можете добавить в систему SQL Server ограничение, запрещающее пустые строки, это, вероятно, самое простое решение.
Если бы. База данных SQL Server не находится под моим контролем.
Я не вижу для этого простого решения.
Возможно, вы можете сохранить свои значения как один или несколько пробелов -> ' ', которые не являются NULL в Oracle, или отслеживать этот особый случай с помощью дополнительных полей / таблиц и уровня адаптера.
Если вы переносите данные, возможно, вам придется заменить пустую строку пробелом. Не очень элегантно, но работоспособно. Это неприятная «особенность» Oracle.
Я хотел озаглавить свой вопрос: «С какой стати Oracle приравнивает пустые строки к нулям?»
Поразмыслив над этим несколько лет, я думаю, что Oracle все правильно понял. SQL Server определяет NULL с точки зрения программиста: для программиста пустая строка по-прежнему является что-нибудь и очень отличается от нулевой переменной в большинстве языков программирования. Oracle определяет его с точки зрения конечного пользователя (представленного в данном случае разработчиком базы данных); если, как разработчик БД, я указываю столбец как NOT NULL, я делаю это так, чтобы пользователи (в том числе программисты) помещали туда реальное значение для каждой строки. Я не могу придумать ни одного примера стола, который я когда-либо создавал там, где мне хотелось бы ...
... люди помещают пустые строки (например, почтовый индекс, номер социального страхования и т. д.) в мое НЕ NULL поле. Не думаю, что я даже знал, что SQL Server позволяет пропускает поля NOT NULL, пока я не прочитал этот вопрос. TL; DR: взлом SQL Server позволяет программистам обойти намерения дизайнеров баз данных.
Моим типичным решением было бы добавить ограничение в SQL Server, заставляющее все строковые значения в затронутых столбцах иметь длину больше 0:
CREATE TABLE Example (StringColumn VARCHAR(10) NOT NULL)
ALTER TABLE Example
ADD CONSTRAINT CK_Example_StringColumn CHECK (LEN(StringColumn) > 0)
Однако, как вы заявили, у вас нет контроля над базой данных SQL. Таким образом, у вас действительно есть четыре варианта (как я это вижу):
Я бы предпочел номер четыре, но это не всегда возможно. Действия, которые вы предпримете, действительно будут зависеть от того, что нужно пользователям оракула. В конце концов, если с базой данных SQL ничего нельзя сделать, я объясню проблему владельцам бизнес-систем Oracle, объясню варианты и последствия и заставлю их принять решение :)
ПРИМЕЧАНИЕ: Я считаю, что в этом случае SQL Server действительно демонстрирует «правильное» поведение.
Это неприятно и может иметь неожиданные побочные эффекты ... но вы можете просто вставить "chr (0)" вместо "".
drop table x
drop table x succeeded.
create table x ( id number, my_varchar varchar2(10))
create table succeeded.
insert into x values (1, chr(0))
1 rows inserted
insert into x values (2, null)
1 rows inserted
select id,length(my_varchar) from x
ID LENGTH(MY_VARCHAR)
---------------------- ----------------------
1 1
2
2 rows selected
select * from x where my_varchar is not null
ID MY_VARCHAR
---------------------- ----------
1
Это «слишком противно». Это занимает крайнюю строчку с одним пробелом.
NOT NULL - это ограничение базы данных, используемое для прекращения размещения недопустимых данных в вашей базе данных. Это не служит какой-либо цели в вашей базе данных Oracle, поэтому у меня этого не будет.
Я думаю, вам следует просто продолжать разрешать NULLS в любом столбце Oracle, который отражает столбец SqlServer, который, как известно, содержит пустые строки.
Если в базе данных SqlServer есть логическая разница между NULL и пустой строкой, вам понадобится что-то дополнительное для моделирования этой разницы в Oracle.
Думаю, это единственный разумный ответ здесь. В основном, если разработчики базы данных sql server хранят пустые строки в столбцах NOT NULL, тогда эти столбцы должны иметь значение NULL в Oracle db. Итак, MusiGenesis с самого начала имел правильный ответ, и ему не нужно было ничего делать для решения проблемы.
Для тех, кто думает, что Null и пустая строка должны рассматриваться как одно и то же. Значение NULL отличается от значения пустой строки. Он фиксирует разницу между «неопределенным» и «заведомо пустым». Например, запись может быть создана автоматически, но никогда не подтверждена пользовательским вводом, и, таким образом, получить «ноль» в ожидании того, что, когда пользователь подтвердит ее, она будет установлена как пустая. На практике мы можем не захотеть запускать логику для нулевого значения, но можем захотеть сделать это для пустой строки. Это аналогично случаю для флажка с тремя состояниями: Да / Нет / Не определено.
И в SQL, и в Oracle это не совсем правильно. Пробел не должен удовлетворять ограничению «not null», и есть необходимость, чтобы пустая строка обрабатывалась иначе, чем обрабатывается пустая строка.
Некоторое время назад я написал в своем блоге объяснение того, как Oracle обрабатывает нулевые значения. Проверьте это здесь: http://www.psinke.nl/blog/hello-world/ и дайте мне знать, если у вас возникнут еще вопросы. Если у вас есть данные из источника с пустыми значениями, и вы должны преобразовать их в базу данных Oracle, где столбцы НЕ ПУСТОЙ, вы можете сделать 2 вещи:
Что ж, главный момент, который я бы рассмотрел, - это отсутствие задач, когда какое-то поле может быть нулевым, это же поле может быть пустой строкой, а бизнес-логика требует различать эти значения. Поэтому я бы сделал такую логику:
Если оба верны, сделайте столбец Oracle НЕ ПУСТОЙ. Если любое из них истинно, сделайте столбец Oracle NULL. Если ни один из них не является истиной, вызовите исключение INVALID DESIGN (или, возможно, проигнорируйте его, если это приемлемо для этого приложения).
При отправке данных из MSSQL в Oracle просто ничего особенного не делайте, все данные будут переданы правильно. При получении данных в MSSQL любые ненулевые данные следует отправлять как есть. Для нулевых строк вы должны решить, следует ли их вставлять как пустую или как пустую строку. Для этого вам следует еще раз проверить структуру таблицы (или вспомнить предыдущий результат) и посмотреть, не установлено ли ограничение NOT NULL. Если есть - используйте пустую строку, если нет - используйте NULL. Просто и умно.
Иногда, если вы работаете с неизвестным и непредсказуемым приложением, вы не можете проверить наличие ограничения {not empty string} из-за его различных форм. Если это так, вы можете либо использовать упрощенную логику (сделать столбцы Oracle всегда допускающими значение NULL), либо проверить, можете ли вы вставить пустую строку в таблицу MSSQL без ошибок.
Я бы пошел с дополнительной колонкой на стороне оракула. Пусть ваш столбец допускает значения NULL и имеет второй столбец, который определяет, должна ли сторона SQL-сервера получать нулевое значение или пустую строку для строки.
Хотя, по большей части, я согласен с большинством других ответов (не буду вдаваться в споры о тех, с которыми я не согласен - не место для этого :))
Я заметил, что OP упомянул следующее:
«Oracle считает пустую строку такой же, как значение NULL, поэтому, если столбец char определен как NOT NULL, вы не можете вставить пустую строку».
В частности, вызов CHAR, а не VARCHAR2. Следовательно, говорить о «пустой строке» длины 0 (т.е. '') нецелесообразно. Если он объявил CHAR как, например, CHAR (5), то просто добавьте пробел к входящей пустой строке, Oracle все равно заполнит ее. В итоге вы получите строку из 5 пробелов.
Теперь, если OP означал VARCHAR2, ну да, это совершенно другой зверь, и да, разница между пустой строкой и NULL становится актуальной.
SQL> drop table junk;
Table dropped.
SQL>
SQL> create table junk ( c1 char(5) not null );
Table created.
SQL>
SQL> insert into junk values ( 'hi' );
1 row created.
SQL>
SQL> insert into junk values ( ' ' );
1 row created.
SQL>
SQL> insert into junk values ( '' );
insert into junk values ( '' )
*
ERROR at line 1:
ORA-01400: cannot insert NULL into ("GREGS"."JUNK"."C1")
SQL>
SQL> insert into junk values ( rpad('', 5, ' ') );
insert into junk values ( rpad('', 5, ' ') )
*
ERROR at line 1:
ORA-01400: cannot insert NULL into ("GREGS"."JUNK"."C1")
SQL>
SQL> declare
2 lv_in varchar2(5) := '';
3 begin
4 insert into junk values ( rpad(lv_in||' ', 5) );
5 end;
6 /
PL/SQL procedure successfully completed.
SQL>
Что касается вашего Edit 2: существует ужасное решение, связанное с использованием CLOB для всех этих столбцов. Oracle различает NULL и пустой CLOB. Вы можете сохранить CLOB в таблице, чтобы снизить влияние на производительность. Я бы не стал этого делать (см. Мой ответ ниже).