how to sort rows according to multiple dependencies - postgresql

The table has many columns, but for the problematic part let's assume only two, [ID] and [dependency].
[Dependency] means which [ID]s should be listed before this [ID].
Each row has its unique [ID] but it might have none, one or more dependencies in the [Dependency] column
ID
Dependency
1
4
2
null
3
1,2
4
2
5
1,2,4
Expected Result
ID
Dependency
2
null
4
2
1
4
3
1,2
5
1,2,4
I have no prior experience in Postgres, I found this very useful:
SELECT aa.dep_id::integer FROM unnest(string_to_array(ss.dependency, ',')) aa(dep_id)
But still, I can't make it right.
EDIT: Added one row with 3 dependencies'
http://sqlfiddle.com/#!15/894c3/4

Use a recursive CTE:
WITH RECURSIVE o AS (
SELECT ss.id, ss.dependency,
1 AS level
FROM ss
WHERE dependency IS NULL
UNION ALL
SELECT ss.id, ss.dependency,
o.level + 1
FROM ss
JOIN o
ON o.id IN (SELECT x
FROM unnest(ss.dependency) AS x(x)
)
)
SELECT o.id, o.dependency
FROM o
GROUP BY o.id, o.dependency
ORDER BY max(o.level);

My working solution, I hope it can be improved
DO $do$
DECLARE v_RowCountInt Int;
--copy all values to temp table
begin
--This is the origin table, all rows to sort
drop table if exists _tempAll;
create temp table _tempAll as select id, dependency from XXXXX;
-- create temp table for results
drop table if exists _tempSort;
create temp table _tempSort (
id integer
,dependency varchar
,pos serial primary key);
-- move all IDs with no depencencies
WITH tmp AS (DELETE FROM _tempAll where dependency is null RETURNING id, dependency)
INSERT INTO _tempSort ( id, dependency)
SELECT id, dependency FROM tmp;
GET DIAGNOSTICS v_RowCountInt = ROW_COUNT;
while v_RowCountInt>0 loop
-- move rows with all dependencies met
WITH tmp AS (DELETE FROM _tempAll a
where a.id in(SELECT a.id FROM _tempSort s inner join _tempAll a on
s.id in (select split.dep_sid::integer from unnest(string_to_array(a.dependency, ',')) split(dep_sid))
group by a.id, a.dependency
-- verify all dependencies are already sorted
having count(*)=(CHAR_LENGTH(a.dependency) - CHAR_LENGTH(REPLACE(a.dependency, ',', ''))+1))
RETURNING a.id, a.dependency)
INSERT INTO _tempSort (id, dependency)
SELECT id, dependency FROM tmp;
GET DIAGNOSTICS v_RowCountInt = ROW_COUNT;
end loop;
end;
$do$

Related

How to add items to local function array in postgress?

