2010-10-21 2 views
2

Представьте, что вы имеете следующую таблицу (примечание: это надуманный/упрощенный пример):Необходимые параметры в предложении SQL where?

CREATE TABLE foo ( 
    book_id number, 
    page number, 
    -- [a bunch of other columns describing a single page in a book] 
); 

ALTER TABLE foo 
ADD (CONSTRAINT foo_pk PRIMARY KEY(book_id, page)); 

В то время как (book_id, страница) пары являются уникальными, и та же страница будет повторяться между книгами (много книг будет есть страница 1). Поэтому, если SQL-запрос не указывает book_id, могут быть выбраны/обновлены/удалены неправильные страницы (-ы). Все наши запросы должны действовать только по одной книге за раз, но я видел пару ошибок, где параметр book_id был случайно опущен.

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

Мы генерируем SQL-код для запросов динамически и выполняем их с использованием Spring JdbcTemplate. База данных - это Oracle. Используя автоматические тесты, чтобы проверить, что многие возможные запросы (плюс новые, которые добавляются в будущем!), Не сработают с дублированием page_ids. Я мог бы переопределить код JdbcTemplate, чтобы гарантировать, что sql-запросы всегда включают параметр book_id, но это требует ручного разбора кода SQL (особенно сложного с подзапросами) и кажется взломанным. Есть ли более надежное решение для обеспечения этого? Некоторые триггер, хранимая процедура, ограничение?

+0

Какую базу данных? Вы могли бы достичь некоторых из этих целей с помощью системы правил PostgreSQL. –

+0

Спасибо за предложение. Просто добавил, что «Oracle» - это БД к исходному вопросу. –

+0

Что делать, если кто-то хочет запустить запрос, который не нуждается в book_id, например. «найти книгу с наибольшим количеством страниц» - такой запрос не будет иметь предиката на book_id. –

ответ

2

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

Другой вариант - убедиться, что запросы, которые вы генерируете, всегда имеют ограничение book_id. Я надеюсь, что вы не создаете весь оператор SQL как String и что вы используете параметризованные запросы. Если вы этого не сделаете, то использование параметризованных запросов - это хороший способ убедиться, что вы всегда проходите book_id (если оставить параметр отключенным, запрос не будет выполняться). Кроме того, вам не грозит опасность, если вы не будете дезинфицировать ваши данные при использовании параметризованных запросов.

+0

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

+0

О, и мы используем параметризованные запросы, проблема в том, что их довольно много, новые добавляются все время, а иногда кто-то забывает параметр book_id в предложении where. Другими словами, я ищу решение, которое помогает в случае неправильного написания запроса программистом. –

3

Общим способом защиты базы данных от ошибки программиста является требование, чтобы приложения использовали хранимые процедуры. (Иногда это может быть сделано с разрешениями.)

Гораздо проще проверить ваши процессы на предмет соответствия, чем специальные запросы.

0

Единственный способ, которым я могу это сделать, - заменить столбцы book_id и page в таблице одним столбцом, в котором хранятся обе части информации - что-то вроде (book_id * 10000 + страница), если вы хотите, чтобы в столбце целого числа или "book_id-page" для столбца строки.

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

1

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

Сказав, что вы могли бы ловушку таких обновлений посредством комбинации триггеров:

  • Заявления уровня ДО триггера для инициализации переменной g_book_id пакета обнулить
  • Триггер на уровне строк (а) проверьте, что обновленная book_id соответствует той, что в переменной пакета (если не null), и (b) инициализирует переменную пакета, если она равна нулю.

Простой пример:

SQL> create table t1 (id int, col2 int); 

Table created. 

SQL> insert into t1 values(1, null); 

1 row created. 

SQL> insert into t1 values(2, null); 

1 row created. 

SQL> create package p1 is g_id integer; end; 
    2/

Package created. 

SQL> create trigger t1_bus 
    2 before update on t1 
    3 begin 
    4 p1.g_id := null; 
    5* end; 
SQL>/

Trigger created. 

SQL> create trigger t1_bir 
    2 before update on t1 
    3 for each row 
    4 begin 
    5  if :new.id != p1.g_id then 
    6  raise_application_error(-20000,'You can only update 1 ID at a time'); 
    7  end if; 
    8  p1.g_id := :new.id; 
    9 end; 
10/

Trigger created. 

SQL> update t1 set col2=1 where id=1; 

1 row updated. 

SQL> update t1 set col2=2 where id=2; 

1 row updated. 

SQL> update t1 set col2=3; -- ID not specified 
update t1 set col2=3 
     * 
ERROR at line 1: 
ORA-20000: You can only update 1 ID at a time 
+0

Он хочет это для 'SELECT', а также ... – egrunin

+0

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

+0

Интересный подход, спасибо. Я не могу себе представить, что существует подход, основанный на триггерах, который тоже будет работать для SELECT? Как я уже сказал в комментарии выше, фактический домен не касается книг (я изменил имена таблиц и столбцов для удобства обсуждения), а в реальном домене запросы по нескольким «книгам» просто не имеют смысла. –