Развернуть таблицу sql oracle

Обновлено: 06.07.2024

До версии 11 задача транспонирования решалась довольно сложно.

В версии 11 в синтаксисе оператора SELECTпоявились фразы PIVOT и UNPIVOT , которые выполняют такое транспонирование прозрачно и просто.
План запроса получается простой и эффективный -- запрос будет хорошо выполняться даже на больших объёмах. Прежние варианты с самосоединением таблиц были бы менее эффективны

Приведу пример использования:

Имеем таблицу курсов валют к рублю:

В 11g это можно сделать таким запросом:

select * from (select ISO_CODE , RATE_DATE , RATE_VALUE from EXCH_RATES )

pivot (max ( RATE_VALUE )
for ISO_CODE in ( 'EUR' as RATE_EUR , 'UAH' as RATE_UAH , 'USD' as RATE_USD )
)

2. Внутри фразы pivot ( ) сначала должна быть агрегатная функция, даже если фраза group by явно не указана. PIVOT выполнит неявный group by по всем полям, кроме поля в агрегатной функции и полей после FOR . Это необходимо, чтобы обработать ситуацию, когда там окажется несколько строк. Если строка одна -- не важно, что писать, MIN, MAX, SUM, AVG .


UPDATE 15.06.2015
Как транспонировать несколько полей ?


В приведённом выше примере мы транспонировали только одно поле -- RATE_VALUE превратилось в RATE_EUR, RATE_UAH, RATE_USD

Но бывают задачи, когда нужно транспонировать несколько полей, ниже я привожу пример, связанный тоже с курсами валют, но курсы взяты с рынка Форекс, и там строка курса содержит несколько значений OPEN (значение на момент начала интервала), MIN (минимальное, которое достигалось в интервале), MAX (максимальное, которое достигалось в интервале), CLOSE (каким значением курса интервал закончился), VOLUME (объём операций)

В этом случае, названия колонок нам нужно собрать перемножением множества значений ('OPEN', 'MIN', 'MAX', 'CLOSE', 'VOLUME') на множество значений названий пар валют

Этот запрос можно написать так, что в каждой колонке сначала будет название пары валют (поле из FOR) и через подчёркивание название поля, которое мы транспонируем

Сначала приведу запрос

select * from (select * from RATES)
pivot (avg (QOPEN) as QOPEN ,
avg (QMIN) as QMIN ,
avg (QMAX) as QMAX ,
avg (QCLOSE) as QCLOSE ,
avg (QVOLUME) as QVOLUME

for (CURPAIR) in ('EURGBP' as EURGBP
, 'EURPLN' as EURPLN
, 'EURUSD' as EURUSD)
)
order by 1

Алиасы полей, помеченные зелёным фоном -- обязательны


Вот таблица для тестирования

create table RATES (
CURPAIR varchar2(16) not null,
SNAPDATE date not null,
QOPEN number not null,
QMAX number not null,
QMIN number not null,
QCLOSE number not null,
QVOLUME number not null);

I needed a PL/SQL routine to summarize and transpose, and I came up short. So I decided to see if I could get it accomplished myself. The resulting procedure, TRANSPOSE, accomplishes this task for me.

Here's how it works: Pass the Source User, Source Table Name, Destination Table Name, X-Column Variable Name, Y-Column Headings Name, and Y-Column Value Name.

Internally, it creates a table with a Distinct List of the X-Column dimension. Next, it creates a cursor of the Distinct Y-Column Headings dimension. And, then for each distinct value ALTERS the table with that new Column and populates each 'cell' with the values from the Y-Column Value Name field. It iterates through the columns cursor until completed.

To view the results, type select * from 'Destination Table Name' and see what it has created.

It does no screening, so whatever X and Y dimensions are in the Source Table are passed into the Destination Table.

There are probably plenty of ways to improve upon this technique, but I ran out of time after getting it working. Feel free to take it to the next level of efficiency!!

Good luck,
Fred Mayer, CDP

