Архив

Публикации с меткой ‘SQL’

Ассоциативные массивы (Associative Arrays)

15 Февраль 2011 Нет комментариев

Ассоциативные массивы

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

Позволяет работать со столбцами как с единой переменной — массивом.

Является временной формой коллекции, существует в течении сессии.

Другое название ассоциативных массивов — индексированные таблицы или pl\sql таблицы.

Ассоциативный массив это наиболее часто используемый вид коллекции. Его не надо инициализировать или расширять. В Oracle9i Database Release 2 и более поздних версиях ассоциативные массивы можно индексировать не только по числам, но и по строкам.

Пример  1: INDEX BY PLS_INTEGER
SQL> set serveroutput on size unlimited;
SQL> DECLARE
  2   TYPE XX_COST_TYPE IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
  3   cost XX_COST_TYPE;
  4   l_res NUMBER;
  5  BEGIN
  6   cost(1) := 5 ;
  7   cost(20) := 10 ;
  8   cost(12) := 15 ;
  9   l_res := cost(1) + cost(20) + cost(12);
 10   DBMS_OUTPUT.PUT_LINE(l_res);
 11  END;
 12  /

30

PL/SQL procedure successfully completed
Пример  2: INDEX BY VARCHAR2
SQL> DECLARE
  2   TYPE XX_COST_TYPE IS TABLE OF NUMBER INDEX BY VARCHAR2(100);
  3   cost XX_COST_TYPE;
  4   L_SELECTED VARCHAR2(100) := 'CHAIR';
  5  BEGIN
  6   cost('TABLE') := 5000;
  7   cost('CHAIR') := 84020 ;
  8   cost('LAMP')  := 8300;
  9   cost('PENCIL'):= 110;
 10   DBMS_OUTPUT.PUT_LINE(cost(L_SELECTED));
 11  END;
 12  /

84020

PL/SQL procedure successfully completed

Ограничения:


Нельзя создать тип ассоциативного массива на уровне схемы:

Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> CREATE OR REPLACE TYPE xx_test_type IS TABLE OF NUMBER INDEX BY PLS_INTEGER
  2  /

Warning: Type created with compilation errors
Предупреждение: Тип создан с ошибками компиляции.

SQL> show errors
Ошибки для TYPE XX_TEST_TYPE:

LINE/COL ERROR
-------- -----------------------------------------------------------------
0/0      PL/SQL: Compilation unit analysis terminated
1/22     PLS-00355: использование таблицы pl/sql не разрешается в этом
         контексте
Categories: oracle, SQL Tags: , ,

Вложенные таблицы (Nested Tables)

15 Февраль 2011 1 комментарий

 

Вложенные таблицы

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

Максимальное число строк вложенной таблицы составляет 2 гигабайта.

Наиболее важным отличием между вложенными и индексированными таблицами является то, что вложенные таблицы являются постоянной формой коллекции.

Свойства вложенных таблиц

  • Могут использоваться как в SQL (тип столбца в таблице), так и в PL\SQL коде
  • Содержат однородные данные, т.е. все строки имеют одинаковую структуру данных
  • Требуется инициализация, при использовании в PL\SQL
  • Порядок элементов не зафиксирован
  • Используется память PGA (для всех 3 типов коллекций)
  • При попытке чтения элемента с несуществующим индексом -> исключение NO_DATA_FOUND

Читать дальше про “Вложенные таблицы (Nested Tables)” »

Categories: oracle, SQL Tags: , ,

Разбор строки на числа с разделителем дробной части в виде точки

11 Январь 2011 Нет комментариев

Используя regexp_substr, получим дробные числа из строки.

SELECT regexp_substr(str, '([[:digit:]]+[.]{1}[[:digit:]]+)+', 1, level) str
FROM (
      SELECT ' 5646.45, 45 sd eds,.sd 9 2 566.11 12.3' str FROM dual) t
CONNECT BY 
 regexp_substr(str,'([[:digit:]][.]{1}[[:digit:]])+',1, level)  is not null;

