Мне нужен совет, как оптимизировать мой запрос/действия поворота/отмены поворота, поскольку выполнение этой процедуры занимает много времени.
По сути, у меня есть данные с большим количеством значений в столбцах. Данные о стоимости разделены по строкам прибылей и убытков (продажи, единицы и т. д.), а также по типам базовых показателей и промо-акций. Данные моей строки содержат тип данных столбца — 1 и 2, который, например, показывает 1 как план и 2 как фактический. То есть, что касается стоимостных продаж, у меня есть строка с типом данных 1, которая показывает базовые продажи плана и плановые рекламные продажи, тип данных 2 показывает фактические базовые и фактические продажи. И так далее с другими ценностями.
Все данные разделены Chain_id (клиент) и int_id (запрос акции).
Тестируя процедуру, я обнаружил, что поворот/отмена поворота занимает 90% времени запроса, поэтому он выполняется для всех клиентов/рекламных запросов более 5 минут, и это то, что я хочу сократить до минимально возможного требуемого времени. В полном объеме у меня есть 120 столбцов значений (последние столбцы, включая все строки прибылей и убытков и отклонения) для каждого набора строк и около 15 тысяч наборов строк.
Я подготовил образец таблицы и включил всего несколько столбцов значений для облегчения понимания.
GO
BEGIN TRANSACTION
DROP TABLE IF EXISTS teststuffsample
CREATE TABLE teststuffsample (chain_id int,int_id int,data_type int,brand_id int,bndl_id int,product_id int,p_month int,bs_units float,bs_fix float,bs_gts float,bs_distr float,bs_chain float,units float,fix float,gts float,distr float,chain float,dsc_abs float,srv float)
INSERT INTO teststuffsample (chain_id,int_id,data_type,brand_id,bndl_id,product_id,p_month,bs_units,bs_fix,bs_gts,bs_distr,bs_chain,units,fix,gts,distr,chain,dsc_abs,srv)
VALUES
('1492','1044','1','133','0','216','10','484.610355961642','144413.886076569','158952.196755419','7152.84885399383','0','1566','466668','513648','23114.16','0','0','135000'),
('1492','1044','1','161','0','217','10','367.465235962632','345784.787040837','345784.787040837','15560.3154168377','0','1071','1007811','1007811','45351.495','0','0','135000'),
('1492','1044','1','174','0','223','10','32.5912503278301','18446.6476855518','18446.6476855518','830.099145849833','0','115','65090','65090','2929.05','0','0','11662.4439085442'),
('1492','1044','1','174','0','224','10','124.621894496795','73651.5396476058','81502.7190009039','3667.62235504068','0','241.5','142726.5','157941','7107.345','0','0','25572.8959980463'),
('1492','1044','1','174','0','225','10','18.0749312628939','10230.4110947979','10230.4110947979','460.368499265908','0','69','39054','39054','1757.43','0','0','6997.46634512653'),
('1492','1044','1','174','0','226','10','23.7828042932815','14055.6373373294','15553.9540078061','699.927930351275','0','69','40779','45126','2030.67','0','0','7306.54171372753'),
('1492','1044','1','174','0','227','10','5.78170671318688','6423.47615835062','7371.67605931327','331.725422669097','0','17.25','19164.75','21993.75','989.71875','0','0','3433.82734515706'),
('1492','1044','1','174','0','228','10','11.8454479001878','13160.2926171086','15102.9460727394','679.632573273275','0','28.75','31941.25','36656.25','1649.53125','0','0','5723.04557526176'),
('1492','1044','1','174','0','229','10','27.0753094861434','30080.6688391053','34521.0195948328','1553.44588176748','0','63.25','70270.75','80643.75','3628.96875','0','0','12590.7002655759'),
('1492','1044','1','174','0','230','10','98.1613226090106','55559.3085967','55559.3085967','2500.1688868515','0','230','130180','130180','5858.1','0','0','23324.8878170884'),
('1492','1044','1','174','0','231','10','55.6341451542095','32879.7797861378','36384.730930853','1637.31289188839','0','115','67965','75210','3384.45','0','0','12177.5695228792'),
('1492','1044','1','174','0','232','10','158.058357207257','173706.134570775','184454.102860869','8300.4346287391','0','345','379155','402615','18117.675','0','0','67934.7660184987'),
('1492','1044','1','174','0','1116','10','17.9385737496933','19714.4925509129','20934.3155658921','942.044200465144','0','46','50554','53682','2415.69','0','0','9057.9688024665'),
('1492','1044','1','174','0','1250','10','78.9370914693898','149822.599608902','159137.17640229','7161.17293810304','0','172.5','327405','347760','15649.2','0','0','58662.5049604689'),
('1492','1044','1','174','0','1297','10','23.8689851946638','45303.3338994719','48119.8741524422','2165.3943368599','0','46','87308','92736','4173.12','0','0','15643.334656125'),
('1492','1044','1','174','0','1547','10','9.8942555467826','5451.73480627721','5689.1969394','256.013862273','0','23','12673','13225','595.125','0','0','2270.67370798864'),
('1492','1044','1','174','0','1548','10','0','0','0','0','0','11.5','7659','7659','344.655','0','0','1372.29463658842'),
('1492','1044','1','174','0','1549','10','8.61391256572434','4746.26582371411','4952.9997252915','222.884987638117','0','28.75','15841.25','16531.25','743.90625','0','0','2838.3421349858'),
('1492','1044','1','174','0','1550','10','10.825592819086','7209.84481751128','7209.84481751128','324.443016788007','0','28.75','19147.5','19147.5','861.6375','0','0','3430.73659147105'),
('1492','1044','1','182','0','1784','10','0','0','0','0','0','600','115800','139800','6291','0','0','40000'),
('1492','1044','1','183','0','65','10','165.577073674536','35433.4937663507','38082.7269451433','1713.72271253145','0','580','124120','133400','6003','0','0','35052.2781250853'),
('1492','1044','1','183','0','69','10','162.957340654596','23954.7290762256','25584.3024827716','1151.29361172472','0','846.8','124479.6','132947.6','5982.642','0','0','35153.8314542328'),
('1492','1044','1','183','0','1200','10','127.028030709007','38616.5213355381','40140.8577040462','1806.33859668208','0','580','176320','183280','8247.6','0','0','49793.8904206819'),
('1492','1044','2','133','0','216','10','484.610355961642','144413.886076569','158952.196755419','7152.84885399383','0','2993.96','892200.08','982018.88','44190.8496','0','0','135000'),
('1492','1044','2','161','0','217','10','367.465235962632','345784.787040837','345784.787040837','15560.3154168377','0','1322.09','1244086.69','1244086.69','55983.90105','0','0','135000'),
('1492','1044','2','174','0','223','10','32.5912503278301','18446.6476855518','18446.6476855518','830.099145849833','0','342.7','193968.2','193968.2','8728.569','0','0','14728.2427831696'),
('1492','1044','2','174','0','224','10','124.621894496795','73651.5396476058','81502.7190009039','3667.62235504068','0','478.4','282734.4','312873.6','14079.312','0','0','21468.3689715829'),
('1492','1044','2','174','0','225','10','18.0749312628939','10230.4110947979','10230.4110947979','460.368499265908','0','93.15','52722.9','52722.9','2372.5305','0','0','4003.31431354609'),
('1492','1044','2','174','0','226','10','23.7828042932815','14055.6373373294','15553.9540078061','699.927930351275','0','157.55','93112.05','103037.7','4636.6965','0','0','7070.11189689147'),
('1492','1044','2','174','0','227','10','5.78170671318688','6423.47615835062','7371.67605931327','331.725422669097','0','72.45','80491.95','92373.75','4156.81875','0','0','6111.8522607868'),
('1492','1044','2','174','0','228','10','11.8454479001878','13160.2926171086','15102.9460727394','679.632573273275','0','118.45','131597.95','151023.75','6796.06875','0','0','9992.39337874667'),
('1492','1044','2','174','0','229','10','27.0753094861434','30080.6688391053','34521.0195948328','1553.44588176748','0','372.6','413958.6','475065','21377.925','0','0','31432.383055475'),
('1492','1044','2','174','0','230','10','98.1613226090106','55559.3085967','55559.3085967','2500.1688868515','0','524.4','296810.4','296810.4','13356.468','0','0','22537.1768762595'),
('1492','1044','2','174','0','231','10','55.6341451542095','32879.7797861378','36384.730930853','1637.31289188839','0','401.35','237197.85','262482.9','11811.7305','0','0','18010.7230074097'),
('1492','1044','2','174','0','232','10','158.058357207257','173706.134570775','184454.102860869','8300.4346287391','0','307.05','337447.95','358327.35','16124.73075','0','0','25622.8357755697'),
('1492','1044','2','174','0','1116','10','17.9385737496933','19714.4925509129','20934.3155658921','942.044200465144','0','71.3','78358.7','83207.1','3744.3195','0','0','5949.87197784766'),
('1492','1044','2','174','0','1250','10','78.9370914693898','149822.599608902','159137.17640229','7161.17293810304','0','428.95','814147.1','864763.2','38914.344','0','0','61819.1855676004'),
('1492','1044','2','174','0','1297','10','23.8689851946638','45303.3338994719','48119.8741524422','2165.3943368599','0','175.95','333953.1','354715.2','15962.184','0','0','25357.4675384527'),
('1492','1044','2','174','0','1547','10','9.8942555467826','5451.73480627721','5689.1969394','256.013862273','0','135.7','74770.7','78027.5','3511.2375','0','0','5677.43074724381'),
('1492','1044','2','174','0','1548','10','0','0','0','0','0','59.8','39826.8','39826.8','1792.206','0','0','3024.09765970266'),
('1492','1044','2','174','0','1549','10','8.61391256572434','4746.26582371411','4952.9997252915','222.884987638117','0','55.2','30415.2','31740','1428.3','0','0','2309.46335481104'),
('1492','1044','2','174','0','1550','10','10.825592819086','7209.84481751128','7209.84481751128','324.443016788007','0','96.6','64335.6','64335.6','2895.102','0','0','4885.0808349043'),
('1492','1044','2','182','0','1784','10','0','0','0','0','0','1929.6','372412.8','449596.8','20231.856','0','0','40000'),
('1492','1044','2','183','0','65','10','165.577073674536','35433.4937663507','38082.7269451433','1713.72271253145','0','1303.84','279021.76','299883.2','13494.744','0','0','38891.528278371'),
('1492','1044','2','183','0','69','10','162.957340654596','23954.7290762256','25584.3024827716','1151.29361172472','0','1295.72','190470.84','203428.04','9154.2618','0','0','26548.8328224475'),
('1492','1044','2','183','0','1200','10','127.028030709007','38616.5213355381','40140.8577040462','1806.33859668208','0','1287.6','391430.4','406881.6','18309.672','0','0','54559.6388991815'),
('1492','1054','1','161','0','218','6','6498.55801052056','2300489.53572428','2430460.69593469','109370.731317061','0','16065','5687010','6008310','270373.95','0','0','1282166.26422904'),
('1492','1054','1','163','0','1288','6','377.013858782708','76910.8271916724','100662.700294983','4529.82151327424','0','2562','522648','684054','30782.43','0','0','117833.735770956'),
('1492','1054','2','161','0','218','6','6498.55801052056','2300489.53572428','2430460.69593469','109370.731317061','0','20458.48','7242301.92','7651471.52','344316.2184','0','0','1206215.17107884'),
('1492','1054','2','163','0','1288','6','377.013858782708','76910.8271916724','100662.700294983','4529.82151327424','0','5703.5','1163514','1522834.5','68527.5525','0','0','193784.828921164'),
('1492','1055','1','174','0','232','6','680.335153258822','655843.087741504','763336.041956398','34350.1218880379','0','2242.5','2161770','2516085','113223.825','0','0','555000'),
('1492','1055','2','174','0','232','6','680.335153258822','655843.087741504','763336.041956398','34350.1218880379','0','1780.2','1716112.8','1997384.4','89882.298','0','0','555000'),
('1492','1057','1','163','0','1288','7','568.329475035592','115939.212907261','151743.969834503','6828.47864255264','0','8540','1742160','2280180','102608.1','0','0','700000'),
('1492','1057','2','163','0','1288','7','568.329475035592','115939.212907261','151743.969834503','6828.47864255264','0','9810.02','2001244.08','2619275.34','117867.3903','0','0','700000'),
('1492','1058','1','133','0','216','7','10714.8714639816','2925159.90966698','3439473.73993809','154776.318297214','0','22040','6016920','7074840','318367.8','0','380000','1023022.25374418'),
('1492','1058','1','174','0','232','7','875.139077089731','843634.070314501','981906.044494678','44185.7720022605','0','2300','2217200','2580600','116127','0','200000','376977.746255823'),
('1492','1058','2','133','0','216','7','10714.8714639816','2925159.90966698','3439473.73993809','154776.318297214','0','23614.12','6446654.76','7580132.52','341105.9634','0','339283.3','1068378.42545737'),
('1492','1058','2','174','0','232','7','875.139077089731','843634.070314501','981906.044494678','44185.7720022605','0','2075.75','2001023','2328991.5','104804.6175','0','150416.7','331621.574542635')
COMMIT TRANSACTION
GO
Моя процедура должна вычислять отклонения между промо и базовым уровнем для плановых и фактических данных, при этом результаты сохраняются в каждом отдельном столбце.
На небольших наборах данных с
Вот как выглядит моя процедура. Вы можете создать таблицу в своей базе данных и запустить эту процедуру, она должна работать нормально:
drop table if exists #product
drop table if exists #value_types
drop table if exists #tmp
drop table if exists #tmp2
drop table if exists #tmp3
--for the sake of testing, I have stored procedure for this table
select *
into #product
from teststuffsample
--help table to cross apply & match with product table
create table #value_types
(
ord int
,value_type nvarchar(13)
,base_type nvarchar(13)
,fc_ac nvarchar(13)
)
insert into #value_types (ord, value_type, base_type, fc_ac)
values (1, 'units_nonprol','bs_units','units_nonprol'),
(2, 'units','bs_units','units'),
(3, 'coverage','coverage','coverage'),
(4, 'fix','bs_fix','fix'),
(5, 'gts','bs_gts','gts'),
(6, 'distr','bs_distr','distr'),
(7, 'chain','bs_chain','chain'),
(8, 'dsc','bs_ttl_dsc','ttl_dsc'),
(9, 'srv','bs_srv','srv'),
(10, 'vat','bs_vat','ttl_vat'),
(11, 'nts','bs_nts','nts'),
(12, 'cogs','bs_cogs','cogs'),
(13, 'gp','bs_gp','gp'),
(14, 'bme','bs_ttl_bme','ttl_bme'),
(15, 'bc','bs_bc','bc');
--here I convert initial multiple value columns to single column with value type & single value column
with i
as
(
select t.chain_id
,t.int_id
,t.p_month
,t.data_type
,t.bndl_id
,t.brand_id
,t.product_id
,t.bs_units
,t.units
,t.bs_fix
,t.fix
,t.bs_gts
,t.gts
,t.bs_distr
,t.distr
,t.bs_chain
,t.chain
,cast(0 as float) as bs_srv
,t.srv
from #product t
)
select chain_id
,int_id
,p_month
,data_type
,bndl_id
,brand_id
,product_id
,value_type
,value
into #tmp2
from i
unpivot
(
value
for value_type in (bs_units,units,bs_fix,fix,bs_gts,gts,bs_distr,distr,bs_chain,chain,bs_srv,srv)
) piv;
--I create base table for each row combination & multiple it by all needed p&l lines from help table
select distinct t.chain_id
,t.int_id
,t.p_month
,t.bndl_id
,t.brand_id
,t.product_id
,cast(n.ord as nvarchar(2)) + '_' + n.value_type as val_type_show
,n.value_type
into #tmp
from #product t
cross apply #value_types n;
--Here i perform selective joins to add value columns to seggregate Baseline/Promo values to perform variance calculation for each p&l line
select t.*
,base_fc.value as 'Baseline FC'
,fc.value as 'Promo FC'
,fc.value - base_fc.value 'Promo FC vs Baseline FC'
,isnull(base_ac.value, base_fc.value) as 'Baseline AC/FC'
,isnull(ac.value, fc.value) as 'Promo AC/FC'
,isnull(ac.value, fc.value) - isnull(base_ac.value, base_fc.value) as 'Promo AC/FC vs Baseline AC/FC'
,isnull(base_ac.value, base_fc.value) - base_fc.value 'Baseline AC/FC vs Baseline FC'
,isnull(ac.value, fc.value) - fc.value 'Promo AC/FC vs Promo FC'
into #tmp3
from #tmp t
left join (select t.int_id
,t.p_month
,t.bndl_id
,t.brand_id
,t.product_id
,n.value_type
,t.value
from #tmp2 t
join #value_types n on n.base_type = t.value_type
where n.value_type is not null and t.data_type = 1) base_fc on base_fc.int_id = t.int_id and
base_fc.p_month = t.p_month and
base_fc.bndl_id = t.bndl_id and
base_fc.brand_id = t.brand_id and
base_fc.product_id = t.product_id and
base_fc.value_type = t.value_type
left join (select t.int_id
,t.p_month
,t.bndl_id
,t.brand_id
,t.product_id
,n.value_type
,t.value
from #tmp2 t
join #value_types n on n.fc_ac = t.value_type
where n.value_type is not null and t.data_type = 1) fc on fc.int_id = t.int_id and
fc.p_month = t.p_month and
fc.bndl_id = t.bndl_id and
fc.brand_id = t.brand_id and
fc.product_id = t.product_id and
fc.value_type = t.value_type
left join (select t.int_id
,t.p_month
,t.bndl_id
,t.brand_id
,t.product_id
,n.value_type
,t.value
from #tmp2 t
join #value_types n on n.base_type = t.value_type
where n.value_type is not null and t.data_type = 2) base_ac on base_ac.int_id = t.int_id and
base_ac.p_month = t.p_month and
base_ac.bndl_id = t.bndl_id and
base_ac.brand_id = t.brand_id and
base_ac.product_id = t.product_id and
base_ac.value_type = t.value_type
left join (select t.int_id
,t.p_month
,t.bndl_id
,t.brand_id
,t.product_id
,n.value_type
,t.value
from #tmp2 t
join #value_types n on n.fc_ac = t.value_type
where n.value_type is not null and t.data_type = 2) ac on ac.int_id = t.int_id and
ac.p_month = t.p_month and
ac.bndl_id = t.bndl_id and
ac.brand_id = t.brand_id and
ac.product_id = t.product_id and
ac.value_type = t.value_type;
--after calculating variances I unpivot variances columns to my value_type column & value column. So in my value type column I have all P&L lines + calculated variances
--after unpivoting I convert whole value_types back to each separate value column to achive set of 1 row - all values
select *
from (select chain_id
,int_id
,p_month
,bndl_id
,brand_id
,product_id
,col + ' ' + value_type as col
,value
from #tmp3
unpivot
(
value
for col in ([Baseline FC], [Promo FC], [Promo FC vs Baseline FC], [Baseline AC/FC], [Promo AC/FC], [Promo AC/FC vs Baseline AC/FC], [Baseline AC/FC vs Baseline FC], [Promo AC/FC vs Promo FC])
) pv
) t
pivot
(
max(value)
for col in ([Baseline AC/FC units],[Baseline AC/FC fix],[Baseline AC/FC gts],[Baseline AC/FC distr],[Baseline AC/FC chain],[Baseline AC/FC dsc],[Baseline AC/FC srv],[Baseline AC/FC vs Baseline FC units],[Baseline AC/FC vs Baseline FC fix],[Baseline AC/FC vs Baseline FC gts],[Baseline AC/FC vs Baseline FC distr],[Baseline AC/FC vs Baseline FC chain],[Baseline AC/FC vs Baseline FC dsc],[Baseline AC/FC vs Baseline FC srv],[Baseline FC units],[Baseline FC fix],[Baseline FC gts],[Baseline FC distr],[Baseline FC chain],[Baseline FC dsc],[Baseline FC srv],[Promo AC/FC units],[Promo AC/FC fix],[Promo AC/FC gts],[Promo AC/FC distr],[Promo AC/FC chain],[Promo AC/FC dsc],[Promo AC/FC srv],[Promo AC/FC vs Baseline AC/FC units],[Promo AC/FC vs Baseline AC/FC fix],[Promo AC/FC vs Baseline AC/FC gts],[Promo AC/FC vs Baseline AC/FC distr],[Promo AC/FC vs Baseline AC/FC chain],[Promo AC/FC vs Baseline AC/FC dsc],[Promo AC/FC vs Baseline AC/FC srv],[Promo AC/FC vs Promo FC units],[Promo AC/FC vs Promo FC fix],[Promo AC/FC vs Promo FC gts],[Promo AC/FC vs Promo FC distr],[Promo AC/FC vs Promo FC chain],[Promo AC/FC vs Promo FC dsc],[Promo AC/FC vs Promo FC srv],[Promo FC units],[Promo FC fix],[Promo FC gts],[Promo FC distr],[Promo FC chain],[Promo FC dsc],[Promo FC srv],[Promo FC vs Baseline FC units],[Promo FC vs Baseline FC fix],[Promo FC vs Baseline FC gts],[Promo FC vs Baseline FC distr],[Promo FC vs Baseline FC chain],[Promo FC vs Baseline FC dsc],[Promo FC vs Baseline FC srv]
)
) pvt
Предварительный просмотр результата процедуры в виде 1 набора строк со всеми значениями, хранящимися в столбцах, идущих вправо:
Я понимаю, что мой пример и описание случая могут быть не такими простыми для понимания с самого начала, но я очень ценю ваше время и советы по этому делу. Пожалуйста, задавайте любые вопросы, если что-то не понятно
Дальнейшие правки:
@dai привет, спасибо за комментарий. Я уже искал это, но не вижу способа избежать этого в SQL. Интерфейс приложения — Excel, я сомневаюсь, что VBA выполнит транспонирование быстрее. Процедура должна возвращать отчет в пустом файле Excel с простыми данными, поэтому у меня может закончиться максимальное количество строк Excel, если я не поверну столбцы.
«Внешняя часть приложения — это Excel, я сомневаюсь, что VBA выполнит транспонирование быстрее» — Вы знаете, что именно в Excel началась вся эта «Pivot», верно? (Возвращаясь в Excel 97) — вы видите большую кнопку «Сводная таблица» на ленте?
@Dai Я узнаю об этом в бизнесе, если это покроет первоначальный запрос. Возможно, было бы хорошей идеей остановиться на строках прибылей и убытков в столбце value_type и базовых/промо/отклонениях в столбцах значений.
Не используйте nvarchar(max)
, а определите разумный максимум и попытайтесь создать индекс, который улучшит план выполнения... (см.: dbfiddle.uk/9s4dnPun)
@Luuk Отличный комментарий, мне удалось сократить его до 19 секунд для всего набора данных. У кого-нибудь есть еще идеи, как я могу улучшить свою процедуру?
Вам следует добавить PK или хотя бы несколько индексов в свой #temporaryTables
, чтобы ваши JOIN
работали быстрее, или рассмотрите возможность замены временных таблиц CTE, если это возможно.
Мне удалось сократить всю процедуру до 6 секунд и я этим доволен. Спасибо всем в комментариях за помощь.
select *
into #tmp4
from (select rn
,col + ' ' + value_type as col
,value
from #tmp3
unpivot
(
value
for col in ([Baseline FC], [Promo FC], [Promo FC vs Baseline FC], [Baseline AC/FC], [Promo AC/FC], [Promo AC/FC vs Baseline AC/FC], [Baseline AC/FC vs Baseline FC], [Promo AC/FC vs Promo FC])
) pv
) t
pivot
(
max(value)
for col in (col names)
)
) pvt;
Лучше всего полностью избегать
PIVOT
— есть ли причина, по которой вы не можете выполнить транспозицию в коде приложения, а не в SQL?