---> select distinct p.code from elma.deals d join elma.deals_services siv on (d.__id=ANY(siv.deal)) join elma.service_parametrs p on (p.__id = ANY(siv.params)) join elma.companies k on (d._companies=k.__id) join elma.additional_agreements aa on (aa.__id = ANY(siv.additional_agreement)) join elma.aggrements a on (aa.contract_uid = a.__id) where p.type=3 AND aa.is_actual order by p.code qCode:#getTickCount()-request.startTickCount# select upper(trim(k.id_klienta)) as wz, k.__id::text as contragent_uid, k.__name as contragent, c.__name as responsible, c.email as responsible_email, c.__status_status as responsible_status from elma.companies k LEFT OUTER JOIN elma.comp_users_ext c on (k.responsible=c.__id) where k."__deletedAt" IS NULL AND k.id_klienta like qContragent:#getTickCount()-request.startTickCount# WITH status(status, status_id, probability_pc) AS (VALUES ('Закрыта неуспешно',7,0),('Закрыта успешно',6,100),('Договор подписан',5,100),('Договор на подписании',15,90),('Договор на согласовании',12,70), ('Тестирование',8,50),('Отправлено ТКП',10,20),('Внутреннее согласование',37,10),('Проработка решения',4,10),('Сбор потребностей',1,0) ) select upper(trim(k.id_klienta)) as wz, k.__id::text as contragent_uid, p.code, SUBSTRING(p.code FROM 1 FOR LENGTH(p.code) - 2) as sku_code, p.price, p.discount as component_discount, k.__name as contragent, a.__name as contract, a.__id::text as contract_uid, d.__name as deal, d.__id::text as deal_uid, siv.abstractive_service as abstract_service, siv.modifier, siv.user_description, siv.hash as line_key ,siv.date_nop as serviceStart, siv.date_end as serviceEnd, siv.discount as service_discount, siv.cnt as service_quantity /*,p.__name as line_key*/, p.ed_izm, p.name as component, p.type, p.cnt*coalesce(siv.cnt,1) as component_quantity, aa.__name as additional_agreement, aa.__id::text as additional_agreement_uid --, aa.is_actual ,siv.end_customer as end_customer_uid ,z.id_klienta as end_customer_wz ,z.__name as end_customer_name --,ft.free_tier ,s.probability_pc ,siv.__id::text as siv_uid from elma.deals d JOIN STATUS s ON (d.__status_status = s.status_id) join elma.deals_services siv on (d.__id=ANY(siv.deal)) --join elma.service_parametrs p on (p.__id = ANY(siv.params)) LEFT JOIN LATERAL unnest(siv.params) AS arr(param_id) ON TRUE JOIN elma.service_parametrs p ON (p.__id = arr.param_id) join elma.companies k on (d._companies=k.__id) join elma.additional_agreements aa on (aa.__id = ANY(siv.additional_agreement)) join elma.aggrements a on (aa.contract_uid = a.__id) left outer join elma.companies z on (siv.end_customer=z.__id) where p.type IN (2,3) AND aa.is_actual AND siv.date_nop < AND (siv.date_end > OR siv.date_end IS NULL)/*****/ AND aa."__deletedAt" IS NULL AND a."__deletedAt" IS NULL AND d."__deletedAt" IS NULL AND siv."__deletedAt" IS NULL AND p."__deletedAt" IS NULL AND p.cnt <> 0 --похоже, только так можно отфильтровать паразитные строки с нулем AND k.id_klienta like AND p.code like --AND k.id_klienta='WZ01031' --AND k.id_klienta='WZ01112' order by k.id_klienta, p.code qSpec:#getTickCount()-request.startTickCount# select max(dt_load) as dt_load from elma.deals_services qSivAge:#getTickCount()-request.startTickCount# select upper(trim(m.clientid)) as wz, vdc.name as vdc, case WHEN vdc.name LIKE '%-v1cl1%' THEN 'iaas.ngc.i29' WHEN vdc.name LIKE '%-v1cl2%' THEN 'iaas.ngc.i31' WHEN vdc.name LIKE '%-v1cl4%' THEN 'iaas.ngc.a28' WHEN vdc.name LIKE '%-v1cl5%' THEN 'iaas.ngc.a45' WHEN vdc.name LIKE '%-v1cl6%' THEN 'iaas.ngc.i28' WHEN vdc.name LIKE '%-v1cl7%' THEN 'iaas.ngc.a40' -- *** смапить все кластера else '['||vdc.name||']' end as code, sum(cpu_used/cpu_speed)/4. as core_h, sum(mem_used)/1024/4. as gb_h from ngcloud_ru.capacity_resource join ngcloud_ru.vdc on capacity_resource.vdc_id=vdc.id join ngcloud_ru.tenant t on (t.id=vdc.tenant_id) LEFT JOIN LATERAL ( SELECT m.clientid FROM ngcloud_ru.tenant_metadata tm JOIN ngcloud_ru.metadata m ON tm.metadata_id = m.id WHERE tm.tenant_id = t.id ORDER BY tm.ts DESC, tm.metadata_id DESC LIMIT 1 ) m ON true where length(m.clientid) > 0 AND capacity_resource.timestamp >= AND capacity_resource.timestamp < AND m.clientid like group by m.clientid, vdc.name order by m.clientid, vdc.name; qComputing:#getTickCount()-request.startTickCount# select max(capacity_resource.timestamp) as dt_load from ngcloud_ru.capacity_resource qComputingAge:#getTickCount()-request.startTickCount# select upper(trim(m.clientid)) as wz, vdc.name as vdc, case WHEN vdc.name LIKE '%-v1cl1%' THEN 'iaas.ngc.i29' WHEN vdc.name LIKE '%-v1cl2%' THEN 'iaas.ngc.i31' WHEN vdc.name LIKE '%-v1cl4%' THEN 'iaas.ngc.a28' WHEN vdc.name LIKE '%-v1cl5%' THEN 'iaas.ngc.a45' WHEN vdc.name LIKE '%-v1cl6%' THEN 'iaas.ngc.i28' WHEN vdc.name LIKE '%-v1cl7%' THEN 'iaas.ngc.a40' -- *** смапить все кластера ELSE '['||vdc.name||']' END || CASE WHEN storage_profile_types.name ILIKE '%FASTSSD%' THEN '.fstssd' WHEN storage_profile_types.name ILIKE '%SSD%' THEN '.ssd' WHEN storage_profile_types.name ILIKE '%SAS%' THEN '.sas' WHEN storage_profile_types.name ILIKE '%SATA%' THEN '.sata' ELSE '.['|| storage_profile_types.name ||']' END as code, storage_profile_types.name as profile, sum("limit")/1024./4./ as gb_m_limit, -- 4 потому, что сбор раз в 15 минут sum(used)/1024./4./ as gb_m_used from ngcloud_ru.capacity_storage join ngcloud_ru.vdc on capacity_storage.vdc_id=vdc.id join ngcloud_ru.tenant t on t.id=vdc.tenant_id LEFT JOIN LATERAL ( SELECT m.clientid FROM ngcloud_ru.tenant_metadata tm JOIN ngcloud_ru.metadata m ON tm.metadata_id = m.id WHERE tm.tenant_id = t.id ORDER BY tm.ts DESC, tm.metadata_id DESC LIMIT 1 ) m ON true join ngcloud_ru.storage_profile_types on capacity_storage.storage_profile_types_id=storage_profile_types.id where length(m.clientid) > 0 AND capacity_storage.timestamp >= AND capacity_storage.timestamp < AND m.clientid like group by m.clientid, vdc.name, storage_profile_types.name order by m.clientid, vdc.name, storage_profile_types.name; qStorage:#getTickCount()-request.startTickCount# select max(capacity_storage.timestamp) as dt_load from ngcloud_ru.capacity_storage qStorageAge:#getTickCount()-request.startTickCount# select /* *** ниже дублирование кода агрегации - round ceil etc, обратить внимание при правке */ (sum(bucket_stat.usage_rgw_main_size_actual)/) as vol_B ,sum(bucket_stat.usage_rgw_main_size_actual)//1024/1024/1024 as vol_GB ,case when bucket_stat.placement_id = 1 then 'paas.s3.ceph' when bucket_stat.placement_id = 2 then 'paas.s3.cphc' else '['|| TO_CHAR(bucket_stat.placement_id,'FM9') ||']' end as code ,bucket_stat.placement_id --,'WZ'||lpad(bucket_stat.owner, 5, '0') as wz -- *** здесь не число, а строка, поэтому другая формула обратной сборки WZ ,bucket_stat.owner as s3_user_name ,bucket_info.name as bucket from s3billing.bucket_stat join s3billing.placement ON s3billing.placement.id=bucket_stat.placement_id join s3billing.bucket_info ON bucket_info.id=bucket_stat.bucket_id where bucket_stat.placement_id in (1,2) AND bucket_stat.timestamp_addition >= AND bucket_stat.timestamp_addition < AND 'WZ'||lpad(bucket_stat.owner, 5, '0') like /*выглядит очень неэффективным запросом*/ group by bucket_stat.owner, bucket_stat.placement_id, bucket_info.name order by bucket_stat.owner, bucket_stat.placement_id, bucket_info.name; qS3Vol:#getTickCount()-request.startTickCount# select max(timestamp_addition) as dt_load from s3billing.bucket_stat qS3VolAge:#getTickCount()-request.startTickCount# select sum( CASE WHEN (category.name LIKE 'get%' OR category.name LIKE 'head%' OR category.name LIKE 'options%' ) THEN ops ELSE 0 END ) as get ,(sum( CASE WHEN (category.name LIKE 'get%' OR category.name LIKE 'head%' OR category.name LIKE 'options%' ) THEN ops ELSE 0 END )/10000) as get_10k ,sum( CASE WHEN (category.name LIKE 'put%' OR category.name LIKE 'post%' OR category.name LIKE 'patch%' OR category.name LIKE 'list%' ) THEN ops ELSE 0 END ) as put ,(sum( CASE WHEN (category.name LIKE 'put%' OR category.name LIKE 'post%' OR category.name LIKE 'patch%' OR category.name LIKE 'list%' ) THEN ops ELSE 0 END )/10000) as put_10k ,(sum(bytes_sent)) as bytes_sent ,(sum(bytes_sent)/1024/1024/1024) as bytes_sent_GB ,case when bucket_info.placement_id = 1 then 'paas.s3.ceph' when bucket_info.placement_id = 2 then 'paas.s3.cphc' else '['|| TO_CHAR(bucket_info.placement_id,'FM9') ||']' end as code --*** зачем делать там ключ bigint ,bucket_info.placement_id --category.name ,bucket_info.name as bucket ,user_info.name as s3_user_name --,'dummy' AS WZ from s3billing.usage_bucket_by_user join s3billing.user_info on usage_bucket_by_user.user_id=user_info.id join s3billing.category on usage_bucket_by_user.category_id=category.id join s3billing.bucket_info on usage_bucket_by_user.bucketid=bucket_info.id join s3billing.placement on bucket_info.placement_id=placement.id where 1=1 AND usage_bucket_by_user.time >= AND usage_bucket_by_user.time < AND 'WZ'||lpad(user_info.name, 5, '0') like /*выглядит очень неэффективным запросом*/ group by user_info.name, bucket_info.placement_id, bucket_info.name order by user_info.name, bucket_info.placement_id, bucket_info.name; qS3OpsTrf:#getTickCount()-request.startTickCount# select max(time) as dt_load from s3billing.usage_bucket_by_user qS3OpsTrfAge:#getTickCount()-request.startTickCount# WITH current_state AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY instance_uid ORDER BY version DESC) as rn FROM instance_state ) SELECT s.instance_data->'job'->>'contragentCode' as wz, s.instance_data->'job'->>'userId' AS user_id, e.display_name FROM current_state s JOIN instance e on (s.instance_uid=e.instance_uid) WHERE rn = 1 AND e.service_id=12 qS3User:#getTickCount()-request.startTickCount# WITH card (model, cluster, code) as (VALUES ('A100', 'msk1-pub02-i31', 'iaas.ngc.i31.gpu80'), ('H100', 'msk1-pub07-a40', 'iaas.ngc.a40.gpuh100'), -- h200 нет в отчете ('RTX 4090', 'msk1-pcs05-a45', 'iaas.ngc.a45.gpu4090'), -- нет такого в каталоге ('RTX 5090', 'msk1-pcs05-a45', 'iaas.ngc.a45.gpu5090'), ('RTX 6000', 'msk1-pub07-a40', 'iaas.ngc.a40.gpu6k'), ('nvidia_a16-16q', 'msk1-pub07-a40', 'iaas.ngc.a40.gpu16'), ('nvidia_a16-16q', 'msk1-pub01-i29', 'iaas.ngc.i29.gpu16'), ('nvidia_a16-1b', 'msk1-pub07-a40', 'iaas.ngc.a40.gpu1'), ('nvidia_a16-2q', 'msk1-pub07-a40', 'iaas.ngc.a40.gpu2'), ('nvidia_a16-4q', 'msk1-pub07-a40', 'iaas.ngc.a40.gpu4'), ('nvidia_rtx_pro_6000_blackwell_dc-4q', 'msk1-pub07-a40', 'iaas.ngc.a40.gpu6k4') ) SELECT item.key AS gpu_model, c.code ,SUM(item.value::int*p.value)/12. AS gpu_h -- 5 minute sampling ,vm.obj_id, upper(trim(vm.client_id)) as WZ FROM gpu.vm CROSS JOIN LATERAL jsonb_each(vm.gpu) AS item(key, value) LEFT OUTER JOIN card c ON (item.key=c.model) JOIN gpu.vm_power_state p ON (vm.obj_uuid=p.obj_uuid) WHERE NOT vm.is_template AND p.ts >= AND p.ts < AND vm.client_id ilike GROUP BY vm.client_id, item.key, c.code, vm.obj_id ORDER BY vm.client_id, item.key, c.code, vm.obj_id; qGpu:#getTickCount()-request.startTickCount# select max(dt_load) as dt_load from gpu.vm qGpuAge:#getTickCount()-request.startTickCount# select wz, code || '.vcpu' as code, core_h as raw_metric, core_h as metric, 'core-h' as unit, vdc as dimension1 from qComputing where code || '.vcpu' like union all select wz, code || '.ram', gb_h as raw_metric, gb_h as metric, 'GByte-h' as unit, vdc as dimension1 from qComputing where code || '.ram' like union all select wz, code, gb_m_used as raw_metric, gb_m_used as metric, 'GByte-m' as unit, vdc ||':'|| profile as dimension1 from qStorage where code like union all select wz, code || '.vol',vol_b as raw_metric, vol_gb as metric, 'GByte-m' as unit, bucket as dimension1 from qS3Vol s left outer join qS3User u ON (s.s3_user_name = u.user_id) where code || '.vol' like union all select wz, code || '.get', get as raw_metric, get_10k as metric, '10k' as unit, bucket as dimension1 from qS3OpsTrf s left outer join qS3User u ON (s.s3_user_name = u.user_id) where code || '.get' like union all select wz, code || '.put', put as raw_metric, put_10k as metric, '10k' as unit, bucket as dimension1 from qS3OpsTrf s left outer join qS3User u ON (s.s3_user_name = u.user_id) where code || '.put' like union all select wz, code || '.trf', bytes_sent as raw_metric, bytes_sent_gb as metric, 'GByte' as unit, bucket as dimension1 from qS3OpsTrf s left outer join qS3User u ON (s.s3_user_name = u.user_id) where code || '.trf' like union all select wz, code, gpu_h as raw_metric, gpu_h as metric, 'h' as unit, obj_id as dimension1 from qGpu where code like order by wz, code qUnifiedMetric:#getTickCount()-request.startTickCount# /*EXPLAIN PLAN FOR*/ WITH metric as (select count(*) as cnt, wz, code, dimension1, max(unit) as unit, max(raw_metric) as raw_metric, max(metric) as metric from qUnifiedMetric group by wz,code, dimension1 ), spec as (select count(*) as cnt, wz, sku_code, max(end_customer_wz) as wze, max(code) as code, max(component_quantity) as component_quantity, max(service_quantity) as service_quantity, max(service_discount) as service_discount, max(type) as type, max(ed_izm) as ed_izm, max(coalesce(service_quantity*(100.-service_discount)/100.*price*(100.-component_discount)/100., 0.)) as discounted_price, max(serviceStart) as serviceStart, max(serviceEnd) as serviceEnd, max(line_key) as line_key, max(price) as price, max(component_discount) as component_discount, max(contract) as contract, max(contract_uid) as contract_uid, max(additional_agreement) as additional_agreement, max(additional_agreement_uid) as additional_agreement_uid, max(deal) as deal, max(deal_uid) as deal_uid, max(abstract_service) as abstract_service, max(modifier) as modifier, max(component) as component, max(user_description) as user_description, max(CASE type WHEN 1 THEN 'install' WHEN 2 THEN 'fix' WHEN 3 THEN 'payg' ELSE NULL END) as pricing_model from qSpec group by wz, sku_code ) SELECT coalesce(coalesce(c.wz, m.wz), s.wz) as wz s.wz as wzs c.wz as wzc m.wz as wzm s.wze coalesce(s.sku_code, m.code) as code s.code as s_code s.sku_code as sku_code s.component_quantity s.service_quantity s.service_discount s.type s.ed_izm coalesce(s.service_quantity*(100.-s.service_discount)/100.*s.price*(100.-s.component_discount)/100., 0.) as discounted_price s.serviceStart s.serviceEnd s.line_key s.price s.component_discount s.contract s.contract_uid s.additional_agreement s.additional_agreement_uid s.deal s.deal_uid s.abstract_service s.modifier s.component s.user_description CASE s.type WHEN 1 THEN 'install' WHEN 2 THEN 'fix' WHEN 3 THEN 'payg' ELSE NULL END as pricing_model m.code as m_code m.dimension1 m.unit m.raw_metric m.metric coalesce(ft.free_tier,0) as free_tier c.contragent c.contragent_uid c.responsible c.responsible_email c.responsible_status s.cnt AS occurrences_spec m.cnt AS occurrences_metric FROM metric m INNER FULL JOIN spec s ON (m.wz=s.wz AND m.code=s.sku_code) -- сервис или компонент, но не цена с типом тарификации, суффикс типа тарификации отрезан --left outer join (values ('paas.s3.ceph.vol-m',1),('paas.s3.ceph.get-m',10),('paas.s3.ceph.put-m',1),('paas.s3.ceph.trf-m',100)) as ft(code,free_tier) ON (m.code=ft.code) left outer join (values ('paas.s3.ceph.vol',1),('paas.s3.ceph.get',10),('paas.s3.ceph.put',1),('paas.s3.ceph.trf',100)) as ft(code,free_tier) ON (m.code=ft.code) LEFT JOIN qContragent c ON ((coalesce(s.wz,m.wz))=(c.wz)) -- пробелы в составе WZ стоили нескольких часов работы WHERE 1=1 AND EXISTS(select * from spec qs where qs.wz=m.wz AND qs.type=3) AND ( m.wz like OR s.wz like OR c.wz like ) --AND s.wz='WZ01031' --AND s.wz='WZ01762 ORDER BY coalesce(coalesce(c.wz, m.wz), s.wz), coalesce(s.code, m.code) qCharge:#getTickCount()-request.startTickCount# Отчет по PAYG Факт потребления по основной базе биллинга, спецификации из DWH. Без детализации по бакетам, VDC и т.д. Все запросы кэшированы на 25 минут.
Внимание! округление вверх при расчете метрик get, put S3. Расчетные суммы могут содержать погрешности округления, поэтому окончательный расчет стоимости (количество * цену) должен выполняться в бухгалтерской программе. Если у клиента больше 1 действующего допника с услугами PAYG, то данные в отчете могут быть замножены, в этом случае считать вручную
Даты актуальности пока проверять вручную. Обращать внимание на замножение строк.
Период с по checked/> checked/>     WZ по вхождению, с регистром     Код по вхождению    
Часов в периоде: #hours# Строк в отчете: #qCharge.recordCount#
Актуальность данных: VCD Computing: class="err">#dateFormat(qComputingAge.dt_load,'YYYY-MM-DD')# #timeFormat(qComputingAge.dt_load,'HH:mm:ss.lXX')# VCD Storage: class="err">#dateFormat(qStorageAge.dt_load,'YYYY-MM-DD')# #timeFormat(qStorageAge.dt_load,'HH:mm:ss.lXX')# S3 хранение: class="err">#dateFormat(qS3VolAge.dt_load,'YYYY-MM-DD')# #timeFormat(qS3VolAge.dt_load,'HH:mm:ss.lXX')# S3 операции и трафик: class="err">#dateFormat(qS3OpsTrfAge.dt_load,'YYYY-MM-DD')# #timeFormat(qS3OpsTrfAge.dt_load,'HH:mm:ss.lXX')#
(перед выгрузкой отчета на произвольную дату или с параметрами сначала просмотрите его в браузере)
class="gray">
Match Услуга Артикул специф. Кол-во специф. Артикул метр. Детализация Кол-во метр. Free Tier. Опл. объем Ед.изм. Цена ₽ со скид., с НДС Стоимость ₽ с НДС Сделка Конечник Компонент Польз. наим. Дата НОУ Дата КОУ Сырая метрика Приведен­ная метрика Цена ₽ до ск. с НДС Цена ₽ co ск. с НДС без окр. Кол-во сервиса Скидка на сервис Кол-во комп-та Скидка на комп-т %
#wz# #contragent# (сводка) ответственный: class="err">#responsible# #responsible_email#
#currentRow# #occurrences_spec#|#occurrences_metric# EXACT MATCH CRITICAL: Many-to-Many Explosion (Duplicates on BOTH sides) DUPLICATION: Multiple entries for this key in Spec DUPLICATION: Multiple entries for this key in Metrics ORPHAN: Only in Spec ORPHAN: Only in Metrics Other Discrepancy #abstract_service#: #modifier# style="background:lightgreen;color:black;">#s_code# "err r""r">#component_quantity# #m_code# #dimension1# #request.roundSafe(metric,3)# #unit# [#ed_izm#] #gaussianRound(discounted_price,2)# #deal# #WZE# #component# #user_description# #dateFormat(serviceStart,'YYYY-MM-DD')# #dateFormat(serviceEnd,'YYYY-MM-DD')# #request.roundSafe(raw_metric,3)# #request.roundSafe(metric,3)# #price# #discounted_price# #service_quantity# #service_discount# #component_quantity# #component_discount#
Итого #code#: #round(final_metric_dimension_1,3)# #free_tier# #max(ceiling(final_metric_dimension_1) - free_tier, 0)# #unit# [#ed_izm#] #current_price# #gaussianRoundSafe(cost_dimension_1,2)# #request.roundSafe(accum_metric_dimension_1_all,3)#
Итого #wz# #contragent#: #accum_cost_svc#
string function gaussianRoundSafe(any num, numeric precision = 0, string default="") { return (isNumeric(arguments.num)) ? gaussianRound(arguments.num, arguments.precision) : arguments.default; } function gaussianRound(numeric number, numeric precision = 0) { var factor = 10 ^ precision; var scaledNumber = number * factor; var absScaled = abs(scaledNumber); var sign = sgn(scaledNumber); var floored = int(absScaled); // If it's not ending in exactly .5, use standard rounding or precision scaling if (absScaled - floored != 0.5) { return round(number * factor) / factor; } // If it is exactly .5, round towards the nearest even number if (floored % 2 != 0) { // Nearest even is UP return (floored + 1) * sign / factor; } else { // Nearest even is DOWN return floored * sign / factor; } }