STR
---------------------------------------
5646.45
566.11
12.3

Генерация DDL для объектов БД

11 Январь 2011 Нет комментариев

Начиная с Oracle 9i появился пакет DBMS_METADATA, который позволяет пользователям получать метаданные о любых объектах (таблицах, индексах, процедурах и т.д.) всего лишь за одно обращение.

1) GET_DDL — возвращает метаданные объъекта как DDL

SELECT object_type,
       object_name,
       dbms_metadata.get_ddl(object_type, object_name, owner) as ddl_code
  FROM dba_objects t
 WHERE t.object_type = 'TABLE'
   and rownum <= 10;

Читать дальше про “Генерация DDL для объектов БД” »

Просмотр количества открытых курсоров для сессий

23 Декабрь 2010 Нет комментариев

Узнать кто активно грузит базу большим количеством курсоров, можно следующим запросом

select a.value,
       s.program,
       s.username,
       s.sid,
       s.serial#,
       s.client_identifier
from v$sesstat  a, 
     v$statname b, 
     v$session  s
where a.statistic# = b.statistic#
      and s.sid = a.sid
      and b.name = 'opened cursors current'
order by 1 desc;

Функция PERCENTILE_DISC

3 Сентябрь 2010 Нет комментариев

Функция принимает на вход процентное значение (P) и условие сортировки, возвращает элемент из набора — наименьшее значение функции CUME_DIST, которое больше или равно значению P.

PERCENTILE_DISC(expr) WITHIN GROUP
   (ORDER BY expr [ DESC | ASC ])
   [ OVER (query_partition_clause) ]

Пример

with t as
(select 1 id, 10 val  from dual
  union all
 select 2 id, 20 val  from dual
  union all
 select 3 id, 30 val  from dual
  union all
 select 4 id, 50 val  from dual
  union all
 select 5 id, 70 val  from dual
)
select  id,
        val,
        cume_dist() over (order by val) "Cume_Dist",
        percentile_disc(0.50) within group (order by val) over() "percentile_disc"
from t
order by val;

        ID        VAL  Cume_Dist percentile_disc
---------- ---------- ---------- ---------------
         1         10        0,2              30
         2         20        0,4              30
         3         30        0,6              30
         4         50        0,8              30
         5         70          1              30
Categories: SQL Tags: ,

Функция CUME_DIST

3 Сентябрь 2010 Нет комментариев

Аналитический вариант

CUME_DIST( ) OVER ( [ query_partition_clause ] order_by_clause )

Функция CUME_DIST рассчитывает кумулятивное распределение значения в наборе данных. Возвращаемое значение находится в диапазоне от 0 до 1. Функция на вход принимает числовой тип данных, или тип данных, который может неявно преобразовать в числовой.

Читать дальше про “Функция CUME_DIST” »

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

2 Сентябрь 2010 Нет комментариев

Вытаскиваем записи, у которых есть хотя бы одна нерусская буква

with t as
(
 select 1 id, 'Сидоров' s from dual
  union all
 select 2, 'Cидoров' from dual
  union all
 select 3, 'Пирогов' from dual
)
select * from t t0
where soundex(t0.s) is not null;

        ID S
---------- -------
         2 Cидoров

Предположительные совпадения (Пирогов добавлен для показательности неточности подхода)

with t as
(
 select 1 id, 'Сидоров' s from dual
  union all
 select 2, 'Cидoров' from dual
  union all
 select 3, 'Пирогов' from dual
)
select * from t t0, t t1
where UTL_MATCH.JARO_WINKLER_SIMILARITY(t0.s,t1.s)>=70
      and soundex(t0.s) is null
      and soundex(t1.s) is not null
      and t0.id<>t1.id;

        ID S               ID S
---------- ------- ---------- -------
         1 Сидоров          2 Cидoров
         3 Пирогов          2 Cидoров

Простой translate для вытаскивания совпадающих написаний

