Месячный Архив: Апрель 2016

SQL and PL/SQL

Отчеты по самодокументированной базе данных

Хорошей практикой в разработке баз данных Oracle являются комментарии к объектам физического и логического слоя (таблицам, представлениям, полям) — такая БД является самодокументированной. Вместе со стандартом именования объектов это значительно облегчает жизнь команды разработчиков, облегчает введение в должность новых сотрудников.

Однако приходится иногда делать отчет с группировкой по тем или иным объектам так, чтобы он хорошо выглядел в технической документации по модулю. Приводим пример запроса, который извлекает из БД документацию по интерфейсным представлениям бизнес-модуля «Форма 302». Использованы аналитические функции LAG (предыдущее значение согласно сортировке) и DENSE_RANK (порядковый номер согласно сортировке).

  1. SELECT CASE
  2. WHEN NVL(LAG(uv1.view_name)
  3. OVER (ORDER BY uv1.view_name, tc1.column_name), '#') <> uv1.view_name
  4. THEN DENSE_RANK()
  5. OVER (ORDER BY uv1.view_name)
  6. ELSE NULL
  7. END AS r_num,
  8. CASE
  9. WHEN NVL(LAG(uv1.view_name)
  10. OVER (ORDER BY uv1.view_name, tc1.column_name), '#') <> uv1.view_name
  11. THEN uv1.view_name
  12. ELSE NULL
  13. END AS first_view_name,
  14. tc1.column_name,
  15. cc1.comments
  16. FROM USER_VIEWS uv1
  17. LEFT JOIN USER_TAB_COLUMNS tc1
  18. ON tc1.table_name = uv1.view_name
  19. LEFT JOIN USER_COL_COMMENTS cc1
  20. ON cc1.table_name = uv1.view_name
  21. AND cc1.column_name = tc1.column_name
  22. WHERE LOWER(uv1.view_name) LIKE '%302%gui%v%' /* используйте свои критерии */
  23. ORDER BY uv1.view_name, tc1.column_name

Часть результатов, включая недокументированные представления, пустая — есть где поработать техническим писателям!

1 CBR_302_GUI_DICT_BS_V BUSINESS_SIZE_CD
BUSINESS_SIZE_NAME
SORT_ID
2 CBR_302_GUI_DICT_OKATO_V OKATO_CD
OKATO_CODE
SORT_ID
3 CBR_302_GUI_DICT_OKVED_V OKVED_CD
OKVED_CODE
SORT_ID
4 CBR_302_GUI_OKATO_V AS_OF_DATE Отчетная дата
FDDR_ID ID ФДП-отчета
OKATO Код Окато
SECT_ID Код раздела
Новости

Мужской квест… год спустя

Квест Диверсант
Спустя год наши коллеги-женщины опять подарили нам участие в квесте. За год из команды ушел Дима Грачев, а пришли новые сотрудники Виталий и Евгений. И если мы с Сергеем знали, с чем столкнемся, то для новичков квест был в диковинку. На самом деле час (58 минут 40 секунд) в двух комнатах с участием актера и холостой стрельбы из боевых пистолетов вышли очень напряженными, и если бы не некоторые подсказки, нас бы точно застрелили нам не удалось бы выбраться из запертых комнат и не пришлось бы сломя голову бежать куда глаза глядят. Было офигенно и с атмосферой Великой Отечественной Войны.

SQL and PL/SQL

Super Merge

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

Решение в виде одного оператора Merge предложил Максим Медведев:

  1. MERGE INTO test_clients dest
  2. USING
  3. (WITH f AS
  4. (SELECT c.ROWID AS rid,
  5. NVL(p.client_id, c.id) AS id,
  6. p.phone,
  7. p.phone_type,
  8. CASE
  9. WHEN c.id IS NULL THEN 'I'
  10. WHEN p.client_id IS NULL THEN 'D'
  11. ELSE
  12. CASE ROW_NUMBER()
  13. OVER (PARTITION BY p.client_id
  14. ORDER BY p.phone)
  15. WHEN 1 THEN 'U'
  16. ELSE 'I'
  17. END
  18. END AS r,
  19. ROW_NUMBER()
  20. OVER (PARTITION BY NVL(c.id, p.client_id)
  21. ORDER BY 1) AS rn
  22. FROM test_phones p
  23. FULL OUTER JOIN
  24. test_clients c
  25. ON c.id = p.client_id AND NVL(c.phone, p.phone) = p.phone
  26. WHERE (p.client_id IS NOT NULL AND NVL(c.phone_type, '-') != p.phone_type)
  27. OR (p.client_id IS NULL))
  28. SELECT f.*, s.name
  29. FROM f JOIN (SELECT DISTINCT id, name FROM test_clients) s ON s.ID = f.ID) src
  30. ON (dest.ROWID = src.rid AND src.r IN ('U', 'D'))
  31. WHEN MATCHED THEN
  32. UPDATE SET phone = src.phone,
  33. phone_type = src.phone_type
  34. DELETE WHERE src.r = 'D' AND src.rn > 1
  35. WHEN NOT MATCHED THEN
  36. INSERT VALUES (src.id,
  37. src.name,
  38. src.phone,
  39. src.phone_type);

Теперь можно изучить результат, попробовать добавлять, менять и удалять данные из таблицы с номерами телефонов

  1. INSERT INTO test_phones
  2. VALUES (2, '8(496)245-55-86', 'Второй домашний');
  3.  
  4. DELETE FROM test_phones
  5. WHERE client_id = 2 AND ROWNUM <= 1;