SQL Postgres display details of edges - sql

I have a table of nodes say
Table Node
Column| type
------|---------------
id | int
x | int
y | int
z | text
for example,
id | x | y | z
---|---|---|---
0 | 0 | 0 |'a'
1 | 0 | 1 |'b'
2 | 1 | 0 |'c'
3 | 1 | 1 |'d'
and
Table Edge
Column| type
------|---------------
source| int references Node.id
target| int references Node.id
for example,
source | target
-------|-------
0 | 1
1 | 3
3 | 2
assuming a node doesn't have an edge to itself, and every source is unique
I want to display the result of the entire edge information
source.x | source.y | target.x | target.y
---------|----------|----------|---------
0 | 0 | 0 | 1
0 | 1 | 1 | 1
1 | 1 | 1 | 0
I have tried many joins(self join of node with inner join with edge), (left join of edge with result of self join of node)
How can I achieve this selection result?

I think you want two joins:
select ns.x, ns.y, nt.x, nt.y
from edge e join
nodes n1
on e.source = ns.id join
nodes nd
on e.target = nt.id

In case of PostgreSQL, you can play with the RECURSIVE WITH clause.
a) build a path:
CREATE LOCAL TEMPORARY TABLE
node(id,x,y,z) AS (
SELECT 0,0,0,'a'
UNION ALL SELECT 1,0,1,'b'
UNION ALL SELECT 2,1,0,'c'
UNION ALL SELECT 3,1,1,'d'
)
;
CREATE LOCAL TEMPORARY TABLE
edge(source,target) AS (
SELECT 0,1
UNION ALL SELECT 1,3
UNION ALL SELECT 3,2
)
;
WITH RECURSIVE backbone AS (
SELECT
source::CHAR(1)||','||target::CHAR(1) AS path
, 1 AS hops
, *
FROM edge
UNION ALL SELECT
p.path||','||c.target::CHAR(1) AS path
, p.hops + 1 AS hops
, c.*
FROM backbone p JOIN edge c ON c.source=p.target
)
SELECT * FROM backbone;
path | hops | source | target
---------+------+--------+--------
0,1 | 1 | 0 | 1
1,3 | 1 | 1 | 3
3,2 | 1 | 3 | 2
0,1,3 | 2 | 1 | 3
1,3,2 | 2 | 3 | 2
0,1,3,2 | 3 | 3 | 2
b) for the paths with 2 and 3 hops, get the first and last node id from the path:
-- as above, but instead of the last line: "SELECT * FROM backbone" ...
,
origin_and_dest AS (
SELECT
*
, SPLIT_PART(path,',',1 )::INT AS origin
, SPLIT_PART(path,',',hops + 1)::INT AS destination
FROM backbone
WHERE hops >=2
)
SELECT * FROM origin_and_dest;
path | hops | source | target | origin | destination
---------+------+--------+--------+--------+-------------
0,1,3 | 2 | 1 | 3 | 0 | 3
1,3,2 | 2 | 3 | 2 | 1 | 2
0,1,3,2 | 3 | 3 | 2 | 0 | 2
With the ultimate source id and the ultimate target id out of the path, join back to nodes:
-- same as above, but instead of the last line: "SELECT * FROM origin_and_dest" ...
SELECT
origin.x AS source_x
, origin.y AS source_y
, dest.x AS target_y
, dest.y AS target_y
FROM origin_and_dest
JOIN node AS origin ON origin=origin.id
JOIN node AS dest ON destination=dest.id
;
source_x | source_y | target_y | target_y
----------+----------+----------+----------
0 | 0 | 1 | 0
0 | 0 | 1 | 1
0 | 1 | 1 | 0
Not quite your result, but then I don't really know what you mean by "entire edge information" ... can you be more precise?

Related

SQL recursion + column concatenation

