Postgres - query taking long time - sql

I have a query which is taking a long time. Is there anyway to write it better and in optimized way:
select 1, my_text from (
select distinct a.my_text||'_'||b.my_text my_text from (
select r_id, my_text
from tmp_v
where r_id in (
select o_id
from tmp_recid
) and v_id in (
select v_id
from o_v
where v_id in (
select o_id from tmp_record_vaid
union
select o_id from tmp_vue_vaid
) and va_nm = 'My V'
)
) a,
(
select r_id, my_text
from tmp_v
where r_id in (
select o_id
from tmp_recid
) and v_id in (
select v_id
from o_v
where v_id in (
select o_id from tmp_record_vaid
union
select o_id from tmp_vue_vaid
) and va_nm = 'My V 2'
)
) b
where a.r_id = b.r_id
except
select e_nm
from myp_ent_id
where p_m_id = 1 and entity_id in (
select entity_id
from o_e_t
where p_m_id = 1 and tag = 'Ample' and tag_category = 'My Type'
)
) a;

please, try with below query except "except query":
select 1, distinct(CONCAT(TV1.my_text, '_'))
from tmp_v TV1
left outer join tmp_recid TR1 on TR1.o_id = TV1.r_id
left outer join o_v OV1 on OV1.v_id = TV1.v_id and OV1.va_nm in ('My V', 'My V 2')
left outer join tmp_record_vaid TRV1 on OV1.v_id = TRV1.o_id
left outer join tmp_vue_vaid TVV1 on OV1.v_id = TVV1.o_id
where TR1.o_id is not null and (TRV1.o_id is not null OR TVV1.o_id is not null)
group by TV1.r_id

First level optimization: I combined two sub queries a and b(tmp_v) into one using CASE as the conditions are common for both.
select 1,my_text(
select distinct string_agg(amytext,'')||'_'||string_agg(bmytext,'') as mytext(
select r_id,case va_nm='My V' then my_text else null end as amy_text,case va_nm='My V 2' then my_text else null end as bmy_text
from tmp_v
where r_id in
(select o_id from tmp_recid)
and v_id in (select v_id from o_v where v_id in (select o_id from tmp_record_vaid union select o_id from tmp_vue_vaid))
)
except
select e_nm
from myp_ent_id
where p_m_id = 1 and entity_id in (
select entity_id
from o_e_t
where p_m_id = 1 and tag = 'Ample' and tag_category = 'My Type'
)
)a;
Further you can replace IN with EXISTS.

Related

Why is Oracle REPLACE function not working for this string?

We have a pattern we use all the time and this is usually pretty straightforward.
sortOrder IN VARCHAR2 := 'Title'
query VARCHAR2(32767) := q'[
SELECT
Columns
FROM tables
ORDER BY {sortOrder}
]';
query := REPLACE(query, '{sortOrder}', sortOrder);
But for this string it is not working:
WITH appl_List
AS
(
SELECT DISTINCT
appls.admin_phs_ORG_code || TO_CHAR(appls.serial_num, 'FM000000') AS core_proj_number,
appls.Appl_ID
FROM TABLE(:portfolioTable) appls
),
g1SupportingProjCount AS
(
SELECT
gen1grants.silverchair_id,
COUNT(DISTINCT al.Appl_ID) AS ApplCount
FROM
appl_List al
JOIN cg_cited_reference_gen1_grant gen1grants
ON al.core_proj_number = gen1grants.ic_serial_num
JOIN cg_cited_reference_gen1 gen1refs
ON gen1grants.silverchair_id = gen1refs.silverchair_id
GROUP BY gen1grants.Silverchair_id
),
g1SupportedPubCount AS
(
SELECT
gen1grants.silverchair_id,
COUNT(DISTINCT gen1refs.gen1_wos_uid) AS PubCount
FROM
appl_List al
JOIN cg_cited_reference_gen1_grant gen1grants
ON al.core_proj_number = gen1grants.ic_serial_num
JOIN cg_cited_reference_gen1 gen1refs
ON gen1grants.silverchair_id = gen1refs.silverchair_id
GROUP BY gen1grants.Silverchair_id
),
g2SupportingProjCount AS
(
SELECT
gen2grants.silverchair_id,
COUNT(DISTINCT al.Appl_ID) AS ApplCount
FROM
appl_List al
JOIN cg_cited_reference_gen2_grant gen2grants
ON al.core_proj_number = gen2grants.ic_serial_num
JOIN cg_cited_reference_gen2 gen2refs
ON gen2grants.silverchair_id = gen2refs.silverchair_id
GROUP BY gen2grants.Silverchair_id
),
g2SupportedPubCount AS
(
SELECT
gen2grants.silverchair_id,
COUNT(DISTINCT gen2refs.gen2_wos_uid) AS PubCount
FROM
appl_List al
JOIN cg_cited_reference_gen2_grant gen2grants
ON al.core_proj_number = gen2grants.ic_serial_num
JOIN cg_cited_reference_gen2 gen2refs
ON gen2grants.silverchair_id = gen2refs.silverchair_id
GROUP BY gen2grants.Silverchair_id
),
portfolio_cg_ids AS
(
SELECT DISTINCT md.silverchair_id
FROM
(
SELECT silverchair_id
FROM cg_cited_reference_gen1_grant gen1Grants
JOIN Appl_List appls
ON appls.core_proj_number = gen1Grants.ic_serial_num
UNION
SELECT silverchair_id
FROM cg_cited_reference_gen2_grant gen2Grants
JOIN Appl_List appls
ON appls.core_proj_number = gen2Grants.ic_serial_num
) grant_sc_ids
JOIN cg_metadata md
ON grant_sc_ids.silverchair_id = md.silverchair_id
)
SELECT
silverchairId,
TITLE,
PMID,
PMCID,
publication_year as year,
referenceCount1Gen,
supportingProjectCount1Gen,
supportedPublicationCount1Gen,
referenceCount2Gen,
supportingProjectCount2Gen,
supportedPublicationCount2Gen,
COUNT(1) OVER() as TotalCount
FROM
(
SELECT
md.SILVERCHAIR_ID silverchairId,
md.TITLE,
md.PMID,
md.PMCID ,
md.publication_year as year,
g1RefCounts.referenceCount1Gen as referenceCount1Gen,
g1SupportingProjCount.ApplCount as supportingProjectCount1Gen,
g1SupportedPubCount.PubCount as supportedPublicationCount1Gen,
g2RefCounts.referenceCount2Gen as referenceCount2Gen,
g2SupportingProjCount.ApplCount as supportingProjectCount2Gen,
g2SupportedPubCount.PubCount as supportedPublicationCount2Gen,
--COUNT(1) OVER() as TotalCount
FROM cg_metadata md
-- BEGIN datascope to current portfolio
JOIN portfolio_cg_ids
ON portfolio_cg_ids.silverchair_id = md.silverchair_id
-- END datascope to current portfolio
LEFT JOIN g1SupportingProjCount
ON g1SupportingProjCount.Silverchair_id = md.silverchair_id
LEFT JOIN g2SupportingProjCount
ON g2SupportingProjCount.Silverchair_id = md.silverchair_id
LEFT JOIN g1SupportedPubCount
ON g1SupportedPubCount.Silverchair_id = md.silverchair_id
LEFT JOIN g2SupportedPubCount
ON g2SupportedPubCount.Silverchair_id = md.silverchair_id
OUTER APPLY
(
Select Count(*) as referenceCount1Gen
FROM cg_cited_reference_gen1 g1Refs
WHERE g1Refs.silverchair_id = md.silverchair_id
) g1RefCounts
OUTER APPLY
(
Select Count(*) as referenceCount2Gen
FROM cg_cited_reference_gen2 g2Refs
WHERE g2Refs.silverchair_id = md.silverchair_id
) g2RefCounts
) results
ORDER BY {sortOrder}
Are there cases where some kind of special char in the string can keep this from working?
I'm kind of perplexed. I've been using this pattern for like 3 years and I've never had this not work.
What could be breaking this?
The query has 4000+ characters.
The text is probably being truncated somewhere down the line.

Reference a view inside of the view in postgresql

I would like to do something like the following:
CREATE VIEW foo_view AS
SELECT
foo.id,
foo.carryover_id,
foo.amount,
(SELECT SUM(foo_view.total)
FROM foo_view
WHERE foo_view.carryover_id = foo.id
) AS carryover_amount,
(amount + carryover_amount) AS total;
However, this raises an error relation "foo_view" does not exist. I would think that RECURSIVE would come in handy here, but the documentation is more reference than tutorial and there don't seem to be any resources out there to help me. Thoughts?
EDIT
Here's a sqlfiddle with a relevant schema http://sqlfiddle.com/#!15/6834d/1
You are right, your article is what you needed.
I formatted the data first into a fooBar temp table:
SELECT b.id
, foo_id
, ISNULL(f.carryover_id, foo_id) as carryover_id
, b.amount
, ROW_NUMBER() OVER(
PARTITION BY ISNULL(f.carryover_id, foo_id)
ORDER BY b.id asc) as rowNmbr
INTO #fooBar
FROM #bar as b
INNER JOIN #foo as f
ON b.foo_id = f.id;
The first row in the WITH is the top level. Then it recursively goes through each level. The rowNmbr is the level.
WITH summarizedFoo(rowNmbr, id, foo_id, carryover_id, amount, totalAmount)
AS
(
SELECT f.rowNmbr
, f.id
, f.foo_id
, f.carryover_id
, ISNULL(f.amount,0) as amount
, ISNULL(f.amount,0) as totalAmount
FROM #fooBar as f
WHERE f.rowNmbr = 1
UNION ALL
SELECT f.rowNmbr
, f.id
, f.foo_id
, f.carryover_id
, ISNULL(f.amount,0) as amount
, CAST((ISNULL(f.amount,0)
+ ISNULL(s.totalAmount,0)) as decimal) as totalAmount
FROM #fooBar as f
INNER JOIN summarizedFoo as s
ON f.carryover_id = s.carryover_id
AND f.rowNmbr = s.rowNmbr +1
)
SELECT rowNmbr, id, foo_id, carryover_id, amount, totalAmount
FROM summarizedFoo
I did this in T-SQL, but to make this work in your fiddler
Here is the PostgreSQL version:
Create a fooBar table (could be a view):
INSERT INTO fooBar
SELECT CAST(b.id as INTEGER)
, CAST(foo_id as INTEGER)
, CAST(CASE WHEN f.carryover_id is NULL THEN foo_id ELSE f.carryover_id END as INTEGER) as carryover_id
, CAST(b.amount as DECIMAL)
, CAST(ROW_NUMBER() OVER(PARTITION BY CASE WHEN f.carryover_id is NULL THEN foo_id ELSE f.carryover_id END ORDER BY b.id asc) as INTEGER) as rowNmbr
FROM bar as b
INNER JOIN foo as f
ON b.foo_id = f.id;
Here is the WITH RECURSIVE:
WITH RECURSIVE summarizedFoo(rowNmbr, id, foo_id, carryover_id, amount, totalAmount)
AS
(
SELECT f.rowNmbr, f.id, f.foo_id, f.carryover_id
, f.amount
, f.amount
FROM fooBar as f
WHERE f.rowNmbr = 1
UNION ALL
SELECT f.rowNmbr, f.id, f.foo_id, f.carryover_id
, f.amount
, f.amount + s.totalAmount
FROM fooBar as f
INNER JOIN summarizedFoo as s
ON f.carryover_id = s.carryover_id
AND f.rowNmbr = s.rowNmbr +1
)
SELECT rowNmbr, id, foo_id, carryover_id, amount, totalAmount
FROM summarizedFoo
I tested in SSMS(T-SQL version) and Fiddler(PostgreSQL), and verified the results in Excel.
Here is Fiddler of the solution:
http://sqlfiddle.com/#!15/a7822/3
The trick is to populate a table with source > target pairs, then to use that table to calculate the total amount:
CREATE RECURSIVE VIEW leaf_view(target_id, source_id) AS (
SELECT id, id FROM foo
UNION ALL
SELECT leaf_view.target_id, foo.id
FROM leaf_view
JOIN foo ON foo.carryover_id = leaf_view.source_id
);
CREATE VIEW foo_view(id, total) AS
SELECT leaf_view.target_id, SUM(bar.amount)
FROM leaf_view
LEFT JOIN bar ON leaf_view.source_id = bar.foo_id
GROUP BY leaf_view.target_id;
http://sqlfiddle.com/#!15/6834d/62
I owe this answer to RhodiumToad in #postgresql on freenode. Note that this will have problems for large foo since the optimizer generally can't work on recursive queries.
It looks like you want to have a cumulative running total amount. You could get this with a self-join, or a sub query, e.g:
select
id,
amount,
(select sum amount from foo b where b.id <= a.id ) as cumulative
from foo a
or
select
a.id,
a.amount,
sum(b.amount) as cumulative
from foo a
join foo b on b.id <= a.id
group by a.id, a.amount
Editing my answer as below.. I realized you are trying to do something recursive, does this help?? It'd really help if you gave a sample input and the output that you are expecting..
WITH RECURSIVE foo_view(id, total) AS (
SELECT id, amount from foo;
UNION ALL
SELECT foo_view.id, sum(foo_view.total+foo.amount)
FROM foo
JOIN
foo_view
ON
foo_view.id = foo.carryover_id
)
SELECT * from foo_view

Error on using union clause in cte sql query

