071 deep debug

This commit is contained in:
2026-08-14 18:07:24 +03:00
parent 5401b7e96d
commit 91b6687220
8 changed files with 277 additions and 127 deletions
+99
View File
@@ -0,0 +1,99 @@
component {
/*0.02 timestamp support*/
/**
* HSQL 1.8
* Принимает Lucee Query, создает в HSQL временную таблицу и льет в нее данные
* @name Имя таблицы, которое ты хочешь использовать в SQL
* @data Сам запрос (результат queryExecute или cfhttp)
* @dsn Имя datasource для HSQL (по умолчанию "inmem")
*/
public string function register(required string name, required query data, string dsn = "inmem") {
var cols = arguments.data.columnList;
var meta = getMetaData(arguments.data);
//writedump(meta);
var colDefs = [];
var colTypes = {};
var params = [];
// 1. Генерируем "CREATE TEMP TABLE name (col1 TYPE, col2 TYPE...)"
for (var i = 1; i <= arrayLen(meta); i++) {
var colName = meta[i].name;
// Маппинг типов Lucee -> типы H2
var dbType = mapType(meta[i].typeName);
arrayAppend(colDefs, "#colName# #dbType#");
colTypes[colName] = meta[i].typeName;
}
//writedump(colDefs);
//var checkSql = "SELECT * FROM INFORMATION_SCHEMA.SYSTEM_TABLES WHERE TABLE_NAME = '#ucase(arguments.name)#';"
//var res = queryExecute(checkSql, {}, { datasource: arguments.dsn });
//if (res.recordCount EQ 0) {
var createSql = "DROP TABLE IF EXISTS #arguments.name#;
CREATE TEMPORARY TABLE #arguments.name#(#arrayToList(colDefs, ', ')#) ON COMMIT PRESERVE ROWS";
queryExecute(createSql, {}, { datasource: arguments.dsn });
//}
// 2. Цикл вставки
var insertSql = "INSERT INTO #arguments.name# (#cols#) VALUES (#repeatString("?,", listLen(cols)-1)#?)";
for (var row in arguments.data) {
var rowData = [];
for (var col in cols) {
arrayAppend(rowData, formatValue(row[col], colTypes[col]));
}
queryExecute(insertSql, rowData, { datasource: arguments.dsn });
}
return arguments.name;
}
/**
* Удалить временные таблицы (опционально, база сама почистит при закрытии коннекта)
*/
public void function drop(required string names, string dsn = "inmem") {
var list = arguments.names;
if (!isArray(list)) list = [list];
for (var name in list) {
queryExecute("DROP TABLE IF EXISTS #name#", {}, { datasource: arguments.dsn });
}
}
// --- Вспомогательная функция маппинга типов ---
private string function mapType(required string luceeType) {
switch (arguments.luceeType) {
case "VARCHAR": case "CHAR": case "LONGVARCHAR": return "LONGVARCHAR";
case "INTEGER": case "BIGINT": case "SMALLINT": case "TINYINT": return "INT";
case "DOUBLE": case "FLOAT": case "REAL": return "DOUBLE";
case "BOOLEAN": case "BIT": return "BIT";
case "DECIMAL": case "NUMERIC": return "DECIMAL(20,6)";
case "TIMESTAMP": case "DATE": case "TIME": return "TIMESTAMP";
default: return "LONGVARCHAR"; // И тут тоже
}
}
private function formatValue(required any val, required string targetType) {
var isTextType = listFindNoCase("VARCHAR,CHAR,LONGVARCHAR", arguments.targetType);
// 1. Обработка пустых значений
if (isNull(arguments.val) || (isSimpleValue(arguments.val) && len(trim(arguments.val)) == 0)) {
// Если это текстовое поле — сохраняем пустую строку как есть
if (isTextType) {
return "";
}
// Для чисел, дат и булев — отдаем настоящий SQL NULL
return javaCast("null", "");
}
// 2. Форматирование непустых дат
if (listFindNoCase("DATE,TIMESTAMP,TIME", arguments.targetType)) {
return dateFormat(arguments.val, "yyyy-mm-dd") & " " & timeFormat(arguments.val, "HH:mm:ss");
}
// 3. Числа и непустой текст отдаем как есть
return arguments.val;
}
}
+96 -100
View File
@@ -86,8 +86,10 @@
<!--- Важно: если в периоде было 2+ цены, то строки должны быть замножены, а цена за период перестает иметь смысл (не показывать же среднее арифметическое, это неправильно) --->
<!--- Left Outer Join with Free Tier --->
<cfquery name="qSpec" dbtype="query">
select s.catalogName as svc, s.component, s.serviceCode as service_code, s.fullCode as code, s.measureShort as unit
, s.priceWVat as price, (s.discount + s.serviceDiscount - s.discount*s.serviceDiscount/100) as discount
select s.catalogName as svc, s.component, s.serviceCode as service_code, NULL as sku_code, s.fullCode as code, s.measureShort as unit
, s.priceWVat as price
,s.componentDiscount as component_discount, s.serviceDiscount as service_discount, s.serviceQuantity as service_quantity, s.componentQuantity as component_quantity
,coalesce(s.serviceQuantity*(100-s.serviceDiscount)/100*s.priceWVat*(100-s.componentDiscount)/100,0) as discounted_price
,s.serviceStart, s.serviceEnd, s.serviceHash, s.serviceCustomName, f.free_tier
from qSpecification s, qFreeTier f
WHERE s.fullCode=f.code
@@ -95,13 +97,18 @@
UNION
select s.catalogName as svc, s.component, s.serviceCode as service_code, s.fullCode as code, s.measureShort as unit
, s.priceWVat as price, (s.discount + s.serviceDiscount - s.discount*s.serviceDiscount/100) as discount
select s.catalogName as svc, s.component, s.serviceCode as service_code, NULL as sku_code, s.fullCode as code, s.measureShort as unit
, s.priceWVat as price
,s.componentDiscount as component_discount, s.serviceDiscount as service_discount, s.serviceQuantity as service_quantity, s.componentQuantity as component_quantity
,coalesce(s.serviceQuantity*(100-s.serviceDiscount)/100*s.priceWVat*(100-s.componentDiscount)/100,0) as discounted_price
,s.serviceStart, s.serviceEnd, s.serviceHash, s.serviceCustomName, 0
from qSpecification s
WHERE s.fullCode NOT IN (<cfqueryparam value="#ValueList(qFreeTier.code)#" cfsqltype="cf_sql_varchar" list="true">)
AND s.serviceType = 3
</cfquery>
<cfloop query="qSpec">
<cfset qSpec.sku_code = (len(qSpec.code) GT 2) ? qSpec.code.left(qSpec.code.len() - 2) : qSpec.code/>
</cfloop>
<cfif (ATTRIBUTES.debug)>
qSpec (Left Outer Join with Free Tier)
@@ -124,7 +131,7 @@ CREATE INDEX idx_tenant_wzcode ON ngcloud_ru.tenant (wzcode); --->
<!--- *** Нужно еще реализовать в модели данных (каталоге) политики бесплатного минимума (free tier), сейчас прибито гвоздями --->
<cfquery name="qComputing">
select 'WZ'||to_char(tenant.wzcode,'FM00000') as wz, vdc.name as vdc_name,
select upper(clientid) as wz, vdc.name as vdc_name,
case
WHEN vdc.name LIKE '%-v1cl1%' THEN 'iaas.ngc.i29'
WHEN vdc.name LIKE '%-v1cl2%' THEN 'iaas.ngc.i31'
@@ -141,11 +148,11 @@ CREATE INDEX idx_tenant_wzcode ON ngcloud_ru.tenant (wzcode); --->
from ngcloud_ru.capacity_resource
join ngcloud_ru.vdc on capacity_resource.vdc_id=vdc.id
join ngcloud_ru.tenant on tenant.id=vdc.tenant_id
where tenant.wzcode = <cfqueryparam cfsqltype="cf_sql_integer" value=#ATTRIBUTES.tenant_wz_index#/>
where tenant.clientid = <cfqueryparam cfsqltype="cf_sql_varchar" value="WZ#numberFormat(ATTRIBUTES.tenant_wz_index,'00000')#"/>
AND capacity_resource.timestamp >= <cfqueryparam cfsqltype="cf_sql_timestamp" value=#attributes.dt_start#/>
AND capacity_resource.timestamp < <cfqueryparam cfsqltype="cf_sql_timestamp" value=#dateAdd("d",1,attributes.dt_finish)#/>
group by tenant.wzcode, vdc.name, capacity_resource.timestamp::date
order by tenant.wzcode, vdc.name, capacity_resource.timestamp::date;
group by tenant.clientid, vdc.name, capacity_resource.timestamp::date
order by tenant.clientid, vdc.name, capacity_resource.timestamp::date;
</cfquery>
<cfif (ATTRIBUTES.debug)>
@@ -172,7 +179,7 @@ from a
<!--- переделать storage_profile_types с расшифровкой ssd, sata, можно прямо до каталожного кода --->
<!--- очень долго исполняется --->
<cfquery name="qStorage">
select 'WZ'||to_char(tenant.wzcode,'FM00000') as wz, vdc.name as vdc_name,
select upper(clientid) as wz, vdc.name as vdc_name,
case
WHEN vdc.name LIKE '%-v1cl1%' THEN 'iaas.ngc.i29'
WHEN vdc.name LIKE '%-v1cl2%' THEN 'iaas.ngc.i31'
@@ -182,15 +189,15 @@ from a
WHEN vdc.name LIKE '%-v1cl7%' THEN 'iaas.ngc.a40'
ELSE '['||vdc.name||']' END
|| CASE
WHEN storage_profile_types.name ILIKE '%FASTSSD%' THEN '.fstssd-m'
WHEN storage_profile_types.name ILIKE '%SSD%' THEN '.ssd-m'
WHEN storage_profile_types.name ILIKE '%SAS%' THEN '.sas-m'
WHEN storage_profile_types.name ILIKE '%SATA%' THEN '.sata-m'
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, -- *** это не тип, а профайл
sum("limit")/1024./4./720 as gb_m_limit,
sum(used)/1024./4./720 as gb_m_used,
sum("limit")/1024./4./<cfqueryparam cfsqltype="cf_sql_integer" value=#hours#/> as gb_m_limit,
sum(used)/1024./4./<cfqueryparam cfsqltype="cf_sql_integer" value=#hours#/> as gb_m_used,
capacity_storage.timestamp::date as dt,
vdc.name as dimension_1,
storage_profile_types.name as dimension_2
@@ -198,11 +205,11 @@ from a
join ngcloud_ru.vdc on capacity_storage.vdc_id=vdc.id
join ngcloud_ru.tenant on tenant.id=vdc.tenant_id
join ngcloud_ru.storage_profile_types on capacity_storage.storage_profile_types_id=storage_profile_types.id
where tenant.wzcode = <cfqueryparam cfsqltype="cf_sql_integer" value=#ATTRIBUTES.tenant_wz_index#/> --AND 1=0
where tenant.clientid = <cfqueryparam cfsqltype="cf_sql_varchar" value="WZ#numberFormat(ATTRIBUTES.tenant_wz_index,'00000')#"/>
AND capacity_storage.timestamp >= <cfqueryparam cfsqltype="cf_sql_timestamp" value=#attributes.dt_start#/>
AND capacity_storage.timestamp < <cfqueryparam cfsqltype="cf_sql_timestamp" value=#dateAdd("d",1,attributes.dt_finish)#/>
group by tenant.wzcode, vdc.name, storage_profile_types.name, capacity_storage.timestamp::date
order by tenant.wzcode, vdc.name, storage_profile_types.name, capacity_storage.timestamp::date
group by tenant.clientid, vdc.name, storage_profile_types.name, capacity_storage.timestamp::date
order by tenant.clientid, vdc.name, storage_profile_types.name, capacity_storage.timestamp::date
;
</cfquery>
<!--- create index IX_capacity_storage_sort on ngcloud_ru.capacity_storage(vdc_id,storage_profile_types_id,timestamp); --->
@@ -221,11 +228,8 @@ from a
<cfquery name="qS3Vol">
select /* *** ниже дублирование кода агрегации - round ceil etc, обратить внимание при правке */
/* *** переделать на avg - сначала агрегировать по бакетам*/
(sum(bucket_stat.usage_rgw_main_size_actual)/720) as vol_B
,ceil(sum(bucket_stat.usage_rgw_main_size_actual)/720/1024/1024/1024) as vol_GB
<!--- ,greatest(ceil(sum(bucket_stat.usage_rgw_main_size_actual)/<cfqueryparam cfsqltype="cf_sql_integer" value=#hours#/>
/1024/1024/1024)-
case when bucket_stat.placement_id = 1 then 1 else 0 end, 0) as vol_GB_free_tier_subtracted /*считается не в точности так, как в функции*/ --->
(sum(bucket_stat.usage_rgw_main_size_actual)/<cfqueryparam cfsqltype="cf_sql_integer" value=#hours#/>) as vol_B
,sum(bucket_stat.usage_rgw_main_size_actual)/<cfqueryparam cfsqltype="cf_sql_integer" value=#hours#/>/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'
@@ -233,10 +237,10 @@ from a
--,bucket_stat.placement_id
,'WZ'||lpad(bucket_stat.owner, 5, '0') as wz -- *** здесь не число, а строка, поэтому другая формула обратной сборки WZ
,bucket_stat.timestamp_addition::date as dt
,bucket_info.name || ' - ' || case
,bucket_info.name /*|| ' - ' || 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
else '['|| TO_CHAR(bucket_stat.placement_id,'FM9') ||']' end */
as dimension_1
,null as dimension_2
from s3billing.bucket_stat
@@ -249,7 +253,6 @@ from a
group by bucket_stat.owner, bucket_stat.placement_id, bucket_info.name, bucket_stat.timestamp_addition::date
order by bucket_stat.owner, bucket_stat.placement_id, bucket_info.name, bucket_stat.timestamp_addition::date;
</cfquery>
<cfif (ATTRIBUTES.debug)>
qS3Vol
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
@@ -267,21 +270,14 @@ from a
OR category.name LIKE 'options%'
) THEN ops ELSE 0 END
) as get
,ceil(sum(
,(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
/*,greatest(ceil(sum(
CASE WHEN
(category.name LIKE 'get%'
OR category.name LIKE 'head%'
OR category.name LIKE 'options%'
) THEN ops ELSE 0 END
)/10000) - CASE WHEN bucket_info.placement_id = 1 THEN 10 ELSE 0 END, 0
) as get_10k_free_tier_subtracted*/
,sum(
CASE WHEN
@@ -291,7 +287,7 @@ from a
OR category.name LIKE 'list%'
) THEN ops ELSE 0 END
) as put
,ceil(sum(
,(sum(
CASE WHEN
(category.name LIKE 'put%'
OR category.name LIKE 'post%'
@@ -299,20 +295,10 @@ from a
OR category.name LIKE 'list%'
) THEN ops ELSE 0 END
)/10000) as put_10k
/*,greatest(ceil(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) - CASE WHEN bucket_info.placement_id = 1 THEN 1 ELSE 0 END, 0
) as put_10k_free_tier_subtracted*/
,(sum(bytes_sent)) as bytes_sent
,ceil(sum(bytes_sent)/1024/1024/1024) as bytes_sent_GB
/*,greatest(ceil(sum(bytes_sent)/1024/1024/1024) - CASE WHEN bucket_info.placement_id = 1 THEN 100 ELSE 0 END, 0
) as bytes_sent_GB_free_tier_subtracted */
,(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'
@@ -321,10 +307,10 @@ from a
--category.name, bucket_info.name
,'WZ'||lpad(user_info.name, 5, '0') as wz
,usage_bucket_by_user.time::date as dt
,bucket_info.name || ' - ' || case
,bucket_info.name <!--- || ' - ' || 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
else '['|| TO_CHAR(bucket_info.placement_id,'FM9') ||']' end --->
as dimension_1
,null as dimension_2
from s3billing.usage_bucket_by_user
@@ -352,12 +338,12 @@ from a
<!--- собрали все метрики в один резалтсет --->
<!--- Здесь мы знаем единицы измерения, причем надежнее, чем в спецификации --->
<cfquery name="qUnifiedMetric" dbType="query">
select dt, wz, code + '.vcpu-m' as code, core_h as raw_metric, core_h as metric,
select dt, wz, code + '.vcpu' as code, core_h as raw_metric, core_h as metric,
dimension_1, 'vdc' as dimension_1_name, dimension_2, NULL as dimension_2_name, 'ядро-ч' as unit
from qComputing
union all
select dt, wz, code + '.ram-m' as code, gb_h as raw_metric, gb_h as metric,
select dt, wz, code + '.ram' as code, gb_h as raw_metric, gb_h as metric,
dimension_1, 'vdc' as dimension_1_name, dimension_2, NULL as dimension_2_name, 'ГБайт-ч' as unit
from qComputing
@@ -367,22 +353,22 @@ from a
from qStorage
union all
select dt, wz, code + '.vol-m', vol_b as raw_metric, vol_gb as metric,
select dt, wz, code + '.vol', vol_b as raw_metric, vol_gb as metric,
dimension_1, 'bucket' as dimension_1_name, dimension_2, NULL as dimension_2_name, 'ГБайт-м' as unit
from qS3Vol
union all
select dt, wz, code + '.get-m', get as raw_metric, get_10k as metric,
select dt, wz, code + '.get', get as raw_metric, get_10k as metric,
dimension_1, 'bucket' as dimension_1_name, dimension_2, NULL as dimension_2_name, '10тыс.шт.' as unit
from qS3OpsTrf
union all
select dt, wz, code + '.put-m', put as raw_metric, put_10k as metric,
select dt, wz, code + '.put', put as raw_metric, put_10k as metric,
dimension_1, 'bucket' as dimension_1_name, dimension_2, NULL as dimension_2_name, '10тыс.шт.' as unit
from qS3OpsTrf
union all
select dt, wz, code + '.trf-m', bytes_sent as raw_metric, bytes_sent_gb as metric,
select dt, wz, code + '.trf', bytes_sent as raw_metric, bytes_sent_gb as metric,
dimension_1, 'bucket' as dimension_1_name, dimension_2, NULL as dimension_2_name, 'ГБайт' as unit
from qS3OpsTrf
@@ -398,7 +384,6 @@ from a
<!--- лучше никогда не связываться с QoQ, но если все исполнять в исходной бд, это скорее мегаселект с большим числом CTE --->
<!--- надо было сразу делать в БД. Причем сводить в одну, чтобы избежать нескольких источников данных... можно было бы и так добиться, но все равно сводим в буферную базу --->
<!--- <cfdump var=#qUnifiedMetric#/> --->
<cfif (ATTRIBUTES.debug)>
qUnifiedMetric
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
@@ -421,20 +406,30 @@ from a
<!--- Вычитание Free Tier можно начинать с начала месяца - для этого можно сгруппировать по месяцам --->
<!--- Кажется, здесь еще рано агрегировать, если мы хотим OUTER JOIN --->
<!--- агрегировать вообще не надо в этом компоненте, а стоимость вычислять после агрегации --->
<cfquery name="qChargeDaily" dbType="query">
<cfset db = new MemoryDbHelper()/>
<cfset db.register("qUnifiedMetric", qUnifiedMetric, "inmem")/>
<cfset db.register("qSpec", qSpec, "inmem")/>
<cfquery name="qChargeDaily" datasource="inmem">
SELECT
<d:field_set titleMapOut="titleMap" lengthOut="fieldCount">
<d:field title="Код компонента">s.code as s_code</d:field>
<d:field title="Код метрики">m.code as m_code</d:field>
<d:field title="Код услуги">s.service_code as service_code</d:field>
<d:field title="Код услуги">s.service_code</d:field>
<d:field title="Код услуги">s.sku_code</d:field>
<d:field title="Услуга">s.svc</d:field>
<d:field title="Компонент">s.component</d:field>
<d:field title="Ед.изм.">m.unit</d:field>
<d:field title="Цена &##8381; с НДС" cfSqlType="CF_SQL_NUMERIC">
<!--- <d:field title="Цена &##8381; с НДС" cfSqlType="CF_SQL_NUMERIC">
s.price*(100-s.discount)/100 as discounted_price
</d:field>
</d:field> --->
<d:field title="Цена &##8381; с НДС" cfSqlType="CF_SQL_NUMERIC">s.discounted_price</d:field>
<d:field title="Цена с НДС" cfSqlType="CF_SQL_NUMERIC">s.price</d:field>
<d:field title="Скидка%" cfSqlType="CF_SQL_NUMERIC">s.discount</d:field>
<d:field cfSqlType="CF_SQL_NUMERIC">s.component_quantity</d:field>
<d:field title="Скидка%" cfSqlType="CF_SQL_NUMERIC">s.component_discount</d:field>
<d:field cfSqlType="CF_SQL_NUMERIC">s.service_quantity</d:field>
<d:field cfSqlType="CF_SQL_NUMERIC">s.service_discount</d:field>
<d:field title="Сырая метрика" cfSqlType="CF_SQL_NUMERIC">m.raw_metric</d:field>
<d:field title="Приведенная метрика" cfSqlType="CF_SQL_NUMERIC">m.metric</d:field>
<d:field title="serviceStart" cfSqlType="CF_SQL_TIMESTAMP">s.serviceStart</d:field>
@@ -448,33 +443,35 @@ from a
<d:field>m.dimension_2</d:field>
<d:field>m.dimension_2_name</d:field>
</d:field_set>
FROM qSpec as s, qUnifiedMetric as m
WHERE s.code=m.code
AND s.serviceStart <= m.dt
AND (m.dt <= s.serviceEnd OR s.serviceEnd IS NULL)
FROM qSpec s
FULL OUTER JOIN qUnifiedMetric m ON (
s.sku_code=m.code
AND s.serviceStart <= m.dt
AND (m.dt <= s.serviceEnd OR s.serviceEnd IS NULL)
)
WHERE 1=1
<cfif #attributes.restrict_to_own_instances#>
AND m.wz=<cfqueryparam cfsqltype="cf_sql_varchar" value=#request.auth.wz#/>
</cfif>
<!--- таким замысловатым способом мы реализуем FULL JOIN --->
<!--- <!--- таким замысловатым способом мы реализуем FULL JOIN --->
UNION ALL
SELECT NULL, m.code, NULL, NULL, NULL, NULL, 0,
NULL, NULL, m.raw_metric, m.metric, NULL, NULL, NULL, NULL, m.dt, 0, m.dimension_1, m.dimension_1_name, m.dimension_2, m.dimension_2_name
SELECT NULL, m.code, NULL, NULL, NULL, NULL, NULL, 0,
0, 0, 0, 0, 0, m.raw_metric, m.metric, NULL, NULL, NULL, NULL, m.dt, 0, m.dimension_1, m.dimension_1_name, m.dimension_2, m.dimension_2_name
FROM qUnifiedMetric as m
WHERE m.code NOT IN (<cfqueryparam value="#ValueList(qSpec.code)#" cfsqltype="cf_sql_varchar" list="true">)
WHERE m.code NOT IN (<cfqueryparam value="#ValueList(qSpec.sku_code)#" cfsqltype="cf_sql_varchar" list="true">)
UNION ALL
SELECT s.code, NULL, s.service_code, s.svc, s.component, s.unit, s.price*(100-s.discount)/100 as discounted_price,
s.price, s.discount, NULL, NULL, s.serviceStart, s.serviceEnd, s.serviceHash, s.serviceCustomName, NULL, 0, NULL, NULL, NULL, NULL
SELECT s.code, NULL, s.service_code, s.sku_code, s.svc, s.component, s.unit, s.discounted_price,
s.price, s.component_quantity, s.component_discount, s.service_quantity, s.service_discount, NULL, NULL, s.serviceStart, s.serviceEnd, s.serviceHash, s.serviceCustomName, NULL, 0, NULL, NULL, NULL, NULL
FROM qSpec as s
WHERE s.code NOT IN (<cfqueryparam value="#listRemoveDuplicates(ValueList(qUnifiedMetric.code))#" cfsqltype="cf_sql_varchar" list="true">)
ORDER by dt, s_code, m_code, dimension_1, dimension_2
WHERE s.code NOT IN (<cfqueryparam value="#listRemoveDuplicates(ValueList(qUnifiedMetric.code))#" cfsqltype="cf_sql_varchar" list="true">) --->
</cfquery>
<!--- диагностика ошибок в QoQ крайне невнятна и вводит в заблуждение --->
<!---<cfdump var=#qChargeDaily# abort=true/> --->
<!--- <cfdump var=#qChargeDaily# abort=true/> --->
<cfif (ATTRIBUTES.debug)>
qChargeDaily
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
@@ -491,11 +488,18 @@ from a
<d:field title="Артикул по системам">m_code</d:field>
<d:field title="Артикул">s_code as code</d:field>
<d:field title="Код услуги">service_code</d:field>
<d:field title="SKU code">sku_code</d:field>
<d:field title="Услуга по каталогу">svc</d:field>
<d:field title="serviceCustomName">serviceCustomName</d:field>
<d:field title="Пользовательское наименование">serviceCustomName</d:field>
<d:field title="Компонент">component</d:field>
<d:field title="Ед.изм.">unit</d:field>
<d:field title="Цена &##8381; с НДС" cfSqlType="CF_SQL_NUMERIC">discounted_price</d:field>
<d:field cfSqlType="CF_SQL_NUMERIC">price</d:field>
<d:field cfSqlType="CF_SQL_NUMERIC">component_quantity</d:field>
<d:field cfSqlType="CF_SQL_NUMERIC">component_discount</d:field>
<d:field cfSqlType="CF_SQL_NUMERIC">0 as discounted_price2</d:field>
<d:field cfSqlType="CF_SQL_NUMERIC">service_quantity</d:field>
<d:field cfSqlType="CF_SQL_NUMERIC">service_discount</d:field>
<d:field title="Сырая метрика" cfSqlType="CF_SQL_NUMERIC">sum(raw_metric) as raw_metric</d:field>
<d:field title="Приведенный объем" cfSqlType="CF_SQL_NUMERIC">sum(metric) as metric</d:field>
<d:field title="serviceStart" cfSqlType="CF_SQL_TIMESTAMP">serviceStart</d:field>
@@ -508,35 +512,27 @@ from a
<d:field>dimension_1_name</d:field>
<d:field>dimension_2</d:field>
<d:field>dimension_2_name</d:field>
<d:field title="Год" cfSqlType="CF_SQL_INTEGER">year(dt) as year</d:field>
<d:field title="Месяц" cfSqlType="CF_SQL_INTEGER">month(dt) as month</d:field>
<d:field title="Год" cfSqlType="CF_SQL_INTEGER">year(dt) as year</d:field>
<d:field title="Месяц" cfSqlType="CF_SQL_INTEGER">month(dt) as month</d:field>
</d:field_set>
from qChargeDaily
group by s_code, m_code, svc, service_code, serviceCustomName, component, unit, discounted_price, serviceStart, ServiceEnd, serviceHash, free_tier, year(dt), month(dt),
group by s_code, m_code, svc, service_code, sku_code, serviceCustomName, component, unit, discounted_price, price, component_quantity, component_discount, service_quantity, service_discount, serviceStart, ServiceEnd, serviceHash, free_tier, year(dt), month(dt),
dimension_1_name, dimension_1, dimension_2_name, dimension_2
order by year(dt), month(dt), m_code, code, serviceStart, dimension_1, dimension_2
order by year(dt), month(dt), sku_code, m_code, serviceStart, dimension_1, dimension_2
</cfquery>
<!--- в связи с бедностью языка QoQ (нет CASE) грубо патчим резалтсет --->
<cfloop query="qChargeMonthly">
<!--- <cfloop query="qChargeMonthly">
<cfset qChargeMonthly.code = (len(code) GT 0) ? code : m_code/><!--- имя в левой части должно быть квалифицировано запросом (резалтсетом) --->
<cftry>
<cfset qChargeMonthly.discounted_price2 = precisionEvaluate("service_quantity*(100-service_discount)/100*price*(100-component_discount)/100")/><!--- имя в левой части должно быть квалифицировано запросом (резалтсетом) --->
<cfcatch type="any">
<cfdump var=#cfcatch#/>
<cfdump var=#queryGetRow(qChargeMonthly, qChargeMonthly.currentRow)# abort=true/>
</cfcatch>
</cftry>
</cfloop>
<!--- тут могут быть подводные камни с копированием и переиспользованием объектов --->
<!--- <cfif isQuery(qTotal)>
<cfquery name="qTotalNew" dbType="query">
select * from qTotal
union all
select * from qCharge
</cfquery>
<cfset qTotal = qTotalNew/>
<cfelse>
<cfset qTotal = qCharge/>
</cfif>
<cfdump var=#qTotal#/> --->
<!--- <cfdump var=#qChargeMonthly#/> --->
<!--- *** проверить краевые даты --->
--->
<cfif (ATTRIBUTES.debug)>
qChargeMonthly
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
+4 -4
View File
@@ -55,9 +55,9 @@
,componentCode
,component
,measureShort
,quantity
,componentQuantity
,priceWVat
,discount
,componentDiscount
,atomicCode
,fullCode
",
@@ -93,9 +93,9 @@
componentCode=local.component.componentCode,
component=local.component.component,
measureShort=local.component.measureShort,
quantity=local.component.quantity,
componentQuantity=local.component.quantity,
priceWVat=local.component.priceWVat,
discount=local.component.discount,
componentDiscount=local.component.discount,
atomicCode=local.component.atomicCode,
fullCode="#local.component.componentCode#-#listGetAt('i,f,m',local.service.serviceType)#"
}])/>