I've got a self referencing table (in SQL Server):
Page
==========
Id: int
RelativeUrl: nvarchar(max)
ParentId: int -> FK to pageId
Example data:
ID | RelativeUrl | ParentId
===============================
1 | /root | null
2 | /test1 | 1
3 | /test2 | 1
4 | /test3 | 1
5 | /test1-1 | 2
6 | /test1-2 | 2
7 | /test1-1-1 | 5
I want to create an sql query to retrieve all pages of the table with FULL relative url.
I thought about using a recursive SQL query, but don't know how to concat the relativeurl to make it a full relative url.
Wanted result:
ID | FullRelativeUrl | ParentId
===================================================
1 | /root | null
2 | /root/test1 | 1
3 | /root/test2 | 1
4 | /root/test3 | 1
5 | /root/test1/test1-1 | 2
6 | /root/test1/test1-2 | 2
7 | /root/test1/test1-1/test1-1-1 | 5
You can use a recursive CTE:
with cte as (
select id, convert(varchar(max), relativeurl) as url, 1 as lev
from page
where parentid is null
union all
select p.id, concat(cte.url, p.relativeurl), lev + 1
from cte join
page p
on p.parentid = cte.id
)
select cte.*
from cte;
Here is a db<>fiddle.

creating pivot table in sql filled with 1 and 0

I have table1 which contains ids [s1,s2,s3..Sn]
s1
s2
s3
I have another table2 containing indexes [0,1,2,..n].
0
1
2
3
I have third table3 [ids, start_index,stop_index]. eg:
s0 | 1 | 3 |
s0 | 4 | 6 |
s1 | 1 | 2 |
and so on. I want to create a pivot table which contains rows a s1,s2,s3... columns with 0,1,2,3.. content of the table should be either zero or 1.
---|0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |...
s0 |0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
s1 |0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
.
.
You can use a CROSS JOIN in concert with your index table and PIVOT the final results.
If you number of columns is variable, you would need DYNAMIC SQL
Example
Select *
From (
Select id
,item = a.[index]
,value = case when a.[index] between b.start_index and b.stop_index then 1 else 0 end
From table2 A
Cross Join table3 B
) src
Pivot (max(value) for Item in ([0],[1],[2],[3],[4],[5],[6],[7],[8],[9],[10]) ) pvt
Returns

How to implement double SELECT in recursive cte on SQL?

I faced a problem while implementing a recursive query on SQL, including double selection from the database at each iteration.
I have two tables, describing the oriented graph, where each nodes located at grid:
table nodes:
id
grid_x
grid_y
table edges:
source
target
And want to implement selection of nodes, which located at adjacent positions on the grid to the right of the initial node with selection of parents and childs at each iteration.
Selection of right nodes realized by following recursive query:
WITH RECURSIVE
cte(id, grid_x, grid_y) AS (
SELECT id, grid_x, grid_y
FROM nodes WHERE id = 0
UNION ALL
SELECT current.id, current.grid_x, current.grid_y
FROM nodes AS current, cte AS prev
WHERE current.grid_x = prev.grid_x + 1
AND current.grid_y = prev.grid_y
)
SELECT * FROM cte;
let the data in the database have the following representation
i.e.
Table nodes:
id | grid_x | grid_y | |
----+--------+--------+---+--
0 | 0 | 0 | |
13 | 1 | 0 | |
14 | 3 | 0 | |
5 | -1 | 1 | |
1 | 0 | 1 | |
2 | 1 | 1 | |
3 | 2 | 1 | |
4 | 4 | 1 | |
12 | -2 | 2 | |
6 | -1 | 2 | |
7 | 0 | 2 | |
8 | 2 | 2 | |
9 | 3 | 2 | |
10 | 4 | 2 | |
11 | 6 | 2 | |
Table edges:
id | source | target
----+--------+--------
1 | 0 | 5
2 | 0 | 1
3 | 1 | 7
4 | 5 | 6
5 | 5 | 12
6 | 13 | 2
7 | 13 | 3
8 | 2 | 8
9 | 3 | 8
10 | 3 | 9
11 | 14 | 3
12 | 14 | 4
13 | 4 | 10
14 | 4 | 11
query above will select nodes 0 and 13
I want to select at each iteration not only the nodes located on the right, but also the child and parent nodes, if they are not already in the cte.
For example, a query that should also select child nodes should start from node 0. Then at firts iteration select nodes 13, 5 and 1, at second iteration select nodes 2, 3, 12, 6, 7, at third iteration - nodes 8, 9 and at fourth iteration - node 10.
The query, which select at each iteration parents and childs, which not already in cte with neighbor right nodes at grid should select all nodes from picture at finish.
I tried to write the following query for select right nodes with their childs:
WITH RECURSIVE
cte(id, grid_x, grid_y) AS (
SELECT id, grid_x, grid_y
FROM nodes WHERE id = 0
UNION ALL
SELECT * FROM (
SELECT current.id, current.grid_x, current.grid_y
FROM nodes AS current, cte AS prev
WHERE current.grid_x = prev.grid_x + 1
AND current.grid_y = prev.grid_y
UNION
SELECT c.id, c.grid_x, c.grid_y
FROM nodes AS c, cte AS p, edges AS e
WHERE p.id = e.source AND c.id = e.target
AND here should be checking on existing node at cte
)
)
SELECT * FROM cte;
But recursive reference to query "cte" must not appear within a subquery
Query like this
WITH RECURSIVE
cte(id, grid_x, grid_y) AS (
SELECT id, grid_x, grid_y
FROM nodes WHERE id = 0
UNION ALL
SELECT current.id, current.grid_x, current.grid_y
FROM nodes AS current, edges AS e, cte AS prev
WHERE (current.grid_x = prev.grid_x + 1
AND current.grid_y = prev.grid_y)
OR (prev.id = e.source AND e.target = current.id)
)
SELECT * FROM cte;
But this query selects only node 0
Please, help me solve this problem.

