Страницы

Поиск по вопросам

Показаны сообщения с ярлыком constraints. Показать все сообщения
Показаны сообщения с ярлыком constraints. Показать все сообщения

суббота, 7 марта 2020 г.

Что означает CONSTRAINT в данном контексте?

#sql #postgresql #constraints


Есть 2 таблицы :

CREATE TABLE company(
    id integer NOT NULL,
    name character varying,
    CONSTRAINT company_pkey PRIMARY KEY (id)
);

CREATE TABLE person(
    id integer NOT NULL,
    name character varying,
    company_id integer,
    CONSTRAINT person_pkey PRIMARY KEY (id)
);


Скрипт писал не я, и пытаюсь понять логику человека который это делал. В чем может
быть смысл этого ограничения CONSTRAINT person_pkey PRIMARY KEY (id)? И во втором случае
аналогично. Это ограничение что id может быть только первичным ключем ? А зачем это
может быть нужно? Тем более еще и именовать это ограничение. Помогите пожалуйста разобраться
зачем делать такой CONSTRAINT?
    


Ответы

Ответ 1



Определение CONSTRAINT company_pkey PRIMARY KEY (id) эквивалентно определению PRIMARY KEY (id) и означает, что id является первичным ключом таблицы. Т.к. в данном случае первичный ключ состоит из одного столбца, то его можно было бы указать на уровне поля: CREATE TABLE company( id integer PRIMARY KEY, name character varying ); Возможность определения ключа на уровне таблицы полезна если ключ — составной PRIMARY KEY (id, name) В первом случае у ограничения задано имя. Это имя будет выводиться в сообщениях об ошибках. Также по имени можно это ограничение удалить. В случае если имя ограничения не задано явно, оно будет сгенерировано СУБД. Это сказано в документации: CONSTRAINT constraint_name An optional name for a column or table constraint. If the constraint is violated, the constraint name is present in error messages, so constraint names like col must be positive can be used to communicate helpful constraint information to client applications. (Double-quotes are needed to specify constraint names that contain spaces.) If a constraint name is not specified, the system generates a name.

вторник, 28 января 2020 г.

Ограничения на параметр шаблона

#cpp #шаблоны_с++ #constraints


Есть шаблонный класс массива

template class Array;


в котором реализована функция сортировки, которая требует наличия у параметра шаблона
функции CompareTo(_Ty& other);
Так вот, как указать в определении шаблона что у параметра _Ty обязательно будет
функция ComareTo(_Ty& other).
Что то подобное реализовано в C#, но не знаю как и возможно ли такое проделать на плюсах?
    


Ответы

Ответ 1



Вот мой вариант: #include template class has_CompareTo_self { typedef char one[1]; typedef char two[2]; template struct type_check; template static one& test( type_check* ); template static two& test(...); public: enum { value = sizeof(test(0)) == sizeof(one) }; }; template class Array { static_assert(has_CompareTo_self<_Ty>::value, "no appropriate CompareTo found"); }; Преимущество этого варианта в том, что он проверяет сигнатуры. То есть, принимает такой класс: class Good { public: int CompareTo(Good& other); }; но не такой: class Bad { public: long CompareTo(Bad& other); }; Проверка: http://ideone.com/drYcz4 Давайте попробую объяснить, как эта шаблонная магия работает, чтобы мой вклад был больше, чем простая адаптация этого ответа. Главной идеей всех таких штук является принцип substitution failure is not an error (SFINAE): если при разворачивании шаблона получается несколько перегрузок функции, но одна из них не компилируется, то это не ошибка, а просто молча игнорируется. Сначала простое: one и two — две структуры данных гарантированно разного размера. Это нам понадобится в дальнейшем. Больше от этих структур ничего не требуется. Теперь, центральный компонент кода — объявление template static one& test( type_check* ); Оно скомпилируется лишь если type_check имеет смысл. Для этого у класса C как минимум должен быть метод CompareTo. Затем, у нас есть хитрый шаблон type_check, принимающий аргументами тип F, и константное выражение типа F! То есть для того, чтобы упоминание этого шаблона скомпилировалось, нужно, чтобы выражение, переданное ему вторым аргументом, имело такой тип, как в первом аргументе. В нашем случае это накладывает требование: &C::CompareTo должна иметь тип int (C::*)(C&) (то есть, функция-член, принимающая аргумент типа C& и возвращающая int). Итак, это самое центральное объявление скомпилируется тогда и только тогда, когда у класса C есть функция-член int C::CompareTo(C&) — то, что нам и надо! Как это работает дальше? У нас есть ещё одна перегрузка: template static two& test(...); которая компилируется всегда. Посмотрим на строку enum { value = sizeof(test(0)) == sizeof(one) }; Она объявляет внутренний enum с одним значением value. Правую часть (то есть, значение самого value) компилятор может вычислить, т. к. это константное выражение времени компиляции! Каков тип test(0)? Если центральная строка скомпилировалась, то 0 подходит в качестве указателя на структуру type_check<...>, и тип возвращаемого значения — one. Если нет, то работает вторая перегрузка, и тип возвращаемого значения — two. Различить эти два случая помогает сравнение размеров: sizeof(one) не равен sizeof(two). Таким образом, в зависимости от компилируемости центральной строки значение value — true или false. Вся эта механика спрятана внутри шаблона has_CompareTo_self, чтобы не засорять нормальным людям область видимости. Дальше всё просто: мы применяем static_assert в теле нашего шаблона Array. Конец! Да, я тоже считаю, что это неподдерживаемый код, и что метапрограммирование должно иметь менее вырвиглазный синтаксис.

