Скрытые функции МН / [закрытого] SQL

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

До dmd 2.051, пример работал (который был ошибкой ). Теперь это было исправлено, поэтому пример в TDPL больше не верен. Тем не менее, дело не столько в том, что правила для ассоциативных массивов изменились, сколько в том, что в них была ошибка, которая не была обнаружена. Пример скомпилирован, когда его не должно было быть, и Андрей пропустил его. Он указан в официальных исправлениях для TDPL и должен быть исправлен в будущих печатных изданиях.

Исправленный код должен использовать либо dictionary[word.idup], либо dictionary[to!string(word)]. word.idup создает дубликат word, который является неизменным. to!string(word), с другой стороны, преобразует word в string наиболее подходящим способом. Поскольку word является char[] в этом случае, это будет использовать idup. Однако, если бы word уже было string, он просто вернул бы переданное значение и не стал бы его копировать без необходимости. Так, в общем случае, to!string(word) является лучшим выбором (особенно в шаблонных функциях), но в этом случае любой из них работает просто отлично (to!() в std.conv).

Технически возможно привести char[] к string, но обычно это плохая идея. Если вы знаете , что char[] никогда не изменится, то вы можете сойти с рук, но в общем случае вы рискуете проблемами, так как компилятор тогда предположит, что результирующий string никогда не может измениться, и это может генерировать код, который является неправильным. Это может даже сегфо. Так что не делайте этого, пока профилирование не покажет, что вам действительно нужна дополнительная эффективность предотвращения копирования, иначе вы не сможете избежать копирования, выполнив что-то вроде простого использования string во-первых (так что преобразование не будет необходимо), и вы знаете , что string никогда не изменится.

В общем, я бы не слишком беспокоился об эффективности копирования строк. Как правило, вы должны использовать string вместо char[], поэтому вы можете копировать их (то есть копировать их ссылки (например, str1 = str2;), а не копировать все их содержимое, как это делают dup и idup) не беспокоясь о том, что это особенно неэффективно. Проблема с примером заключается в том, что stdin.byLine() возвращает char[], а не string (предположительно, чтобы избежать копирования данных, если в этом нет необходимости). Так, splitter() возвращает char[], и поэтому word является char[] вместо string. Теперь вы можете сделать splitter(strip(line.idup)) или splitter(strip(line).idup) вместо idup ввода ключа. Таким образом, splitter() будет возвращать string, а не char[], но это, вероятно, в сущности так же эффективно, как idup ing word. Несмотря на это, из-за того, откуда текст исходит изначально, это char[] вместо string, что заставляет вас idup его где-то вдоль линии, если вы собираетесь использовать его в качестве ключа в ассоциативном массиве. Однако в общем случае лучше использовать string, а не char[]. Тогда вам не нужно ничего idup.

РЕДАКТИРОВАТЬ:
На самом деле, даже если вы обнаружите ситуацию, когда приведение от char[] к string кажется безопасным и необходимым, рассмотрите возможность использования std.exception.assumeUnique() ( документация ). По сути, это предпочтительный способ преобразования изменяемого массива в неизменяемый, когда вам нужно знать об этом. Обычно это делается в тех случаях, когда вы создали массив, который нельзя было сделать неизменным, потому что вам приходилось делать это по частям, но у которого нет других ссылок, и вы не хотите создавать его глубокую копию. Это не будет полезно в ситуациях, подобных примеру, о котором вы спрашиваете, поскольку вам действительно нужно скопировать массив.

27
задан 5 revs, 3 users 69% 23 May 2017 в 12:01
поделиться

11 ответов

Вы можете переопределять переменные, вы можете давать имена анонимным блокам, и вы все равно можете ссылаться на переопределенные переменные по имени:

PROCEDURE myproc IS
   n NUMBER;
BEGIN
   n := 1;
   <<anon>>
   DECLARE
      n NUMBER;
   BEGIN
      n := 2;
      dbms_output.put_line('n=' || n);
      dbms_output.put_line('anon.n=' || anon.n);
      dbms_output.put_line('myproc.n=' || myproc.n);
   END anon;
END myproc;
25
ответ дан 28 November 2019 в 04:01
поделиться

Истинно скрытая функция оракула - это функция OVERLAPS, но, вероятно, не очень разумно использовать какие-либо неподдерживаемые функции.

select 'yes' from dual where (sysdate-5,sysdate) overlaps (sysdate-2,sysdate-1);
15
ответ дан 28 November 2019 в 04:01
поделиться

Может быть, недостаточно скрыт, но мне нравится оператор Merge , который позволяет делать upserts (вставку или обновление)

MERGE <hint> INTO <table_name>
USING <table_view_or_query>
ON (<condition>)
WHEN MATCHED THEN <update_clause>
DELETE <where_clause>
WHEN NOT MATCHED THEN <insert_clause>
[LOG ERRORS <log_errors_clause> <reject limit <integer | unlimited>];
10
ответ дан 28 November 2019 в 04:01
поделиться

Одна малоизвестная функция, в которой я добился большого успеха, - это возможность вставки в таблицу с использованием переменной, объявленной как ее % ROWTYPE . Например:

CREATE TABLE CUSTOMERS (
    id NUMBER,
    name VARCHAR2(100),
    birth DATE,
    death DATE
)

PROCEDURE insert_customer IS
    customer CUSTOMERS%ROWTYPE;
BEGIN
    customer.id := 45;
    customer.name := 'John Smith';
    customer.birth := TO_DATE('1978/04/03', 'YYYY/MM/DD');

    INSERT INTO CUSTOMERS VALUES customer;