Convert an adjacency list to an ltree in PostgreSQL

I have a tree structure stored as an adjacency list in PostgreSQL 9.6:
"departments" table (parent_id = -1 means a node has no parent)
id | name | parent_id
-----------------------------------
-1 | NULL | -1
1 | Dep_1 | -1
2 | Dep_2 | 1
3 | Dep_3 | 1
4 | Dep_4 | 3
5 | Dep_5 | -1
I need to convert it to ltree type with id's as path labels:
id | name | path
------------------------------
1 | Dep_1 | 1
2 | Dep_2 | 1.2
3 | Dep_3 | 1.3
4 | Dep_4 | 1.3.4
5 | Dep_5 | 5
What's the easiest way to do this? Is it possible using SQL only?
let's say I create a table a and fill it with your data, then recursive CTE will do it neat:
t=# with recursive r(id, name, path) as (
select id, name, ''::text
from a
where id = -1
union all
select o.id, o.name, concat(path, '.', o.id)
from a o
join r n on o.parent_id = n.id and o.id <> -1
)
select id,name,right(path,-1)::ltree path
from r
where id <> -1
order by id asc;
id | name | path
----+------------+-------
1 | Dep_1 | 1
2 | Dep_2 | 1.2
3 | Dep_3 | 1.3
4 | Dep_4 | 1.3.4
5 | Dep_5 | 5
(5 rows)

select records where condition is true in one record

I need to select cid, project, and owner from rows in the table below where one or more rows for a cid/project combination has an owner of 1.
cid | project | phase | task | owner
-----------------------------------
1 | 1 | 1 | 1 | 1
1 | 1 | 1 | 2 | 2
1 | 1 | 1 | 3 | 2
2 | 1 | 1 | 1 | 1
2 | 1 | 1 | 2 | 1
3 | 1 | 1 | 3 | 2
My output table should look like the this:
cid | project | phase | task | owner
-----------------------------------
1 | 1 | 1 | 1 | 1
1 | 1 | 1 | 2 | 2
1 | 1 | 1 | 3 | 2
2 | 1 | 1 | 1 | 1
2 | 1 | 1 | 2 | 1
The below query is what I came up with. It does seem to test okay, but my confidence is low. Is the query an effective way to solve the problem?
select task1.cid, task1.project, task1.owner
from
(select cid, project, owner from table) task1
right join
(select distinct cid, project, owner from table where owner = 1) task2
on task1.cid = task2.cid and task1.project = task2.project
(I did not remove the phase and task columns from the sample output so that it would be easier to compare.)
You can simply use a IN clause
select cid, project, owner
from table
where cid in (select distinct id from table where owner = 1)
or a inner join with a subquery
select a.cid, a.project, a.owner
from table a
INNER JOIN ( select distinct cid , project
from table where owner = 1
) t on t.cid = a.cid and t.project = a.project