/* --------------------------------------------------------------------
*/
/* TRANSPOSE: A Procedure to transpose and summarize a source table
*/
/* into a resulting smaller matrix table ***
*/
/*
*/
/* Parms: (1) Owner of the table
*/
/* (2) Source Table Name
*/
/* (3) New, Matrix Destination Table Name
*/
/* (4) X-Column dimension Column_Name
*/
/* (5) Y-Column dimension Column_Name
*/
/* (6) Cell Value Column_Name to populate each XY Cell
*/
/* NOTE: assume (6) is numeric, and will be SUM'd.
*/
/*
*/
/* ex: exec TRANSPOSE( 'myOwner', 'oldTable',
*/
/* 'newTable', 'GLDate',
*/
/* 'Division', 'Expenses' );
*/
/*
*/
/* *** If there are 6,200 records in the source, oldTable,
*/
/* with 450 distinct X-Dims and 18 distinct Y-Dims,
*/
/* then the resulting Matrix Table will be a new table with
*/
/* 450 rows and 19 columns: (1 X-Dim + 18 Y_Dim values).
*/
/* Each cell will have the (6) 'Value' for the XY intersection
*/
/*
*/
/* Column-Title is the Y-Column Name plus '_' plus each
*/
/* distinct Y-Column value. ex: DIVISION_ITD
*/
/* if 'DIVISION' = 'ITD'
*/
/*
*/
/* Run logic:
*/
/*
*/
/* Drop 'new Destination Table' (if it exists)
*/
/* Create 'newTable', with one column distinct X-Column values
*/
/* Alter 'newTable', adding 1 new Column per distinct Y_Column
*/
/* Populate 'cell', w/ UPDATE, each new Column with 'Value'.
*/
/*
*/
/*

To Test: (1) Find and run DEMOBLD.SQL (found in SQLPLUS/demo) (2) exec transpose('YOUR_UID','emp', 'myemp','hiredate','job','sal'); (3) select * from myemp; Results:


HIREDATE JOB_ANALYST JOB_CLERK JOB_MANAGER JOB_PRESIDENT JOB_SALESMAN

Oracle PIVOT позволяет написать перекрестный запрос таблицы, начал использоваться в Oracle 11g. Это означает, что вы можете объединить свои результаты и повернуть строки в столбцы.

Синтаксис

Синтаксис для оператора PIVOT в Oracle/PLSQL:

SELECT * FROM
(
SELECT column1, column2
FROM tables
WHERE conditions
)
PIVOT
(
aggregate_function(column2)
FOR column2
IN ( expr1, expr2, . expr_n) | subquery
)
ORDER BY expression [ ASC | DESC ];

Параметры или аргументы

Это может быть функция, такая, как SUM, COUNT, MIN, MAX или AVG.

IN ( expr1 , expr2 , . expr_n )

Список значений для поворота column2 в заголовке кросс-табличного результата запроса.

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

Применение

Оператор PIVOT может использоваться в следующих версиях Oracle/PLSQL:

Пример

Рассмотрим как использовать предложение PIVOT в Oracle.

Мы будем основывать наш пример на таблице под названием orders со следующим определением:

Для того чтобы показать вам данные для этого примера, мы будем выбирать записи из таблицы orders со следующим запросом SELECT:

Это записи таблицы orders . Мы будем использовать эти записи, чтобы продемонстрировать, как работает оператор PIVOT:

order_id customer_ref product_id
50001 SMITH 10
50002 SMITH 20
50003 ANDERSON 30
50004 ANDERSON 40
50005 JONES 10
50006 JONES 20
50007 SMITH 20
50008 SMITH 10
50009 SMITH 20

Теперь, создадим кросс-табличный запрос, используя следующий оператор PIVOT:

В этом примере, оператор PIVOT будет возвращать следующие результаты:

customer_ref 10 20 30
ANDERSON 0 0 1
JONES 1 1 0
SMITH 2 3 0

Теперь давайте разберем оператор PIVOT и объясним, как он работает.

Специфика полей для включения

Во-первых, мы хотим указать, какие поля включить в кросс-таблице. В этом примере мы хотим включить поля customer_ref и product_id . Это делается с помощью следующей части запроса:

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

Специфика агрегатной функции

Далее, при создании нашего запроса кросс-таблицы, нам необходимо указать агрегатную функцию. Вы можете использовать любую из функций, например: SUM, COUNT, MIN, MAX или AVG.

В этом примере мы будем использовать функцию COUNT. Это позволит подсчитать количество значений product_id , которые соответствуют нашим критериям. Это делается с помощью следующей части запроса:

Специфика PIVOT значений

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

В этом примере мы будем возвращать только следующие значения product_id : 10, 20, 30. В нашем кросс-табличном запросе эти значения станут нашими заголовками столбцов. Кроме того, обратите внимание, что эти значения являются конечным списком значений product_id , и не обязательно будут содержать все возможные значения.

Это делается с помощью следующей части запроса:

Теперь, когда мы совместим все это вместе, мы получим следующую PIVOT таблицу:


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

Поворот(Pivot)

Как известно, реляционные таблицы обычно представляются в матричном виде, то есть, они состоят из пар — столбец-значение. Рассмотрим, например, таблицу CUSTOMERS.

Когда из этой таблицы делается выборка:

Заметим, что данные представлены в виде строк значений: для каждого клиента строка называет его домашний штат и количество заказов, сделанных им в магазине. Всякий раз, когда клиент делает следующий заказ, столбец times_purchased обновляется.

Теперь рассмотрим случай, когда надо получить отчет о частоте заказов по каждому штату, то есть, сколько клиентов в конкретном штате сделали один, два, три заказа и т.д. На стандартном языке SQL это могло бы выглядеть следующем образом:

Это та информация, которая вам нужна, только ее несколько неудобно читать. Лучше было бы представить эти же самые данные в виде кросс-табличного отчета, в котором следовало бы вертикально расположить частоту покупок, а коды штатов — горизонтально, как в сводной таблице:

До Oracle Database11 вы сделали бы это посредством некоей сортировки функции декодирования для каждого значения и записали бы каждое неповторяющееся значение отдельным столбцом. Однако, такой способ неочевиден.

К счастью, сейчас имеется отличная новая возможность – PIVOT (ПОВОРОТ) для представления любого запроса в кросс-табличном формате, используя новый оператор, соответственно названный pivot . Теперь, когда в запросе написано:

Это показывает возможности оператора pivot. Коды штатов (state_codes) показываются в строке заголовка, а не в столбце. Иллюстративно обычный табулированный формат выглядит так:


Рис. 1. Обычное табулированное представление.

В кросс-табличном отчете вы хотите переместить столбец «Times Purchased» в строку заголовка, как показано на рис. 2. Столбец становится строкой, как если бы столбец был повернут на 90 градусов против часовой стрелки, чтобы стать строкой заголовка. Это фигуративное вращение (figurative rotation) должно иметь поворотную точку (pivot point), и в нашем случае точкой поворота служит выражение count(stat_code).


Рис.2. Транспонированное представление

Это выражение должно быть задано в синтаксисе запроса:

Вторая строка "for state_code . " ограничивает запрос только указанными значениями. Эта строка необходима, поэтому, к сожалению, нужно заранее знать возможные значения. Это ограничение смягчается в XML-формате запроса, описанном далее в этой статье.

Обратите внимание на строку заголовка в отчете:

Заголовки столбцов являются значениями из самой таблицы: это коды штатов. Сокращения могут говорить сами за себя, но допустим, вы хотите вывести названия штатов вместо сокращений, ("Connecticut " вместо "CT"). В таком случае нужно немного скорректировать запрос в фразе FOR, как показано ниже:

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

Разворот (Unpivot)

Если для материи существует антиматерия, то и для транспонирования ( pivot) должно существовать обратное транспонирование ("unpivot"), не так ли?

Действительно, существует естественная необходимость в обратной к pivot операции, так сказать, в обратном транспонировании. Предположим, что есть сводная таблица, которая отображает кросс-табличный отчет следующим образом:

Purchase Frequency New York Connecticut New Jersey Florida Missouri
0 12 11 1 0 0
1 900 14 22 98 78
2 866 78 13 3 9
. .

Теперь надо загрузить данные в реляционную таблицу CUSTOMERS:

Данные из сводной таблицы должны быть переведены в реляционный формат и после этого сохранены. Конечно, вы могли бы написать сложный скрипт для SQL*Loader или SQL-скрипт, использующий функцию DECODE для загрузки данных в таблицу CUSTOMERS. Или же можно воспользоваться обратным повороту (pivot) действием — UNPIVOT – и разбить столбцы, чтобы они стали строками, как это возможно сделать в Oracle Database 11g.

Это проще продемонстрировать на примере. Создадим для начала кросс-таблицу, используя оператор pivot:

В сводной таблице данные сохраняются следующим образом: каждый штат — это столбец в таблице ("New York", "Conn" и так далее).

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

Отметим, что каждое имя столбца стало значением в столбце STATE_CODE. Как Oracle узнал, что state_column — заголовок столбца? Он узнал это из следующего выражения в запросе:

Здесь вы определили, что значения "New York", "Conn" и т.д. — это значения нового столбца, называемого state_code, который вы хотите свернуть обратно (unpivoted). Посмотрим на фрагмент исходных данных:

Коль скоро имя столбца "New York" неожиданно стало значением в строке, где показать значение 33048, к какому столбцу его отнести? Ответ на этот вопрос находится в приведенном выше запросе прямо в выражении for в операторе unpivot. Как вы определите state_counts, таким и будет имя вновь создаваемого столбца в результирующем выводе.

Действие unpivot можно рассматривать как противоположное действию pivot, но не надо полагать, что одно из них может обратить то, что сделает другое. Например, в предыдущем примере вы создали таблицу CUST_MATRIX, использовав оператор pivot над таблицей CUSTOMERS. Затем был применен оператор unpivot к таблице CUST_MATRIX, но вы не получили в точности исходную таблицу CUSTOMERS. Вместо этого кросс-табличный отчет показал другой способ загрузки в реляционную таблицу. Таким образом, unpivot не отменяет действия, сделанные pivot, факт который нужно самым тщательным образом учитывать прежде, чем удалять (dropping) исходную таблицу после создания повернутой (pivoted) таблицы.

Некоторые очень интересные случаи использования unpivot, подобно рассмотренному ранее примеру, выходят за грани обычной обработки данных. Член-директор Oracle ACE Лукас Джеллема (Lucas Jellema) из компании Amis Technologies в заметке «Flexible Row Generator with Oracle 11g Unpivot Operator» показал, как генерировать строки специфичных данных для тестирования [От редакции FORS Magazine: перевод этой заметки приводится ниже в качестве приложения к данной статье]. В данной статье я буду использовать незначительно измененную форму его кода для генерации гласных английского алфавита:

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

XML Type

В предыдущем примере нужно было определить правильные коды штатов state_codes:

Это требование предполагает, что вы знаете, какие значения присутствуют в столбце state_column. А как построить запрос, если не известно, какие значения допустимы?

К счастью, существует другая форма операции pivot — XML, которая позволяет создавать повернутый отчет в XML-формате, когда можно определить специальный оператор ANY, вместо литеральных значений. Вот пример:

Результат выводится как CLOB, поэтому прежде чем выполнять запрос, надо удостовериться, что параметр LONGSIZE устанавливает достаточное значение.

Существуют два четких различия (помечены жирным шрифтом) этого запроса по сравнению с исходной операцией pivot. Во-первых, вы пишете pivot xml вместо просто pivot. Это создает вывод в формате XML. Во-вторых, выражение for содержит for state_code in (any) вместо длинного списка значений. XML позволяет использовать ключевое слово ANY, и вам не нужно вводить значения strong>state_code. Вот выход:

Как можно видеть, тип столбца STATE_CODE_XML — действительно XMLTYPE, а <PivotSet> — корневой элемент (root element). Каждое значение представлено парами элементов «имя–значение». Вы можете использовать этот выход в любом XML-парсере для создания более наглядного выхода.

В дополнение к выражению ANY можно написать подзапрос. Предположим, существует список предпочтительных штатов, и вы хотите выбрать строки только для них. Вы сохранили коды предпочитаемых штатов в новой таблице, названной preferred_states:

В подзапросе фразы for может быть все, что вы захотите. Например, если надо выбрать все записи, без ограничительных условий на предпочтительные штаты, во фразе for можно использовать следующую конструкцию:

Подзапрос должен возвращать неповторяющиеся значения, иначе запрос будет ошибочным. Именно поэтому в запросе был использован оператор DISTINCT .

Заключение

Оператор Pivot добавляет очень важную и полезную функциональность в язык SQL. Вместо создания замысловатого непрозрачного кода с большим количеством функций-декодеров (decode functions), можно использовать функцию pivot для создания кросс-табличного представления любой реляционной таблицы. Точно так же можно преобразовать кросс-табличное представление в обычную реляционную таблицу, используя операцию unpivot. Выход pivot может быть как текстовым, так и в XML-формате. В последнем случае не нужно определять область значений, к которым применяется этот оператор.

За более подробной информацией об операторах pivot и unpivot следует обратиться к документу Oracle Database 11g SQL Language Reference

Приложение

Лукас Джеллема

Универсальный генератор строк для оператора Unpivot в Oracle11g

(Flexible Row Generator with Oracle 11g Unpivot Operator, by Lucas Jellema)

Оператор Unpivot в Oracle11g предоставляет нам новый способ сгенерировать строки с великолепными возможностями управления над значениями в строках и более компактный и изящный синтаксис, чем альтернатива UNION ALL.

Давайте рассмотрим простой пример.

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

Единственным select-предложением выборки из DUAL, а не шестью запросами из DUAL, которые по UNION ALL [соединяются] вместе, мы выбираем шесть требуемых значений, как из индивидуальных столбцов – от a до f. Оператор UNPIVOT, который мы затем применяем к этому результату, берет единственную строку с шестью столбцами и превращает ее в шесть строк с двумя столбцами, один из которых содержит имя исходного столбца исходной строки, а другой — значение в том исходном столбце:

Результат этого запроса таков: :

Замечание:

в ситуациях, где требуется прямая генерация большого количества строк, прием strong>«CONNECT BY» все еще будет превалирующим. Например, чтобы сгенерировать алфавит, следует использовать предложение типа:

Однако, чтобы сгенерировать поднабор, скажем, все гласные из алфавита, подход с применением оператора strong>UNPIVOT может оказаться полезным.

В версии 11g появились функции Pivot/Unpivot(которые сначала появились в MS SQL 2005), позволяющие динамически разносить вертикальные данные по столбцам как нам удобно.




Допустим у вас есть таблица customers:

select cust_id, state_code, times_purchased
from customers
order by cust_id;

показывает идентификатор заказчика, код штата, и сколько раз он что-либо покупал:

Нам нужно узнать количество заказчиков сгрупированных по каждому штату и по количеству их заказов:

select state_code, times_purchased, count(1) cnt
from customers
group by state_code, times_purchased;

Этот запрос выдает то, что нам нужно, но гораздо удобнее был бы в таком виде:

До версии 11g такое пришлось бы делать многократно повторяя sum(decode(state_code,'CT',1,0) «CT», sum(decode(state_code,'NY',1,0) «NY»,… Но благодаря функции pivot мы можем это сделать просто:

select * from (
select times_purchased as «Puchase Frequency», state_code
from customers t
)pivot(
count(state_code)
for state_code in ('NY' as "New York",'CT' «Connecticut»,'NJ' "New Jersey",'FL' «Florida»,'MO' as «Missouri»)
)
order by 1
/

Функция Unpivot совершает противоположные преобразования.

Тем же, кто еще не мигрировал на 11g, могу предложить свой модифицированный код Тома Кайта:

create or replace type varchar2_table as table of varchar2(4000);
/
create or replace package PKG_PIVOT is

function pivot_sql (
p_max_cols_query in varchar2 default null
, p_query in varchar2
, p_anchor in varchar2_table
, p_pivot in varchar2_table
, p_pivot_head_sql in varchar2_table default varchar2_table()
)
return varchar2;

function pivot_ref (
p_max_cols_query in varchar2 default null
, p_query in varchar2
, p_anchor in varchar2_table
, p_pivot in varchar2_table
, p_pivot_name in varchar2_table default varchar2_table()
)
return sys_refcursor;

end PKG_PIVOT;
/
create or replace package body PKG_PIVOT is
/**
* Function returning query
*/
function pivot_sql (
p_max_cols_query in varchar2 default null
, p_query in varchar2
, p_anchor in varchar2_table
, p_pivot in varchar2_table
, p_pivot_head_sql in varchar2_table
) return varchar2
is
l_max_cols number;
l_query varchar2(4000);
l_pivot_name varchar2_table:=varchar2_table();
k integer ;
c1 sys_refcursor;
v varchar2(30);
begin
-- Получаем кол-во столбцов
if (p_max_cols_query is not null ) then
execute immediate p_max_cols_query
into l_max_cols;
else
raise_application_error (-20001, 'Cannot figure out max cols' );
end if ;

-- Собираем по кускам необходимый нам запрос
l_query := 'select ' ;

for i in 1 .. p_anchor. count loop
l_query := l_query || p_anchor (i) || ',' ;
end loop;
--Получаем названия колонок
k:=1;
if p_pivot_head_sql. count =p_pivot. count
then
for j in 1 .. p_pivot. count loop
open c1 for p_pivot_head_sql(j);
loop
fetch c1 into v;
l_pivot_name.extend(1);
l_pivot_name(k):=v;
EXIT WHEN c1%NOTFOUND;
k:=k+1;
end loop;
end loop;
end if ;

-- Добавляем колонки с полученными названиями
-- в виде "max(decode(rn,1,C,null)) c_name+1_1"
for i in 1 .. l_max_cols loop
for j in 1 .. p_pivot. count loop
l_query := l_query || 'max(decode(rn,' || i || ',' || p_pivot (j) || ',null)) '
|| '"' ||l_pivot_name ((j-1)*l_max_cols+i) || '"' || ',' ;
end loop;
end loop;

-- Вставляем исходный запрос
l_query := rtrim (l_query, ',' ) || ' from ( ' || p_query || ') group by ' ;

-- Группируем по колонкам
for i in 1 .. p_anchor. count loop
l_query := l_query || p_anchor (i) || ',' ;
end loop;