END;

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

13
ответ дан 28 November 2019 в 04:01
поделиться

Вы можете индексировать таблицы pl / sql по другим типам, кроме целых чисел. Таким образом вы можете создавать структуры, подобные «словарю», которые могут значительно упростить чтение вашего кода:

Пример:

DECLARE
  TYPE dictionary IS TABLE OF VARCHAR2(200) INDEX BY VARCHAR2(100);
  dict dictionary;
BEGIN
  dict('NAME') := 'John Doe';
  dict('CITY') := 'New York';

  dbms_output.put_line('Name:' || dict('NAME'));
END;
17
ответ дан 28 November 2019 в 04:01
поделиться
  1. Недокументированная функция: dbms_system.ksdwrt (записывает в файлы предупреждений / трассировки)
  2. Пакет DBMS_SQL (в качестве примера его использования см. этот вопрос
  3. Предложение AUTHID CURRENT_USER
  4. Условная компиляция
6
ответ дан 28 November 2019 в 04:01
поделиться

Динамический PL / SQL некрасив, но может делать кое-что интересное. Например, имена можно рассматривать как переменные, которые я использовал ранее для обхода переменных% rowtype, таких как массивы, и для создания функции, которая для заданного имени таблицы будет возвращать курсор, который выбирает одну строку со значениями по умолчанию. каждого столбца. Оба являются полезными обходными путями для денормализованных таблиц.

4
ответ дан 28 November 2019 в 04:01
поделиться

Эту процедурную конструкцию PL / SQL я часто использую (спасибо Стивену Фейерштейну и Чену Шапире). Ассоциативный массив, используемый для чачинга, но он не загружает все данные заранее, но при необходимости получает данные из базы данных и помещает их в ассоциативный массив.

create or replace
PACKAGE justonce
IS
  FUNCTION hair (code_in IN hairstyles.code%TYPE)
    RETURN hairstyles%ROWTYPE;
  TYPE hair_t IS TABLE OF hairstyles%ROWTYPE
    INDEX BY BINARY_INTEGER;
  hairs          hair_t;
END justonce;

create or replace 
PACKAGE BODY justonce
IS
  FUNCTION hair (code_in IN hairstyles.code%TYPE) RETURN hairstyles%ROWTYPE
  IS
    return_value   hairstyles%ROWTYPE;
    FUNCTION hair_from_database RETURN hairstyles%ROWTYPE
    IS
      CURSOR hair_cur IS
      SELECT * FROM hairstyles WHERE code = code_in;
    BEGIN
      OPEN hair_cur;
      FETCH hair_cur INTO return_value;
      CLOSE hair_cur;
      RETURN return_value;
    END hair_from_database;
  BEGIN
    IF NOT (hairs.exists(code_in))
    THEN
      dbms_output.put_line('Get record from database');
      hairs (code_in) := hair_from_database;
    END IF;
    RETURN hairs (code_in);
  END hair;
END justonce;

Проверьте это:

declare
    h hairstyles%ROWTYPE;
begin
   for i in 1000..1004
   loop
      h := justonce.hair(i);
      dbms_output.put_line(h.description);
   end loop;
   for i in 1000..1004
   loop
      h := justonce.hair(i);
      dbms_output.put_line(h.description||' '||h.price);
   end loop;

end;
/

Get record from database
CREWCUT
Get record from database
BOB
Get record from database
SHAG
Get record from database
BOUFFANT
Get record from database
PAGEBOY
CREWCUT 10
BOB 20
SHAG 21
BOUFFANT 11
PAGEBOY 44
7
ответ дан 28 November 2019 в 04:01
поделиться

Мой ответ на Скрытые функции в Oracle уместен здесь:

Поскольку Apex теперь является частью каждой базы данных Oracle, эти служебные функции Apex полезны, даже если вы не 'Не используется Apex:

SQL> declare
  2    v_array apex_application_global.vc_arr2;
  3    v_string varchar2(2000);
  4  begin
  5  
  6    -- Convert delimited string to array
  7    v_array := apex_util.string_to_table('alpha,beta,gamma,delta', ',');
  8    for i in 1..v_array.count
  9    loop
 10      dbms_output.put_line(v_array(i));
 11    end loop;
 12  
 13    -- Convert array to delimited string
 14    v_string := apex_util.table_to_string(v_array,'|');
 15    dbms_output.put_line(v_string);
 16  end;
 17  /
alpha
beta
gamma
delta
alpha|beta|gamma|delta

PL/SQL procedure successfully completed.
10
ответ дан 28 November 2019 в 04:01
поделиться

Процедуры и функции могут быть определены в блоках DECLARE :

DECLARE

    PROCEDURE print(text VARCHAR2) IS
    BEGIN
        DBMS_OUTPUT.put_line(text);
    END;

BEGIN

    print('Yay!');
    print('Woo hoo!');

END;

Это удобно для создания автономных сценариев.

12
ответ дан 28 November 2019 в 04:01
поделиться

Знаете ли вы, что с опцией SAMPLE (K) вы можете ВЫБРАТЬ только образец, состоящий из K процентов таблицы Oracle?

SELECT *
  FROM MASSIVE_TABLE SAMPLE (5);

Предыдущий оператор извлекает случайный набор, состоящий из до 5% записей, хранящихся в массивной таблице MASSIVE_TABLE.

12
ответ дан 28 November 2019 в 04:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: