Подтвердить что ты не робот

Oracle Gotchas для опытного Newb

Что такое Oracle gotchas для кого-то нового для платформы, но не нового для реляционных баз данных (MySQL, MS SQL Server, Postgres и т.д.) в целом.

Два примера того, что я ищу

  • Многие продукты реляционной базы данных обрабатывают создание ключа auto_increment для вас. Oracle нет, вы должны вручную создать последовательность, а затем создать триггер

  • При вводе данных через интерфейс SQL Developer вам необходимо вручную зафиксировать данные

Бонусные очки для связанных с PHP getchas, так как платформа Я буду использовать этот гипотетический опытный newb.

4b9b3361

Ответ 1

Примечание: я объясняю только gotchas здесь, я. е. ситуации, когда Oracle ведет себя не так, как другие системы. Oracle имеет множество преимуществ по сравнению с другими RDBMS, но они не являются темой сообщения.

  • Вы не можете SELECT без FROM.

    SELECT  1
    

    не удастся, вам необходимо:

    SELECT  1
    FROM    dual
    
  • Пустая строка и NULL - это одно и то же.

    SELECT  *
    FROM    dual
    WHERE   '' = ''
    

    ничего не возвращает.

  • Нет TOP и LIMIT. Вы ограничиваете свои результаты в предложении WHERE:

    SELECT  *
    FROM    (
            SELECT  *
            FROM    mytable
            ORDER BY
                    col
            )
    WHERE   rownum < 10
    

    именно таким образом, используя подзапрос, поскольку ROWNUM оценивается до ORDER BY.

  • Вы не можете вложить коррелированные подзапросы более одного уровня. Это не будет выполнено:

    SELECT  (
            SELECT  *
            FROM    (
                    SELECT  dummy
                    FROM    dual di
                    WHERE   di.dummy = do.dummy
                    ORDER BY
                            dummy
                    )
            WHERE   rownum = 1
            )
    FROM    dual do
    

    Это проблема.

    Значения
  • NULL не индексируются. Этот запрос не будет использовать индекс для упорядочения:

    SELECT  *
    FROM    (
            SELECT  *
            FROM    mytable
            ORDER BY
                    col
            )
    WHERE   rownum < 10
    

    если col не помечен как NOT NULL.

    Обратите внимание на значения NULL, которые не индексируются, а не столбцы. Вы можете создать индекс в столбце с нулевым значением, а значения индекса NULL попадут в индекс.

    Однако индекс не будет использоваться, когда условие запроса предполагает, что значения NULL могут удовлетворить его.

    В приведенном выше примере вы хотите вернуть все значение (включая NULL s). Тогда индекс не знает значений не NULL, следовательно, не может их получить.

    SELECT  *
    FROM    (
            SELECT  *
            FROM    mytable
            ORDER BY
                    col
            )
    WHERE   rownum < 10
    

    Но этот запрос будет использовать индекс:

    SELECT  *
    FROM    (
            SELECT  *
            FROM    mytable
            WHERE   col IS NOT NULL
            ORDER BY
                    col
            )
    WHERE   rownum < 10
    

    так как значения не NULL никогда не могут удовлетворять условию.

  • По умолчанию NULL сортируются последним, а не первым (например, в PostgreSQL, но в отличие от MySQL и SQL Server)

    Этот запрос:

    SELECT  *
    FROM    (
            SELECT  1 AS id
            FROM    dual
            UNION ALL
            SELECT  NULL AS id
            FROM    dual
            ) q
    ORDER BY
            id
    

    вернет

    id
    ---
    1
    NULL
    

    Чтобы отсортировать, как в SQL Server и MySQL, используйте это:

    SELECT  *
    FROM    (
            SELECT  1 AS id
            FROM    dual
            UNION ALL
            SELECT  NULL AS id
            FROM    dual
            ) q
    ORDER BY
            id NULLS FIRST
    

    Обратите внимание, что он нарушает порядок ROWNUM, если последний не используется из подзапроса (как описано выше)

  • "MYTABLE" и "MYTABLE" (материал с двойными кавычками) - это разные объекты.

    SELECT  *
    FROM    mytable -- wihout quotes
    

    будет выбирать из первого, а не последнего. Если первое не существует, запрос завершится с ошибкой.

    CREATE TABLE mytable
    

    создает "MYTABLE", а не "MYTABLE".

  • В Oracle все неявные блокировки (которые являются результатом операций DML) являются уровнями строк и никогда не эскалируются. Это не строка, на которую не влияет транзакция, может быть неявно заблокирована.

    Писатели никогда не блокируют читателей (и наоборот).

    Чтобы заблокировать всю таблицу, вы должны указать явный оператор LOCK TABLE.

    Блокировки строк хранятся в данных.

  • В Oracle нет индексов "CLUSTERED", есть "индексированные таблицы". По умолчанию таблицы организованы в кучу (в отличие от SQL Server и MySQL с InnoDB).

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

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

Ответ 2

SELECT 1 не работает, тогда выберите 1 из двойного.

Если вы работаете с иерархическими данными, соединитесь с ними отлично.

Ответ 3

Один комментарий: вам не нужно создавать триггер, чтобы использовать последовательности, если вы не настаиваете на репликации поведения столбца IDENTITY в Sybase/SQL Server. Я считаю более полезным просто использовать последовательность непосредственно в действительных инструкциях вставки, например.

INSERT
  INTO MyTable
     ( KeyCol
     , Name
     , Value
     )
