Как скопировать уникальное ограничение в Oracle с помощью SQLAlchemy?

У меня есть таблица (над которой у меня нет контроля), которую я должен скопировать. Целевая схема может быть такой же, как исходная, поэтому все индексы и ограничения должны быть определены без имени, неявно.

Я использую Python 3.4.3 с SQLAlchemy 1.0.8 и cx_oracle 5.2.

Таблица такая:

CREATE TABLE "MY_TABLE" 
(   "ITEMID" NUMBER(*,0) NOT NULL ENABLE, 
    "LABEL" NVARCHAR2(80) NOT NULL ENABLE, 
    "FIRSTCHILDID" NUMBER(*,0) NOT NULL ENABLE, 
    "LASTCHILDID" NUMBER(*,0) NOT NULL ENABLE, 
    "DEFAULTPARENTID" NUMBER(*,0) NOT NULL ENABLE, 
    "PICTUREID" NUMBER(6,0) NOT NULL ENABLE, 
    "SECURITYID" NUMBER(*,0) NOT NULL ENABLE, 
    PRIMARY KEY ("ITEMID")
    UNIQUE ("LABEL"));

Код, который я использую, находится по адресу https://gist.github.com/toyg/9fb541ff3dbc8c175329. но суть его в следующем (smeta и dmeta являются исходными и целевыми метаданными, связанными):

table = Table(table_name, smeta, autoload=True)
target_name = prefix + str(table.name)
target_table = table.tometadata(dmeta, name=target_name)
for constraint in target_table.constraints:
    constraint.name = None
target_table.metadata.create_all(dengine)

Это терпит неудачу с этой ошибкой:

sqlalchemy.exc.DatabaseError: (cx_Oracle.DatabaseError) 
ORA-00955: name is already used by an existing object 
[SQL: b'CREATE UNIQUE INDEX sys_c009016 ON "TMP_MY_TABLE" (label)']

Это связано с тем, что SQLAlchemy пытается создать уникальный индекс после создания таблицы, когда уже слишком поздно: CREATE INDEX требует имя, поэтому SA использует то же имя, что и существующее, и это не удается.

Я попытался установить имя индекса в None перед созданием, чтобы дать SA подсказку, но это приводит к ошибкам, потому что он всегда ожидает там строку.

Есть ли способ сказать SA, чтобы он просто сразу же добавил чертову фразу UNIQUE в таблицу DDL?


person Giacomo Lacava    schedule 13.08.2015    source источник
comment
Вы пытаетесь клонировать данные? Если схема та же   -  person Dmitry.Samborskyi    schedule 13.08.2015
comment
Да, мне нужно скопировать всю таблицу. Схема может быть, а может и не быть одинаковой; проблема, очевидно, возникает, когда она есть, но конфликты имен могут происходить даже в разных схемах.   -  person Giacomo Lacava    schedule 13.08.2015
comment
вы пробовали что-то вроде constraint.name = prefix + constraint.name? В этом случае у вас будут имена для ограничений, и они будут отличаться от существующих ограничений.   -  person pavel_form    schedule 13.08.2015
comment
извините, я имел в виду установку имени индекса   -  person pavel_form    schedule 13.08.2015
comment
Спасибо @pavel_form, я об этом не подумал. Это все еще немного обходной путь, но если я не найду лучшего способа, я пойду с ним.   -  person Giacomo Lacava    schedule 13.08.2015


Ответы (1)


«УНИКАЛЬНЫЙ ИНДЕКС» означает, что используется конструкция Index. Его DDL не генерируется в CREATE TABLE. Похоже, вы ищете конструкцию UniqueConstraint. Кажется вероятным, что в этом случае Oracle возвращает отраженную информацию о том, что вы сначала создали как объект UniqueConstraint, как объект Index с уникальным=True (эти конструкции «разные», но на многих бэкендах они синонимичны и/или смешаны и сопоставлены). а иногда даже зеркально, это совершенно сбивает с толку).

в конце концов, если вы хотите использовать ключевое слово UNIQUE в качестве встроенного ограничения, вам нужно использовать объект UniqueConstraint, и вам нужно будет удалить этот Index из таблицы - возможно, вам удастся обойтись table.indexes.remove(index). Объект Index не будет в table.constraints. Вероятно, вы захотите сделать свою «копию» таблицы более программным способом, а не использовать tometadata(). Попробуйте, возможно, использовать интерфейс проверки напрямую и просто создайте из него Table, который вы хотите.

person zzzeek    schedule 14.08.2015