Ответ 2



В C++11 можно сделать static_assert с std::is_base_of. Придется сделать базовый класс с методом ComareTo(_Ty& other); и проверять, является ли этот класс базовым классом для текущей спецификации шаблона.

Ответ 3



В определении шаблона - пока никак. В настоящий момент такой функциональности просто не существует. Она прорабатывается на уровне комитета, но не вошла в стандарт, пока. Существуют методы, с помощью которых можно это сделать. Но они далеки от идеала и используют довольно зубодробительный код. Но это не самое страшное, самое страшное, что они не являются общими и для каждой функции нужно писать свой код. Можете посмотреть на английском SO пример ответа с таким кодом.

Ответ 4



Установить ограничение на параметр шаблона просто так (из вредности) нельзя. Но зато можно проверить наличие у параметра шаблона определённой функции и в случае отсутствия таковой переключиться на отдельную специализацию шаблона. #include using namespace std; template class ensure_CompareTo { // используем SFINAE, чтобы определить наличие функции // когда функция есть, выбирается первый шаблон, когда нет - второй template static T test(char (*)[sizeof(&U::CompareTo)]); template static void test(...); public: // этот алиас зависит от того, какой шаблон был выбран с помощью SFINAE // он равен либо исходному типу, либо void using type = decltype(test(0)); }; // базовый шаблон массива // второй параметр выводится автоматически из первого // он либо совпадает с ним, когда требуемая функция есть, либо равен void template ::type> class Array { public: static const char* desc() { return "отсортированный массив"; } }; // специализация шаблона массива для случая, когда второй параметр, выводимый // автоматически из первого, оказался равным void, т.е. когда требуемой // функции в параметре шаблона не оказалось template class Array { public: static const char* desc() { return "просто массив"; } }; class A { public: int CompareTo(A& other); }; class B { }; int main() { cout << Array::desc() << endl; cout << Array::desc() << endl; return 0; }

Ответ 5



Не уверен, что это возможно. Но почему обязательно привязывать интерфейс сравнения к шаблону класса? Почему бы не сделать это как в Java или C#? Там есть параметризованный интерфейс Comparable (IComparable для C#), который реализуют классы, которые должны иметь возможность сравнения с экземплярами других классов. Таким образом вы получите даже большую гибкость, обеспечив возможность сравнения одновременно с несколькими типами данных: template class Comparable { public: virtual bool CompareTo(T* other) = 0; }; template class Array : Comparable >, Comparable { public: Array() {} bool CompareTo(Array* other) { return false; } bool CompareTo(int* other) { return false; } }; int main() { Array a; Array b; int c = 123; a.CompareTo(&b); a.CompareTo(&c); return 0; }

Ответ 6



