sql - 有没有办法将“CTE”临时结果传递给函数?
问题描述
我对类似部分的CTE
查询很长:
_saldo_end as (
SELECT
*,
coalesce( start_debet, 0 ) -coalesce( start_kredit, 0 )
+coalesce( oborot_deb, 0 ) -coalesce( oborot_kred, 0 ) as _saldo_end
FROM saldo_start ss
FULL JOIN schet_oborot( (select * from _schet), daterange(
(select * from _curr_day),
((select * from _curr_day) +interval '1day')::date
)) o USING ( analitid1 )
),
saldo_end as (
select *,
CASE WHEN _saldo_end > 0 THEN _saldo_end ELSE 0 END as end_debet,
CASE WHEN _saldo_end < 0 THEN -_saldo_end ELSE 0 END as end_kredit
from _saldo_end
)
我想为此创建函数:
CREATE OR REPLACE FUNCTION schet_saldo_end( _schet tbuhschet, _period daterange )
RETURNS table( _table setof, analitid1 int, oborot_deb numeric, oborot_kred numeric )
LANGUAGE sql AS $$
WITH
_saldo_start as (
SELECT
*,
coalesce( start_debet, 0 ) -coalesce( start_kredit, 0 )
+coalesce( oborot_deb, 0 ) -coalesce( oborot_kred, 0 ) as _saldo_end
FROM _table ss <<---- I do not know how to pass SETOF/TABLE
FULL JOIN schet_oborot( (select * from _schet), _period ) o USING ( analitid1 )
),
select analitid1,
CASE WHEN _saldo_end > 0 THEN _saldo_end ELSE 0 END as start_debet,
CASE WHEN _saldo_end < 0 THEN -_saldo_end ELSE 0 END as start_kredit
from _saldo_start
$$
所以我的CTE
会看起来像:
WITH
saldo_init as (
select
analitid1,
sumdeb as start_debet,
sumkred as start_kredit
from saldoanal
where schet = (select * from _schet)
and nyear = extract( year from (select * from _prev_mon) )
and nmonth = extract( month from (select * from _prev_mon) )
),
saldo_start as (
select *
from schet_saldo_end( 'saldo_init', 681, daterange( '2020-06-01', '2020-06-10' ) )
),
saldo_end as (
select *
from schet_saldo_end( 'saldo_start', 681, daterange( '2020-06-10', '2020-06-11' ) )
),
select * from saldo_end;
saldo_start
我的问题是它saldo_init
不是真正的表,所以我不能传递他们的名字。
有没有办法将CTE
临时结果传递给函数或引用一些CTE
,以便我可以从中选择?
UPD
可能的解决方案之一是将 CTE 结果插入 TEMP TABLE,并将此 TEMP TABLE 的名称传递给函数。但我无法想象如何完成这个
解决方案
我不知道为什么不能将CTEs
结果传递给函数。实际上,我认为这只是C
指向某些数据的指针。
但是我们可以将CTEs
结果存储到临时表中,然后TEMP TABLE
从我们的函数中引用它。
函数定义为:
--CREATE OR REPLACE FUNCTION schet_saldo_end( _tbl anyelement, _schet tbuhschet, _period daterange )
--RETURNS SETOF anyelement
CREATE OR REPLACE FUNCTION schet_saldo_end( _tbl regclass, _schet tbuhschet, _period daterange )
RETURNS table( analitid1 int, start_debet numeric, start_kredit numeric )
LANGUAGE plpgsql AS $$ BEGIN
RETURN QUERY EXECUTE FORMAT ('
WITH
_saldo_start as (
SELECT
*,
coalesce( start_debet, 0 ) -coalesce( start_kredit, 0 )
+coalesce( oborot_deb, 0 ) -coalesce( oborot_kred, 0 ) as _saldo_end
FROM %1$I ss
FULL JOIN schet_oborot( $1, $2 ) o USING ( analitid1 )
)
select analitid1,
CASE WHEN _saldo_end > 0 THEN _saldo_end ELSE 0 END as start_debet,
CASE WHEN _saldo_end < 0 THEN -_saldo_end ELSE 0 END as start_kredit
from _saldo_start
', /* pg_typeof( _tbl ) */ _tbl ) USING _schet, _period;
END $$
最后查询:
NOTICE BEGIN
是强制性的!
BEGIN;
create temp table xxx
on commit drop as
WITH
_schet AS ( select 681::numeric(6,2) as schet ),
_curr_day AS ( select '2020-06-10'::date as cd ),
_curr_mon AS ( select '2020-06-01'::date as cm ),
_prev_mon AS ( select cd -interval '1mon' as pm from _curr_day ),
saldo_init as (
select
analitid1,
sumdeb as start_debet,
sumkred as start_kredit
from saldoanal
where schet = (select * from _schet)
and nyear = extract( year from (select * from _prev_mon) )
and nmonth = extract( month from (select * from _prev_mon) )
)
select * from saldo_init;
create temp table yyy
on commit drop as
select * from schet_saldo_end( 'xxx', 681, daterange( '2020-06-01', '2020-06-10' ) );
select * from schet_saldo_end( 'yyy', 681, daterange( '2020-06-10', '2020-06-11' ) );
COMMIT;
尽管我解决了我的问题(是的,丑陋,但它有效=))问题仍然是开放的:
这是否可以传递saldo_init
给函数:
WITH saldo_init as ( ... )
select * from schet_saldo_end( 'saldo_init', ... );
UPD
因为saldo_init
只是查询,当我们将它传递给返回QUERY
的函数时
(我认为这是一个关键的事情,它允许我正在谈论的步骤)
所以这里schet_saldo_end
只是saldo_init
像SELECT fn( t.* )
展开成一样展开SELECT fn( t.a ), fn( t.b )
。
所以
saldo_start as (
select *
from schet_saldo_end( 'saldo_init', 681, daterange( '2020-06-01', '2020-06-10' ) )
),
只是:
saldo_start as (
select *
from (
-- schet_saldo_end starts here
-- ##########
_saldo_start as (
SELECT
*,
coalesce( start_debet, 0 ) -coalesce( start_kredit, 0 )
+coalesce( oborot_deb, 0 ) -coalesce( oborot_kred, 0 ) as _saldo_end
FROM (
--saldo_init starts here
select
analitid1,
sumdeb as start_debet,
sumkred as start_kredit
from saldoanal
where schet = (select * from _schet)
and nyear = extract( year from (select * from _prev_mon) )
and nmonth = extract( month from (select * from _prev_mon) )
--saldo_init end
) ss
FULL JOIN schet_oborot( 681, daterange( '2020-06-01', '2020-06-10' ) ) o USING ( analitid1 )
)
select analitid1,
CASE WHEN _saldo_end > 0 THEN _saldo_end ELSE 0 END as start_debet,
CASE WHEN _saldo_end < 0 THEN -_saldo_end ELSE 0 END as start_kredit
from _saldo_start
--#############
--schet_saldo_end END
) )
这个最终查询也可以折叠为 original CTEs
。这个“函数调用”可以扩展为:
WITH
saldo_init as (
select
analitid1,
sumdeb as start_debet,
sumkred as start_kredit
from saldoanal
where schet = (select * from _schet)
and nyear = extract( year from (select * from _prev_mon) )
and nmonth = extract( month from (select * from _prev_mon) )
),
--saldo_start as (
-- select *
-- from schet_saldo_end( 'saldo_init', 681, daterange( '2020-06-01', '2020-06-10' ) )
--),
_saldo_end as (
SELECT
*,
coalesce( start_debet, 0 ) -coalesce( start_kredit, 0 )
+coalesce( oborot_deb, 0 ) -coalesce( oborot_kred, 0 ) as _saldo_end
FROM saldo_init ss -- <<<< NOTICE HERE we refer `saldo_init`
FULL JOIN schet_oborot( 681, daterange( '2020-06-01', '2020-06-10' )) o USING ( analitid1 )
),
saldo_end as (
select *,
CASE WHEN _saldo_end > 0 THEN _saldo_end ELSE 0 END as end_debet,
CASE WHEN _saldo_end < 0 THEN -_saldo_end ELSE 0 END as end_kredit
from _saldo_end
),
saldo_start as (
select * from saldo_end
)
因为schet_saldo_end
嵌入到主CTE
查询中,我们可以参考saldo_init
;-)(见上文)并且没有任何规则被破坏。可能在 PostgreSQL 中应该实现这个函数返回的特殊优化QUERY
。
希望大家现在明白我的想法。