SELECT Seq_MyTable.NextVal
     , 'some name'
     , 123
  FROM dual;

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

Ответ 4

Кажется, что я столкнулся с большим количеством баз данных Oracle, чувствительных к регистру объектов и данных схемы, чем в SQL Server.

Ответ 5

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

Это право, полное отсутствие!

Пример:

SELECT nvl(employeeName,'Archie'), nvl(employeeSpouse,'Edith') FROM Employee

Это гарантирует, что вы получите два столбца в наборе строк, даже если все значения равны нулю. Вы увидите кучу значений "Арчи" и "Эдит". Если вы не используете nvl(), вы можете получить только один столбец или ни один из них. Исходная часть этого состоит в том, что ваш код может отлично работать в вашей среде разработки и даже передавать QA, но когда он добирается до производства, значения в таблице могут изменять структуру результатов!

Итак, короче говоря, всякий раз, когда вы выбираете нулевой столбец, обязательно используйте nvl().

Ответ 6

В MySQL нет групповой конкатенации. Если вам нужна функция объединения совокупности групп, вы должны написать свой собственный. Вот моя реализация:

drop type T_GROUP_CONCAT;

create or replace type GROUP_CONCAT_PARAM as object
(
  val varchar2(255),
  separator varchar2(10),
  numToConcat NUMBER,
  MAP MEMBER FUNCTION GROUP_CONCAT_PARAM_ToInt  return VARCHAR2
);

--map function needed for disctinct in select clauses
CREATE OR REPLACE TYPE BODY GROUP_CONCAT_PARAM IS
    MAP MEMBER FUNCTION GROUP_CONCAT_PARAM_ToInt return VARCHAR2 is 
      begin 
        return val; 
      end; 

end;


/

CREATE OR REPLACE TYPE T_GROUP_CONCAT 
AS OBJECT (

runningConcat VARCHAR2(5000),
runningCount NUMBER,

STATIC FUNCTION ODCIAggregateInitialize
  ( actx IN OUT T_GROUP_CONCAT
  ) RETURN NUMBER,

MEMBER FUNCTION ODCIAggregateIterate
  ( self  IN OUT T_GROUP_CONCAT,
    val   IN       GROUP_CONCAT_PARAM
  ) RETURN NUMBER,

MEMBER FUNCTION ODCIAggregateTerminate
  ( self             IN   T_GROUP_CONCAT,
    returnValue  OUT VARCHAR2,
    flags           IN   NUMBER
  ) RETURN NUMBER,

MEMBER FUNCTION ODCIAggregateMerge
  (self  IN OUT T_GROUP_CONCAT,
   ctx2 IN      T_GROUP_CONCAT
  ) RETURN NUMBER

);
/

CREATE OR REPLACE TYPE BODY T_GROUP_CONCAT AS

STATIC FUNCTION ODCIAggregateInitialize
  ( actx IN OUT T_GROUP_CONCAT
  ) RETURN NUMBER IS 
  BEGIN
    IF actx IS NULL THEN
      actx := T_GROUP_CONCAT ('', 0);
    ELSE
      actx.runningConcat := '';
      actx.runningCount := 0;
    END IF;
    RETURN ODCIConst.Success;
  END;

MEMBER FUNCTION ODCIAggregateIterate
  ( self  IN OUT T_GROUP_CONCAT,
    val   IN     GROUP_CONCAT_PARAM
  ) RETURN NUMBER IS
  BEGIN
    if self.runningCount = 0 then
        self.runningConcat := val.val;
    elsif self.runningCount < val.numToConcat then
        self.runningConcat := self.runningConcat || val.separator || val.val;
    end if;
    self.runningCount := self.runningCount + 1;
    RETURN ODCIConst.Success;
  END;

MEMBER FUNCTION ODCIAggregateTerminate
  ( self        IN  T_GROUP_CONCAT,
    ReturnValue OUT VARCHAR2,
    flags       IN  NUMBER
  ) RETURN NUMBER IS
  BEGIN
    returnValue := self.runningConcat;
    RETURN ODCIConst.Success;
  END;

MEMBER FUNCTION ODCIAggregateMerge
  (self IN OUT T_GROUP_CONCAT,
   ctx2 IN     T_GROUP_CONCAT
  ) RETURN NUMBER IS
  BEGIN
    self.runningConcat := self.runningConcat || ',' || ctx2.runningConcat;
    self.runningCount := self.runningCount + ctx2.runningCount;
    RETURN ODCIConst.Success;
  END;

END;
/

CREATE OR REPLACE FUNCTION GROUP_CONCAT
( x GROUP_CONCAT_PARAM
) RETURN VARCHAR2
--PARALLEL_ENABLE
AGGREGATE USING T_GROUP_CONCAT;
/

Чтобы использовать его:

select GROUP_CONCAT(GROUP_CONCAT_PARAM(tbl.someColumn, '|', 2)) from someTable tbl

Ответ 8

Временные таблицы

Вы создаете и индексируете их, как обычные таблицы, но каждый сеанс/транзакция видит только свои собственные данные. Это отличается от MS SQL.

Глобальные переменные

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

Триггеры

До самых последних версий не было способа определить способ срабатывания подобных триггеров. Если вы действительно позаботились о том, что "ПЕРЕД ОБНОВЛЕНИЕМ ДЛЯ КАЖДОЙ РЯДЫ" было первым, вы поставили все за один триггер.