Если мы хотим просто проверить наличие члена - достаточно его вызвать. Мы не хотим выполнять этот код, соответственно мы будем вызывать его в контексте который не приводит к выполнению кода, а именно sizeof, decltype или noexcept. Код, который будет выполняться, во всех случаях выглядит одинаково - создание объекта с помощью std::declval() и вызов метода: std::declval().CompareTo(std::declval()) Если надо проверить несколько свойств, можно использовать оператор "запятая": std::declval().f(), std::declval().g() При использовании decltype мы получаем тип, и его можно использовать как дополнительный параметр шаблона: template().CompareTo(std::declval()))> struct Array {}; Этому decltype можно сделать псевдоним (alias) template using has_CompareTo = decltype(std::declval().CompareTo(std::declval())); template> struct Array {}; Для sizeof и noexcept мы тоже можем использовать параметр шаблона: template().CompareTo(std::declval()), 0)> struct Array {}; , 0 на конце выражения нужно на случай если результат CompareTo имеет тип void. Также мы можем написать это как член класса: template struct Array { enum { has_CompareTo = noexcept(std::declval().CompareTo(std::declval()), 0) }; // или using has_CompareTo = decltype(std::declval().CompareTo(std::declval())); }; Вариант с параметром шаблона интересен тем что он включает SFINAE, но в данном случае оно не нужно.

воскресенье, 15 декабря 2019 г.

Запрос, при наличии unique constraint, выдаёт ошибку ORA-01779: нельзя изменить столбец, кот.отображает non key-preserved таблицу

#sql #oracle #constraints


В таблице user1.btable заведён unique constraint на 4 поля: c, d, e, f.

Запрос

update (select a.bid a$bid, b.bid b$bid
       from user1.atable a
       join user1.btable b
         on a.c = b.c
        and a.d = b.d
        and a.e = b.e
        and b.f = 0)
set a$bid = b$bid; 


выдаёт ошибку   


  ORA-01779: нельзя изменить столбец, кот.отображает non key-preserved таблицу


Почему? Ведь обычно такая ошибка возникает по следующей причине


  Ораклу надо уникально идентифицировать запись которую надо поменять. А для этого
подзапрос должен использовать все столбцы в уникальном ключе. Кроме того у вас в первом
случае может возникнуть проблема с тем, что для одной записи из таблицы A будет выбрано
несколько строк из балицы B и тогда ораклу будет не ясно а из какой из записей B взять
bid - Mike


А если в таблицу user1.atable добавить поле f, и изменить условие соединения на 

     on a.c = b.c
    and a.d = b.d
    and a.e = b.e
    and b.f = a.f


запрос ошибки не выдаёт.

Выдача SELECT * FROM V$VERSION


  Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
     PL/SQL Release 11.2.0.1.0 - Production
     CORE   11.2.0.1.0  Production
     TNS for Linux: Version 11.2.0.1.0 - Production
     NLSRTL Version 11.2.0.1.0 - Production

    


Ответы

Ответ 1



В описанной Вами схеме все должно работать создал подобную тестовую схему: CREATE TABLE TEST_TABLEA( "C" VARCHAR2(10), "D" VARCHAR2(10), "E" VARCHAR2(10), "F" VARCHAR2(10), "BID" VARCHAR2(10)); CREATE TABLE TEST_TABLEB( "C" VARCHAR2(10), "D" VARCHAR2(10), "E" VARCHAR2(10), "F" VARCHAR2(10), "BID" VARCHAR2(10)); ALTER TABLE TEST_TABLEB add constraint CONSTR unique (C, D, E, F) using index; И запрос (который Вы указывали в вопросе) на ней работает правильно, если есть constraint, но если его нет то выбрасывается исключение ORA-01779. Что бы избежать появление ошибки попробуйте следующий запрос: update test_tablea set bid = (select b.bid from test_tableb b where test_tablea.c = b.c and test_tablea.d = b.d and test_tablea.e = b.e and b.f = 0) where exists( select b.bid from test_tableb b where test_tablea.c = b.c and test_tablea.d = b.d and test_tablea.e = b.e and b.f = 0) или его аналог с nvl предложенный в комментариях, но тогда у Вас будет лишние update(ы). После проверки на разных версиях БД удалось установить следующее join update НЕ заработал на Oracle версий: Oracle Database 10g Enterprise Edition Release 10.2.0.4.0, Oracle Database 11g Enterprise Edition Release 11.2.0.2.0. Работает на: Oracle Database 11g Enterprise Edition Release 11.2.0.3.0, Oracle Database 12c Enterprise Edition Release 12.1.0.2.0, Oracle Database 12c Standard Edition Release 12.1.0.2.0.

Ответ 2



В дополнение к ответу Mikhailov Valentine. На 10-ке запрос не работает, а на SE 11.2.0.3.0 - работает. Судя по всему, в более новой версии при разборе запроса допускаются некоторые классы выражений, помимо прямых отсылок к полям базовой таблицы. Так, для простых выражений, вычисляемых на этапе разбора, и в итоге порождающих константу, запрос срабатывает: b.f = 0 или case when 0 is null then 0 else 0 end. Но если это выражение содержит отсылки к другим полям, то не сработает: b.f = a.f - a.f, case when a.f is null then a.f else a.f end Работает для условия b.f = a.f, но при этом не работает для условий: b.f = a.f + 0, b.f = nvl(a.f, 0)

четверг, 25 октября 2018 г.

Запрос, при наличии unique constraint, выдаёт ошибку ORA-01779: нельзя изменить столбец, кот.отображает non key-preserved таблицу

В таблице user1.btable заведён unique constraint на 4 поля: c, d, e, f
Запрос
update (select a.bid a$bid, b.bid b$bid from user1.atable a join user1.btable b on a.c = b.c and a.d = b.d and a.e = b.e and b.f = 0) set a$bid = b$bid;
выдаёт ошибку
ORA-01779: нельзя изменить столбец, кот.отображает non key-preserved таблицу
Почему? Ведь обычно такая ошибка возникает по следующей причине
Ораклу надо уникально идентифицировать запись которую надо поменять. А для этого подзапрос должен использовать все столбцы в уникальном ключе. Кроме того у вас в первом случае может возникнуть проблема с тем, что для одной записи из таблицы A будет выбрано несколько строк из балицы B и тогда ораклу будет не ясно а из какой из записей B взять bid - Mike
А если в таблицу user1.atable добавить поле f, и изменить условие соединения на
on a.c = b.c and a.d = b.d and a.e = b.e and b.f = a.f
запрос ошибки не выдаёт.
Выдача SELECT * FROM V$VERSION
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production PL/SQL Release 11.2.0.1.0 - Production CORE 11.2.0.1.0 Production TNS for Linux: Version 11.2.0.1.0 - Production NLSRTL Version 11.2.0.1.0 - Production


Ответ

В описанной Вами схеме все должно работать создал подобную тестовую схему:
CREATE TABLE TEST_TABLEA( "C" VARCHAR2(10), "D" VARCHAR2(10), "E" VARCHAR2(10), "F" VARCHAR2(10), "BID" VARCHAR2(10));
CREATE TABLE TEST_TABLEB( "C" VARCHAR2(10), "D" VARCHAR2(10), "E" VARCHAR2(10), "F" VARCHAR2(10), "BID" VARCHAR2(10));
ALTER TABLE TEST_TABLEB add constraint CONSTR unique (C, D, E, F) using index;
И запрос (который Вы указывали в вопросе) на ней работает правильно, если есть constraint, но если его нет то выбрасывается исключение ORA-01779
Что бы избежать появление ошибки попробуйте следующий запрос:
update test_tablea set bid = (select b.bid from test_tableb b where test_tablea.c = b.c and test_tablea.d = b.d and test_tablea.e = b.e and b.f = 0) where exists( select b.bid from test_tableb b where test_tablea.c = b.c and test_tablea.d = b.d and test_tablea.e = b.e and b.f = 0)
или его аналог с nvl предложенный в комментариях, но тогда у Вас будет лишние update(ы).

После проверки на разных версиях БД удалось установить следующее join update НЕ заработал на Oracle версий:
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0, Oracle Database 11g Enterprise Edition Release 11.2.0.2.0.
Работает на:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0, Oracle Database 12c Enterprise Edition Release 12.1.0.2.0, Oracle Database 12c Standard Edition Release 12.1.0.2.0.