Why "alias X conflicts with an alias in the same statement" on aliased CTE use but not aliased table use? - firebird

I wonder why the below statement causes this error with at least the Win32 Firebird WI-V2.5.7.27050 and WI-V2.5.2.26540 versions.
Dynamic SQL Error SQL error code = -204 alias TRIPLEDIGITS conflicts with an alias in the same statement
Unlinke many errors in SQL statements, the above error does not indicate the line that causes it, but with some fiddling I found the line marked with a comment causes it.
Query having one CTE unaliased and an the same CTE aliased:
with
recursive
tripledigits(n) as (
select 0
from rdb$database
union all
select tripledigits.n + 1
from tripledigits
where tripledigits.n < 999
),
sextupledigits(n) as (
select tripledigits.n
+ tripledigits000.n * 1000
from tripledigits
cross join tripledigits tripledigits000 -- causes "Dynamic SQL Error SQL error code = -204 alias TRIPLEDIGITS conflicts with an alias in the same statement"
order by tripledigits.n
+ tripledigits000.n * 1000
)
select sextupledigits.n
from sextupledigits
order by sextupledigits.n
It also fails when moving the failing query out of the sextupledigits CTE into the main query:
with
recursive
tripledigits(n) as (
select 0
from rdb$database
union all
select tripledigits.n + 1
from tripledigits
where tripledigits.n < 999
)
select tripledigits.n
+ tripledigits000.n * 1000
from tripledigits
cross join tripledigits tripledigits000 -- causes "Dynamic SQL Error SQL error code = -204 alias TRIPLEDIGITS conflicts with an alias in the same statement"
order by tripledigits.n
+ tripledigits000.n * 1000
Whereas using the below using an unaliased table and the same table aliased:
select rdb$database.rdb$relation_id + rd.rdb$relation_id * 1000
from rdb$database
cross join rdb$database rd
Why is my CTE based query throwing the error?
I know at least two workarounds (see below), but for documentation purposes and posterity I'd like an answer to the question.
Workaround 1: alias both CTE uses
with
recursive
tripledigits(n) as (
select 0
from rdb$database
union all
select tripledigits.n + 1
from tripledigits
where tripledigits.n < 999
),
sextupledigits(n) as (
select tripledigitsBare.n
+ tripledigits000.n * 1000
from tripledigits tripledigitsBare
cross join tripledigits tripledigits000
order by tripledigitsBare.n
+ tripledigits000.n * 1000
)
select sextupledigits.n
from sextupledigits
order by sextupledigits.n
Workaround 2: use an intermediate CTE
with
recursive
tripledigits(n) as (
select 0
from rdb$database
union all
select tripledigits.n + 1
from tripledigits
where tripledigits.n < 999
),
tripledigits000(n) as (
select tripledigits.n * 1000
from tripledigits
),
sextupledigits(n) as (
select tripledigits.n
+ tripledigits000.n
from tripledigits
cross join tripledigits000
order by tripledigits.n
+ tripledigits000.n
)
select sextupledigits.n
from sextupledigits
order by sextupledigits.n
I've reported it in the Firebird tracker as CORE-5519.

Related

Ho do i convert this code into PostgreSQL Store Procedure?

I am new to PostgreSQL. I want to converter or add this code to Store Procedure.
WITH RECURSIVE t(i) AS (
SELECT * FROM unnest((select regexp_split_to_array('signature',''))::char[])
), cte AS (
SELECT i AS combo, i, 1 AS ct
FROM t
UNION ALL
SELECT cte.combo || t.i, t.i, ct + 1
FROM cte, t
WHERE ct <= 8
AND position(t.i in cte.combo) = 0
)
SELECT distinct cc.combo,ww.word
FROM cte cc
inner join words ww ON ww.word=cc.combo
WHERE length(combo)>1
AND ww.source_id in(1,2,19,21,24,26,33,34)
ORDER BY cc.combo ASC;
Just put it into a function:
create function generate_anagrams(p_word text)
returns table(combo text, word text)
as
$$
WITH RECURSIVE t(i) AS (
SELECT *
FROM unnest((select regexp_split_to_array(p_word,''))::char[])
), cte AS (
SELECT i AS combo, i, 1 AS ct
FROM t
UNION ALL
SELECT cte.combo || t.i, t.i, ct + 1
FROM cte, t
WHERE ct <= 8
AND position(t.i in cte.combo) = 0
)
SELECT distinct cc.combo,ww.word
FROM cte cc
inner join words ww ON ww.word=cc.combo
WHERE length(combo)>1
AND ww.source_id in(1,2,19,21,24,26,33,34)
ORDER BY cc.combo ASC
$$
language sql;
You can use it like this:
select *
from generate_anagrams('signature');