l_query := rtrim (l_query, ',' );

-- Возвращаем готовый SQL запрос
return l_query;
end ;

/**
* Функция возвращающая курсор на выполненный запрос
*/
function pivot_ref (
p_max_cols_query in varchar2 default null
, p_query in varchar2
, p_anchor in varchar2_table
, p_pivot in varchar2_table
, p_pivot_name in varchar2_table
) return sys_refcursor
is
p_cursor sys_refcursor;
begin
execute immediate 'alter session set cursor_sharing=force' ;
open p_cursor for pkg_pivot.pivot_sql (
p_max_cols_query
, p_query
, p_anchor
, p_pivot
, p_pivot_name
);
execute immediate 'alter session set cursor_sharing=exact' ;
return p_cursor;
end ;
end PKG_PIVOT;
/

begin
:qq:=pkg_pivot.pivot_sql(
'select count(distinct trunc(dt)) from actions'
, 'select e.name name,sum(a.cnt) sum_cnt,a.dt,dense_rank() over(order by dt) rn from actions a left join emp e on e.id=a.emp group by e.name,a.dt'
, varchar2_table( 'NAME' )
, varchar2_table( 'SUM_CNT' )
, varchar2_table( 'select distinct ' 'Date ' '||trunc(dt) from actions' )
);
:qc :=pkg_pivot.pivot_ref(
'select count(distinct trunc(dt)) from actions'
, 'select e.name,sum(a.cnt) sum_cnt,a.dt,dense_rank() over(order by dt) rn from actions a left join emp e on e.id=a.emp group by e.name,a.dt'
, varchar2_table( 'NAME' )
, varchar2_table( 'SUM_CNT' )
, varchar2_table( 'select distinct ' 'Date ' '||trunc(dt) from actions' )
);
end ;

* This source code was highlighted with Source Code Highlighter .

Читайте также: