Создание функций Oracle — как уменьшить шаблонный код

#oracle #function #plsql #boilerplate

#Oracle #функция #plsql #шаблонный

Вопрос:

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

Я новичок в SQL, поэтому мне интересно, есть ли какой-нибудь чистый способ определить функцию с параметрами 3 column name и 4-м параметром, который выдает агрегатную функцию.

 SELECT *
FROM (
  SELECT SDDCTO, SDSRP4, SDSOQS
  FROM MyTable
)
PIVOT (
  sum(SDSOQS)  
  for SDDCTO 
  IN ('EB' AS EB 
      'EL' AS EL, 
      'ER' AS ER, 
      'ES' AS ES, 
      'E1' AS E1, 
      'E2' AS E2, 
      'E5' AS E5 
      'E9' AS E9
      )
)
ORDER BY SDSRP4
;

-- Same query with different columns and aggregate function
SELECT *
FROM (
  SELECT SDDCTO, SDMCU, SDAEXP
  FROM MyTable
)
PIVOT (
  avg(SDAEXP)  
  for SDDCTO 
  IN ('EB' AS EB, 
      'EL' AS EL, 
      'ER' AS ER, 
      'ES' AS ES, 
      'E1' AS E1, 
      'E2' AS E2, 
      'E5' AS E5, 
      'E9' AS E9
      )
)
ORDER BY SDMCU
;
  

Ответ №1:

Для проектов реального мира ранее вы освоитесь с dyanmic SQL и функциями в предложении from — в долгосрочной перспективе это лучше.

Я ненавижу писать один и тот же или похожий код дважды. Так что лучшим вариантом будет что-то вроде

 select ...
from table(plsql_package ( parameters ))
  

Комментарии:

1. Какой, по вашему мнению, наилучший подход, документация или учебное пособие для начала изучения динамического SQL? Честно говоря, на первый взгляд это кажется немного жестковатым.

2. Времена изменились — теперь интерфейс execute immediate делает это довольно простым. Так что просто посмотрите документацию execute immediate и создавайте оттуда. Затем смотрите курсоры для этого. У вас есть хороший проект для создания, который облегчает процесс обучения.

Ответ №2:

При этом не используется функция, которая должна была бы использовать динамический SQL, вероятно, будет более сложной в обслуживании, чем два отдельных запроса; но вы могли бы создать представление, объединяющее оба агрегата в одном:

 CREATE VIEW MyView AS
SELECT *
FROM (
  SELECT SDDCTO, SDSRP4, SDSOQS, SDMCU, SDAEXP
  FROM MyTable
)
PIVOT (
  sum(SDSOQS) as SUM_SDOQS,
  avg(SDAEXP) as AVG_SDAEXP
  for SDDCTO 
  IN ('EB' AS EB, 
      'EL' AS EL, 
      'ER' AS ER, 
      'ES' AS ES, 
      'E1' AS E1, 
      'E2' AS E2, 
      'E5' AS E5, 
      'E9' AS E9
      )
)
;
  

… и затем запрашивайте представление в ваших двух запросах вместо таблицы:

 SELECT SDSRP4, EB_SUM_SDOQS, EL_SUM_SDOQS, ER_SUM_SDOQS, ES_SUM_SDOQS,
  E1_SUM_SDOQS, E2_SUM_SDOQS, E5_SUM_SDOQS, E9_SUM_SDOQS
FROM MyView
ORDER BY SDSRP4
;

SELECT SDMCU, EB_AVG_SDAEXP, EL_AVG_SDAEXP, ER_AVG_SDAEXP, ES_AVG_SDAEXP,
  E1_AVG_SDAEXP, E2_AVG_SDAEXP, E5_AVG_SDAEXP, E9_AVG_SDAEXP
FROM MyView
ORDER BY SDMCU
;
  

Хотя я не уверен, что это принесет вам столько пользы…

Комментарии:

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

2. Вы могли бы добавить больше агрегатов к сводке в представлении и больше столбцов к внутреннему запросу; при условии, что всем им нужны одни и те же предложения pivot ‘in’ и ‘for’. Я думаю, что это либо это, либо (возможно, конвейерная) функция, использующая динамический SQL; но кто-то другой может придумать что-то, о чем я, конечно, не подумал.