postgresql combining several periods into one

I'm trying to combine range.
WITH a AS (
select '2017-09-16 07:12:57' as begat,'2017-09-16 11:30:22' as endat
union
select '2017-09-18 17:05:21' ,'2017-09-19 13:18:01'
union
select '2017-09-19 15:34:40' ,'2017-09-22 13:29:37'
union
select '2017-09-22 12:24:16' ,'2017-09-22 13:18:29'
union
select '2017-09-28 09:48:54' ,'2017-09-28 13:39:13'
union
select '2017-09-20 13:52:43' ,'2017-09-20 14:14:43'
), b AS (
SELECT *, lag(endat) OVER (ORDER BY begat) < begat OR NULL AS step
FROM a
)
, c AS (
SELECT *, count(step) OVER (ORDER BY begat) AS grp
FROM b
)
SELECT min(begat), coalesce( max(endat), 'infinity' ) AS range
FROM c
GROUP BY grp
ORDER BY 1
Result
1 "2017-09-16 07:12:57";"2017-09-16 11:30:22"
2 "2017-09-18 17:05:21";"2017-09-19 13:18:01"
3 "2017-09-19 15:34:40";"2017-09-22 13:29:37"
4 "2017-09-22 12:24:16";"2017-09-22 13:18:29"
5 "2017-09-28 09:48:54";"2017-09-28 13:39:13"
positions 3,4 intersect (endata> next begat)
How do I make the union of all the intersections into one large interval
I need result
1 "2017-09-16 07:12:57";"2017-09-16 11:30:22"
2 "2017-09-18 17:05:21";"2017-09-19 13:18:01"
3 "2017-09-19 15:34:40";"2017-09-22 13:29:37"
4 "2017-09-28 09:48:54";"2017-09-28 13:39:13"
Hey I would suggest using the following process :
1- Identify when a row is new, so you give a value of 1 to values that do not overlap (CTE b)
2- Sequence together the rows that have overlaps with others. This way you can see have a common identifier that will allow you to MAX and MIN begat and endat (CTE c)
3- For each sequence, give the MIN of begat and the MAX of endat so you will have your final values
WITH a AS (
select '2017-09-16 07:12:57' as begat,'2017-09-16 11:30:22' as endat
union
select '2017-09-18 17:05:21' ,'2017-09-19 13:18:01'
union
select '2017-09-19 15:34:40' ,'2017-09-22 13:29:37'
union
select '2017-09-22 12:24:16' ,'2017-09-22 13:18:29'
union
select '2017-09-28 09:48:54' ,'2017-09-28 13:39:13'
union
select '2017-09-20 13:52:43' ,'2017-09-20 14:14:43'
)
, b AS (
SELECT
begat
, endat
, (begat > MAX(endat) OVER w IS TRUE)::INT is_new
FROM a
WINDOW w AS (ORDER BY begat ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)
)
, c AS (
SELECT
begat
, endat
, SUM((is_new)) OVER (ORDER BY begat) seq
FROM b
)
SELECT
MIN(begat) beg_at
, MAX(endat) end_at
FROM c
GROUP BY seq
EDITED
If you need speed you can use a psql function:
create or replace function append_ranges_in_a() returns setof a
language plpgsql
as
$BODY$
declare
v_current a%rowtype;
v_new a%rowtype;
v_first boolean:=true;
begin
for v_current in select begat, endat from a order by begat, endat
loop
if v_first then
v_first := false;
v_new.begat := v_current.begat;
elsif v_new.endat < v_current.begat then
return next v_new;
v_new.begat := v_current.begat;
end if;
v_new.endat := greatest(v_current.endat,v_new.endat);
end loop;
return next v_new;
return;
end;
$BODY$;
select * from append_ranges_in_a()
I test it with ~ 400000 rows:
delete from a;
insert into a (begat, endat)
select time::text, (time+'1 day'::interval)::text
from (select t+(round(random()*23.0)||' hours')::interval as time
from generate_series('1401-01-01'::timestamp,'2018-08-21'::timestamp,'1 day'::interval) t
) t;
select count(*) from a;
select * from append_ranges_in_a() offset 100000 limit 10
and it is twice fast as O(n^2) pure SQL version.
OLD slow solution:
You can use a recursive WITH query https://www.postgresql.org/docs/current/static/queries-with.html to construct the result row by row.
I create the table
The first row is the candidate first row (ending where ending), but the row is not "ready"
Then I look at the next row (step) and if it is not intersecting I add a ready row,
Also I add a not ready row with the current (last) observed range
When I do not have more rows I calculate the last row
I retain ready rows and the last row
Here is the code
CREATE TABLE a as
select '2017-09-16 07:12:57' as begat,'2017-09-16 11:30:22' as endat
union
select '2017-09-18 17:05:21' ,'2017-09-19 13:18:01'
union
select '2017-09-19 15:34:40' ,'2017-09-22 13:29:37'
union
select '2017-09-22 12:24:16' ,'2017-09-22 13:18:29'
union
select '2017-09-28 09:48:54' ,'2017-09-28 13:39:13'
union
select '2017-09-20 13:52:43' ,'2017-09-20 14:14:43';
WITH RECURSIVE t(begat, endat, ready, step) AS (
select * from (
select *,false,1 from a order by begat, endat limit 1) a
UNION ALL
SELECT new_rows.*
FROM (SELECT * FROM t ORDER BY begat DESC limit 1) t,
lateral (SELECT * FROM a ORDER BY begat, endat OFFSET step LIMIT 1) a,
lateral (
SELECT t.begat, t.endat, true as ready, step WHERE t.endat < a.begat
UNION SELECT CASE WHEN t.endat < a.begat THEN a.begat ELSE t.begat END, greatest(a.endat, t.endat), false, step+1
) new_rows
)
select begat, endat
from (
select begat, endat, ready, row_number() over (order by begat desc, endat desc)=1 is_last
from t
order by begat, endat) t
where ready or is_last;
i using range type
https://www.postgresql.org/docs/9.3/static/rangetypes.html
WITH tmp AS (
-- preparation range type
select begat, coalesce( endat, 'infinity' ) as endAt, tsrange( begat, coalesce( endat, 'infinity' ) ) as rg
from (
select '2017-09-11 17:13:03'::timestamp as begat ,'2017-09-12 12:24:09'::timestamp as endat union
select '2017-09-19 15:34:40','2017-09-20 11:04:45' union
select '2017-09-20 08:32:00','2017-09-22 13:28:37' union
select '2017-09-20 13:52:43','2017-09-20 14:14:43' union
select '2017-09-21 12:24:16','2017-09-21 13:28:29' union
select '2017-09-22 12:24:16','2017-09-22 13:28:29' union
select '2017-09-22 12:34:16','2017-09-23 13:28:29' union
select '2017-09-22 12:25:16','2017-09-24 13:28:29' union
select '2017-09-28 09:48:54','2017-09-28 13:39:13' union
select '2017-09-28 14:22:16','2017-09-28 15:52:15' union
select '2017-10-05 12:17:45','2017-10-06 12:35:38' union
select '2017-10-06 16:20:44','2017-10-07 10:11:09' union
select '2017-10-07 20:38:32','2017-10-09 14:42:29' union
select '2017-10-12 18:22:14','2017-10-12 20:52:45'
) a
),a as (
-- group intersecting range
select l.*
from tmp l left join tmp r on l.begAt > r.begAt and r.rg #> l.rg
where r.begAt is null
),
b AS (
SELECT *, lag(endat) OVER (ORDER BY begat) < begat OR NULL AS step
FROM a
)
, c AS (
SELECT *, count(step) OVER (ORDER BY begat) AS grp
FROM b
)
SELECT min(begat), coalesce( max(endat), 'infinity' ) AS range
FROM c
GROUP BY grp
ORDER BY 1

sql recursive within a range

I have a sql query to form a parent/child structure to a tree-like view, the outcome is like this:
lvl1a
lvl1a/lvl2a
lvl1a/lvl2b
lvl1b/lvl2a/lvl3a
lvl1c
lvl1d/lvl2a/lvl3a/lvl4a
...
the query itself doesn't have a limited range, for instance, if i only want to get this tree-like view for the first and second level
can someone modify the sql query to add such function? tks
;with cte as
(
select
labelID,
Title,
ParentLevel,
cast(Title as varchar(max)) as [treePath]
from TestTable
where ParentLevel = 0
union all
select
t.labelID,
t.Title,
t.ParentLevel,
[treePath] + '/' + cast(t.Title as varchar(255))
from
cte
join TestTablet on cte.labelID = t.ParentLevel
)
select
labelID,
Title,
ParentLevel,
[treePath]
from cte
order by treePath
All we did here was add lvl 0 for the first part of the union in the CTE
then increment it by 1 each time the recursion occurs (after the union all)
then add a where clause to the select to eliminate levels beyond 2.
Though I find it odd this works since t isn't aliased in your code...
.
;with cte as
(
select
labelID,
Title,
ParentLevel,
cast(Title as varchar(max)) as [treePath],
0 as lvl
from TestTable
where ParentLevel = 0
union all
select
t.labelID,
t.Title,
t.ParentLevel,
[treePath] + '/' + cast(t.Title as varchar(255)),
cte.lvl+1 as lvl
from
cte
join TestTablet t on cte.labelID = t.ParentLevel
)
select
labelID,
Title,
ParentLevel,
[treePath]
from cte
where lvl <=2
order by treePath

