Ассоциативные массивы требуют, чтобы их ключи были неизменными. Это имеет смысл, когда вы думаете о том, что если он не является неизменяемым, то он может измениться, что означает, что его хеш-код изменяется, а это означает, что когда вы снова получите значение, компьютер его не найдет. И если вы замените его, вы получите еще одно значение, добавленное к ассоциативному массиву (поэтому у вас будет одно с правильным хешем и одно с неправильным хешем). Однако, если ключ является неизменным, он не может измениться, и поэтому такой проблемы нет.
До 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()
( документация ). По сути, это предпочтительный способ преобразования изменяемого массива в неизменяемый, когда вам нужно знать об этом. Обычно это делается в тех случаях, когда вы создали массив, который нельзя было сделать неизменным, потому что вам приходилось делать это по частям, но у которого нет других ссылок, и вы не хотите создавать его глубокую копию. Это не будет полезно в ситуациях, подобных примеру, о котором вы спрашиваете, поскольку вам действительно нужно скопировать массив.
Вы можете переопределять переменные, вы можете давать имена анонимным блокам, и вы все равно можете ссылаться на переопределенные переменные по имени:
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;
Истинно скрытая функция оракула - это функция OVERLAPS, но, вероятно, не очень разумно использовать какие-либо неподдерживаемые функции.
select 'yes' from dual where (sysdate-5,sysdate) overlaps (sysdate-2,sysdate-1);
Может быть, недостаточно скрыт, но мне нравится оператор 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>];
Одна малоизвестная функция, в которой я добился большого успеха, - это возможность вставки в таблицу с использованием переменной, объявленной как ее % 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;
Хотя он занимает немного больше табличного пространства повтора, он, безусловно, делает вставку данных (особенно в большие таблицы) намного более понятной. Это также позволяет избежать множества переменных, необходимых для хранения значения каждого столбца, который вы хотите вставить.
Вы можете индексировать таблицы 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;
Динамический PL / SQL некрасив, но может делать кое-что интересное. Например, имена можно рассматривать как переменные, которые я использовал ранее для обхода переменных% rowtype, таких как массивы, и для создания функции, которая для заданного имени таблицы будет возвращать курсор, который выбирает одну строку со значениями по умолчанию. каждого столбца. Оба являются полезными обходными путями для денормализованных таблиц.
Эту процедурную конструкцию 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
Мой ответ на Скрытые функции в 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.
Процедуры и функции могут быть определены в блоках DECLARE
:
DECLARE
PROCEDURE print(text VARCHAR2) IS
BEGIN
DBMS_OUTPUT.put_line(text);
END;
BEGIN
print('Yay!');
print('Woo hoo!');
END;
Это удобно для создания автономных сценариев.
Знаете ли вы, что с опцией SAMPLE (K) вы можете ВЫБРАТЬ только образец, состоящий из K процентов таблицы Oracle?
SELECT *
FROM MASSIVE_TABLE SAMPLE (5);
Предыдущий оператор извлекает случайный набор, состоящий из до 5% записей, хранящихся в массивной таблице MASSIVE_TABLE.