I'm trying to do following but getting an max recursive error. Can someone please help?
Sample code to demonstrate what I'm trying to achieve:
DECLARE #SecurityMaster AS TABLE
(
ID INT,
SecurityAlias INT,
LegNumber INT
)
INSERT INTO #SecurityMaster
SELECT 12829, 3030106, NULL
UNION ALL
SELECT 12829, 3030107, 1
SELECT * FROM #SecurityMaster;
WITH CTE1 (ID, SecurityAlias, LegNumber)
AS
(
SELECT S.ID, S.SecurityAlias, S.LegNumber
FROM #SecurityMaster S
WHERE S.LegNumber IS NOT NULL
UNION ALL
select s.ID, s.SecurityAlias, s.LegNumber
from #SecurityMaster S inner join CTE1 c on s.ID = c.ID
where s.LegNumber is NULL
)
SELECT *
FROM CTE1;
Result I'm expecting:
ID SecurityAlias LegNumber
-----------------------------------------
12829 3030107 1
12829 3030106 NULL
Your questionis difficult to understand, but will this work for you?
select s.ID, s.SecurityAlias, s.LegNumber
from #SecurityMaster S
where s.LegNumber is NULL
And s.id in (SELECT f.ID
FROM #SecurityMaster f
WHERE f.LegNumber IS NOT NULL)

What does ")x" mean in a query?