T-SQL, SQL Server Compact Edition, Alias For SELECT

How can I optimize my SQL code ?
I want to add alias.
I am using SQL Server Compact Edition.
( ... ) is a SELECT query
SELECT
*
FROM ( ... )
WHERE
id IN
(
SELECT
id
FROM ( ... )
GROUP BY
id
HAVING
COUNT( * ) > 1
)
I would suggest to use that query only once. You can create a CTE of that query and then write the query as follows:
with cte
As
(....)
Select *
from cte
where id in
(select id from cte
group by id
having count(*) > 1)
Hope it helps
this is another option:
SELECT * FROM
(SELECT *, COUNT(*) OVER(PARTITION BY id) ids FROM (...)) x
WHERE ids>1

T-sql CTE recursion

I just want it to return
1
2
WITH
CTE1 AS
(
select value
UNION ALL
select value=value+1
FROM CTe1
WHERE value =2
)
select * from cte1
how come that doesnt work.
The following will print out 1, 2:
WITH
CTE1 AS
(
select 1 as value
UNION ALL
select value=value+1
FROM CTe1
WHERE value = 1
)
select * from cte1
The problem was that value was not defined for your first CTE clause. I assume you wanted 1. Then the second CTE clause self-references the first one and adds 1.
The CTE will print 1 and 2 as rows. Is this what you are after?
WITH
CTE1 AS
(
select 1 as value
UNION ALL
select value=value+1
FROM CTe1
WHERE value < 2
)
select * from cte1
here is a code to search a string for a character macth thank you all.
Begin
with recursiveCTE(matchNumber, foundAt) as (
select 1, charindex(#toFind, #ToSearch, 0)
union all
select matchNumber + 1, charindex(#toFind, #ToSearch, foundAt + 1)
from recursiveCTE where foundAt > 0
)
select
matchNumber as "Match Number",
(case when foundAt = 0 then null else foundAt end) as "Found At"
from recursiveCTE
where foundAt 0 or matchNumber = 1
;
end;