with t as
(
 select 1 id, 'Сидоров' s from dual
  union all
 select 2, 'Cидoров' from dual
  union all
 select 3, 'Пирогов' from dual
)
select * from t t0, t t1
where translate(t1.s,'AaBbCcEegHKkMmOoPpTuXxy','АаВьСсЕедНКкМтОоРрТиХху')=t0.s
      and t0.id<>t1.id;

        ID S               ID S
---------- ------- ---------- -------
         1 Сидоров          2 Cидoров

Вывести данные в одном языке (латиница или кирилица) + цифры и знак подчёрка:

with t as
(
 select 'Сидоров' val from dual
  union all
 select 'Cидoров' from dual
  union all
 select 'Пирогов' from dual
  union all
 select 'ASD_1' from dual 
  union all
 select 'ASD_1Я' from dual  
)
select val from t
where regexp_like(val, '^([A-z]|[0-9]|_)+$') or regexp_like(val, '^([А-я]|[0-9]|_)+$')
/

VAL
-------
Сидоров
Пирогов
ASD_1

Объединение интервалов дат

31 Август 2010 Нет комментариев
with t as (
select to_date ('01.01.2009', 'dd.mm.yyyy') beg_date,
       to_date ('10.01.2009', 'dd.mm.yyyy') end_date
 from dual
union all
select to_date ('03.01.2009', 'dd.mm.yyyy') beg_date,
       to_date ('05.01.2009', 'dd.mm.yyyy') end_date
 from dual
union all
select to_date ('10.01.2009', 'dd.mm.yyyy') beg_date,
       to_date ('12.01.2009', 'dd.mm.yyyy') end_date
 from dual
union all
select to_date ('13.01.2009', 'dd.mm.yyyy') beg_date,
       to_date ('20.01.2009', 'dd.mm.yyyy') end_date
 from dual
union all
select to_date ('01.02.2009', 'dd.mm.yyyy') beg_date,
       to_date ('10.02.2009', 'dd.mm.yyyy') end_date
 from dual
)
--
select min (beg_date) as beg_date,
       max (end_date) as end_date
from (
      select beg_date,
             end_date,
             sum(strt_grp) over (order by beg_date, end_date ) grp_num
      from (
             select  beg_date,
                     end_date,
                     case when beg_date > 1 + max(end_date) over (order by beg_date, end_date rows between unbounded preceding and 1 preceding) then 1 end strt_grp
             from t
           )
     )
group by grp_num
order by beg_date;

BEG_DATE    END_DATE
----------- -----------
01-01-2009  20-01-2009
01-02-2009  10-02-2009

Разбить период на месяца

5 Август 2010 7 comments
with t as (
  select to_date('03-05-2010','dd-mm-yyyy') d1,
         to_date('26-08-2010','dd-mm-yyyy') d2
  from dual
)
--
select decode(level,1,d1,trunc(add_months(d1,level-1),'mm')) as date_from,
       case when add_months(trunc(d1,'mm'),level)>d2 then d2
            else last_day(add_months(d1,level-1))
       end date_to
from t
connect by add_months(trunc(d1,'mm'),level-1) < d2;


DATE_FROM   DATE_TO
----------- -----------
03.05.2010  31.05.2010
01.06.2010  30.06.2010
01.07.2010  31.07.2010
01.08.2010  26.08.2010

Если хотим, чтобы в период входил 1 день последнего месяца, то :

with t as (
  select to_date('03-05-2010','dd-mm-yyyy') d1,
         to_date('01-08-2010','dd-mm-yyyy') d2
  from dual
)
--
select decode(level,1,d1,trunc(add_months(d1,level-1),'mm')) as date_from,
       case when add_months(trunc(d1,'mm'),level)>d2 then d2
            else last_day(add_months(d1,level-1))
       end date_to
from t
connect by add_months(trunc(d1,'mm'),level-1) <= d2;

DATE_FROM   DATE_TO
----------- -----------
03.05.2010  31.05.2010
01.06.2010  30.06.2010
01.07.2010  31.07.2010
01.08.2010  01.08.2010