I have the following stored procedure (in MS SQL):
SET NOCOUNT ON
DECLARE #Cuantos INT
IF EXISTS
(
SELECT TOP 1 * FROM IntProgramas WHERE cod_programa IN
( SELECT cod_programa FROM IntGrupo_programa WHERE cod_grupo IN
( SELECT cod_grupo FROM IntUsuarios WHERE cod_usuario = #cod_usuario
)
)
)
BEGIN
SET nocount ON
SELECT P.cod_programa
,nb_programa
,descripcion
,secuencia
,P.Accion
,P.Controlador
INTO #mitabla1
FROM IntProgramas P
WHERE P.cod_programa
IN (
SELECT cod_programa FROM Intgrupo_programa WHERE cod_grupo IN
(
SELECT cod_grupo FROM Intusuarios WHERE cod_usuario=#cod_usuario
)
)
SET nocount ON
SELECT GP.cod_programa
,P.nb_programa
,P.descripcion
INTO #mitabla2
FROM IntGrupo_Programa GP
JOIN Intprogramas P on GP.cod_programa = P.cod_programa
WHERE GP.cod_grupo IN (SELECT cod_grupo FROM Intusuarios WHERE cod_usuario=#cod_usuario
SELECT #Cuantos = COUNT(*)
FROM( SELECT nb_programa, descripcion FROM IntProgramas
WHERE cod_programa in (select cod_programa from #mitabla1
union select cod_programa from #mitabla2))x
/*si existe en ambas macheo*/
SELECT nb_programa, descripcion, P.cod_programa
INTO #mitabla3
FROM IntProgramas P
WHERE cod_programa in (SELECT cod_programa FROM #mitabla1
union SELECT cod_programa FROM #mitabla2)
select
t.nb_programa
, t.descripcion
, t.cod_programa
, p.secuencia
, ISNULL(et.cod_menu,0) as cod_menu
, ISNULL(et.desc_menu,0) as desc_menu
, ISNULL(et_sprog.cod_sub_menu_programa,0) AS cod_sub_menu_programa
, ISNULL(et_sprov.desc_sub_menu,0) AS desc_sub_menu_N2
, p.Accion
, p.Controlador
from #mitabla3 t
JOIN IntProgramas p
ON t.cod_programa = p.cod_programa
LEFT JOIN IntEstructura_sub_menu_programa et_sprog
ON t.cod_programa = et_sprog.cod_programa
LEFT JOIN IntEstructura_menu_Usuarios et
ON et_sprog.cod_menu = et.cod_menu
LEFT JOIN IntEstructura_sub_menu_Usuarios et_sprov
ON et_sprog.cod_sub_menu_programa = et_sprov.cod_sub_menu
WHERE et_sprog.cod_programa IS NOT NULL
order by et.cod_menu, et_sprog.cod_sub_menu_programa, p.secuencia
--gp.cod_grupo,
drop table #mitabla1
drop table #mitabla2
drop table #mitabla3
END
i am trying to understand it but when I get to the line
union select cod_programa from #mitabla2))x
I don't understand what the x does any help would be apreciated.
I have been trying to run the SP in parts to better understand the flow but that line has really complicated things for me.
X is an alias for this sub-query or derived table:
( SELECT nb_programa, descripcion FROM IntProgramas
WHERE cod_programa in (select cod_programa from #mitabla1
union select cod_programa from #mitabla2))
It's an alias for the subquery:
select #Cuantos = COUNT(*)
from (
select nb_programa, descripcion
from IntProgramas
where cod_programa in (
select cod_programa
from #mitabla1
union
select cod_programa
from #mitabla2
)
) x
When you do SELECT FROM (SELECT... you have to name the subquery / derived table.
It works as if you were doing:
select #Cuantos = COUNT(*)
from x
Unlike an alias in a table name that is used mostly to make it easier to read or to not have to repeat the table name, in this case an alias is mandatory.
X is the alias for the results of the sub-query. There should really be a space before it though

get last node given the full path of all ancestor's node attributes using cte

Given the following PostgreSQL table:
items
integer id
integer parent_id
string name
unique key on [parent_id, name]
parent_id is null for all root nodes
Currently I build the sql query manually, doing a join for every path element. But is seems quite ugly to me and of course it limits the possible depth.
Example:
path: holiday,images,spain
SELECT i3.*
FROM items AS i1
, items AS i2
, items AS i3
WHERE i1.parent_id IS NULL AND i1.name = 'holiday'
AND i2.parent_id=i1.id AND i2.name = 'images'
AND i3.parent_id=i2.id AND i3.name = 'spain'
I wonder if there's a better way, probably using CTE?
You can see how my current code works and what the expected output is here:
http://sqlfiddle.com/#!1/4537c/2
update2 here's a function, it peforms well, because search goes only within the path, starting from parent:
create or replace function get_item(path text[])
returns items
as
$$
with recursive cte as (
select i.id, i.name, i.parent_id, 1 as level
from items as i
where i.parent_id is null and i.name = $1[1]
union all
select i.id, i.name, i.parent_id, c.level + 1
from items as i
inner join cte as c on c.id = i.parent_id
where i.name = $1[level + 1]
)
select c.id, c.parent_id, c.name
from cte as c
where c.level = array_length($1, 1)
$$
language sql;
sql fiddle demo
update I think you can do recursive traversal. I've written sql version of this, so it's a bit messy because of cte, but it's possible to write a function:
with recursive cte_path as (
select array['holiday', 'spain', '2013'] as arr
), cte as (
select i.id, i.name, i.parent_id, 1 as level
from items as i
cross join cte_path as p
where i.parent_id is null and name = p.arr[1]
union all
select i.id, i.name, i.parent_id, c.level + 1
from items as i
inner join cte as c on c.id = i.parent_id
cross join cte_path as p
where i.name = p.arr[level + 1]
)
select c.*
from cte as c
cross join cte_path as p
where level = array_length(p.arr, 1)
sql fiddle demo
or you can build path for all of the elements using recursive cte for that and accumuate your path into array or string:
with recursive cte as (
select i.id, i.name, i.parent_id, i.name::text as path
from items as i
where i.parent_id is null
union all
select i.id, i.name, i.parent_id, c.path || '->' || i.name::text as path
from items as i
inner join cte as c on c.id = i.parent_id
)
select *
from cte
where path = 'holiday->spain->2013';
or
with recursive cte as (
select i.id, i.name, i.parent_id, array[i.name::text] as path
from items as i
where i.parent_id is null
union all
select i.id, i.name, i.parent_id, c.path || array[i.name::text] as path
from items as i
inner join cte as c on c.id = i.parent_id
)
select *
from cte
where path = array['holiday', 'spain', '2013']
sql fiddle demo
This should perform very well, as it eliminates impossible paths immediately:
WITH RECURSIVE cte AS (
SELECT id, parent_id, name
,'{holiday,spain,2013}'::text[] AS path -- provide path as array here
,2 AS lvl -- next level
FROM items
WHERE parent_id IS NULL
AND name = 'holiday' -- being path[1]
UNION ALL
SELECT i.id, i.parent_id, i.name
,cte.path, cte.lvl + 1
FROM cte
JOIN items i ON i.parent_id = cte.id AND i.name = path[lvl]
)
SELECT id, parent_id, name
FROM cte
ORDER BY lvl DESC
LIMIT 1;
Assuming you provide a unique path (only 1 result).
->SQLfiddle demo
Too late to post my answer (very equivalent to Roman's and Erwin's) But an improvement on the table definition instead:
CREATE TABLE items
( id integer NOT NULL PRIMARY KEY
, parent_id integer REFERENCES items(id)
, name varchar
, UNIQUE (parent_id,name) -- I don't actually like this one
); -- ; UNIQUE on a NULLable column ...
INSERT INTO items (id, parent_id, name) values
(1, null, 'holiday')
, (2, 1, 'spain'), (3, 2, '2013')
, (4, 1, 'usa'), (5, 4, '2013')
;