I need to do multiple inserts in the table that number depends on how many records we got from select. I need to iterate over records from select and then insert them to another table. I want to get all new Ids from insert to array to use them later in the following inserts, how can I do this?
I can't collect them using select after insert, because there can be old records.
for record in (select test, test1, test2
from public.a
join public.b on a.reg_id = b.id
where a.id = arg_id) loop
(INSERT into public.c
( a, b, c)
(select test, test1, test2
from record)--need to get ids from this
end loop;
---
some block where I have old_ids
---
--to insert them there
insert into public.d(d,e,f,g)values(..,..,old_id,(id from previous insert))
upd
Tried to make like this:
with a2 as(
INSERT INTO public.reg
(name_, code, state)
(select a.secondname, a.code, b.state_name--multiple rows from select
from public.client a
left join public.states b on a.state_id = b.id
where a.id = id_p) RETURNING id
)
INSERT INTO public.request
(phone, address, qty, prod_id, reg_id)
(select phone, address, qty, prod_id, (select id from a2)--maybe something wrong there, but error happend before
from public.shp a
where a.id = id_p);
but getting an error: more than one row returned by a subquery used as an expression
Demonstration of using the result of a query:
\i tmp.sql
CREATE TABLE aa(aa integer not null primary key);
CREATE TABLE bb(bb integer not null primary key);
CREATE TABLE cc(cc integer not null primary key);
WITH x0 AS (
INSERT INTO aa(aa) values (1),(2),(3)
returning aa
)
, x1 AS (
INSERT INTO bb(bb)
SELECT aa*aa from x0
returning bb
)
, x2 AS (
INSERT INTO cc(cc)
SELECT bb*bb from x1
returning cc
)
-- main query
SELECT *
FROM x2
;
-- Check cc
SELECT *
FROM cc
;
Output:
DROP SCHEMA
CREATE SCHEMA
SET
CREATE TABLE
CREATE TABLE
CREATE TABLE
cc
----
1
16
81
(3 rows)
cc
----
1
16
81
(3 rows)

Same name attributes in select list in pg-promise

Is it possible to get the same name attributes in the select list (as JSON deduplicates them)?
For instance:
CREATE TABLE t1 (
id int;
);
INSERT INTO t1 VALUES(1);
INSERT INTO t1 VALUES(2);
CREATE TABLE t2 (
id int;
);
INSERT INTO t2 VALUES(1);
SELECT *
FROM t1 LEFT JOIN t2 ON t1.id = t2.id
should return:
id id
-----
1 1
2 null
but will return instead:
id
---
1
null
I'm trying to build a web-based SQL editor, and this is kind of a showstopper.
Sorry, found it, it was solved in:
pg: https://github.com/brianc/node-postgres/pull/393
and subsequently in pg-promise: https://github.com/vitaly-t/pg-promise/releases/tag/v.4.0.5
One can use rowMode argument to get results as an array:
http://vitaly-t.github.io/pg-promise/PreparedStatement.html#rowMode

PostgreSql: how to update table from array?

How to update table fields from array to avoid temp table using?
User passes array to reorder table.
create TABLE items
(
id serial primary key,
name text,
select_order int
)
insert into items(name, select_order)
values
('cucumber',0),
('milk',1),
('coffee',2),
('water',3)
select * from items order by select_order;
DO
$body$
DECLARE var_array int[]:='{3,0,2,1}';
BEGIN
update items ??
END;
$body$
The final result in this example should be
select * from items order by select_order;
name select_order
------------------------
water 0
cucumber 1
coffee 2
milk 3
Assuming the index in the array corresponds to the value in select_order the following query returns the new and old value:
select i.id, i.select_order, t.new_sort
from items i
join lateral unnest(array[3,0,2,1]) with ordinality as t(new_sort, idx) on t.idx - 1 = i.select_order
Now this can be used to update the target table:
update items
set select_order = r.new_sort
from (
select i.id, i.select_order, t.new_sort
from items i
join lateral unnest(array[3,0,2,1]) with ordinality as t(new_sort, idx) on t.idx - 1 = i.select_order
) r
where r.id = items.id;
This also assumes that select_order starts a 0 and has no gaps.

T-SQL: Determine upline and downline from hierarchy without the Parent ID

I have this self-referencing table where-in I should get the upline and downline and hierarchy levels without the Parent ID provided.
Any ideas?
Have you tried using recursive CTE?
for example:
assume you have table tbl(EmpId, Name, MngrId) which has self-referencing relationship
create table tbl
(
EmpId int not null,
Name nvarchar(100),
MngrId int null)
insert into tbl(EmpId, Name, MngrId)
values (1,'Adel',Null),
(2,'Ali',1),
(3,'Shaban',1),
(4,'Mark',3),
(5,'John',3),
(6,'Tony',Null),
(7,'Peter',6)
You can create some view like that:
create view Employees
Begin
with cte
as
(
Select EmpId,Name, Null as MngrId, cast(null as nvarchar(100)) as MngrName, 1 as EmpLevel
from tbl
where MngrId is Null
Union All
Select t.EmpId, t.Name, c.EmpId as MngrId, c.Name as MngrName, c.EmpLevel + 1 as EmpLevel
from tbl t
inner join cte c
on t.MngrId = c.EmpId
)
Select *
from cte
order by EmpLevel, EmpId
End
You can now use EmpLevel to jump between different levels and MngrName to get information about parent node

T-SQL A question about inner join table variable

in my stored procedure I have a table variable contains rows ID. The are 2 scenarios - that table variable is empty and not.
declare #IDTable as table
(
number NUMERIC(18,0)
)
In the main query, I join that table:
inner join #IDTable tab on (tab.number = csr.id)
BUT:
as we know how inner join works, I need that my query returns some rows:
when #IDTable is empty
OR
return ONLY rows that exist in
#IDTable
I tried also with LEFT join but it doesn't work. Any ideas how to solve it ?
If `#IDTable' is empty then what rows do you return? Do you just ignore the Join on to the table?
I'm not sure I get what you're trying to do but this might be easier.
if (Select Count(*) From #IDTable) == 0
begin
-- do a SELECT that doesn't join on to the #IDTable
end
else
begin
-- do a SELECT that joins on to #IDTable
end
It is not optimal, but it works:
declare #z table
(
id int
)
--insert #z values(2)
select * from somTable n
left join #z z on (z.id = n.id)
where NOT exists(select 1 from #z) or (z.id is not null)