Postgresql - Renumber columns - postgresql

I need to renumber the rows of my database. After I delete some rows I have to renumber certain column. How can I do that using postgresql?
UPDATE:
For example:
I have a table like this:
ac_n_circ name
1 x
2 y
8 c
15 b
And I want to renumber this table like this:
ac_n_circ name
1 x
2 y
3 c
4 b
Is there any algorithm or something like that to do this in posgresql?
Thanks!

Caution:
This only makes sense if ac_n_circ is NOT the primary key column.
If you are sure you need this (are you really?), then something like the following should work:
with new_numbers as (
select row_number() over (order by ac_n_circ) as new_nr,
ac_n_circ,
id
from foo
)
update foo
set ac_n_circ = nn.new_nr
from new_numbers nn
where nn.id = foo.id;
Alternatively:
update foo
set ac_n_circ = nn.new_number
from (
select id,
ac_n_circ,
row_number() over (order by ac_n_circ) as new_number
from foo
) nn
where nn.id = foo.id;
Both statements assume that there is a primary key column named id.

Related

postgres - single record with one to many join tables

I have two tables a and b. Where main table is 'a' where I want to select from and table 'b' is for filtering.
Below is sample table with some data.
create table a (
id varchar primary key,
name varchar,
p varchar[]
);
insert into a (id, name, p) values
('1', 'v1', array['p1']),
('2', 'v2', array['p1','p2']),
('3', 'v3', array['p2','p3']);
create table b (
p varchar,
x varchar
);
insert into b(p, x) values
('p1', 'x1'),
('p2', 'x2'),
('p3', 'x1'),
('p3', 'x3'),
('p1', 'x2');
I want only one row from table a based on join on column p and filter on x. I tried few options, it works when I have one to one record in a and b but when I have one to many then I get multiple records.
select a.* from a,b where b.p=any(a.p) and b.x='x2';
Output I get is:
id name p
-----------------
1 v1 p1
2 v2 p1,p2
2 v2 p1,p2
3 v3 p2,p3
What I want is
id name p
-----------------
1 v1 p1
2 v2 p1,p2
3 v3 p2,p3
Also I am expecting table 'a' to have millions of rows and 'b' will have only few, so query has to be perform effectively.
As you only want columns from table a use an exists condition rather than a join:
select a.*
from a
where exists (select *
from b
where b.p = any(a.p)
and b.x='x2');
That will however be quite hard to optimize.
Another option - if not too many rows match the join criteria - is to apply a distinct on the result of the join.
In that case a GIN index on the array column can be used:
create index on a using gin (p);
select distinct a.*
from a
join b on a.p #> array[b.p]
where b.x= 'x2';

Calculation in a table with a value from another table

Assumed we have two tables:
Table A:
a b c
x 1 null
x 2 null
y 3 null
Table B:
a b
x 5
y 10
I want to update Table A by multiplication of TableA.b with TableB.b and writing it into TableA.c. The value of TableB should be selected by the condition TableA.a = TableB.a. Thus my updated TableA should look like this:
Table A:
a b c
x 1 5
x 2 10
y 3 30
I thought to do a join of both tables before, but im not sure. What do you think is the easiest and best solution?
In Postgres, you can use the update ... set ... from ... where syntax.
Consider:
update tablea ta
set c = ta.b * tb.b
from tableb tb
where tb.a = ta.a

Create multiple incrementing columns using with recursive in postgresql

I'm trying to create a table with the following columns:
I want to use a with recursive table to do this. The following code however is giving the following error:
'ERROR: column "b" does not exist'
WITH recursive numbers AS
(
SELECT 1,2,4 AS a, b, c
UNION ALL
SELECT a+1, b+1, c+1
FROM Numbers
WHERE a + 1 <= 10
)
SELECT * FROM numbers;
I'm stuck because when I just include one column this works perfectly. Why is there an error for multiple columns?
This appears to be a simple syntax issue: You are aliasing the columns incorrectly. (SELECT 1,2,4 AS a, b, c) is incorrect. Your attempt has 5 columns: 1,2,a,b,c
Break it down to just: Select 1,2,4 as a,b,c and you see the error but Select 1 a,2 b,4 c works fine.
b is unknown in the base select because it is being interpreted as a field name; yet no table exists having that field. Additionally the union would fail as you have 5 fields in the base and 3 in the recursive union.
DEMO: http://rextester.com/IUWJ67486
One can define the columns outside the select making it easier to manage or change names.
WITH recursive numbers (a,b,c) AS
(
SELECT 1,2,4
UNION ALL
SELECT a+1, b+1, c+1
FROM Numbers
WHERE a + 1 <= 10
)
SELECT * FROM numbers;
or this approach which aliases the fields internally so the 1st select column's names would be used. (a,b,c) vs somereallylongalias... in union query. It should be noted that not only the name of the column originates from the 1st query in the unioned sets; but also the datatype for the column; which, must match between the two queries.
WITH recursive numbers AS
(
SELECT 1 as a ,2 as b,4 as c
UNION ALL
SELECT a+1 someReallyLongAlias
, b+1 someReallyLongAliasAgain
, c+1 someReallyLongAliasYetAgain
FROM Numbers
WHERE a<5
)
SELECT * FROM numbers;
Lastly, If you truly want to stop at 5 then the where clause should be WHERE a < 5. The image depicts this whereas the query does not; so not sure what your end game is here.

How to do conditional aggregation in postgres?

I want to do conditional aggregation in Postgres. I have a table with an id column and a float items column.
The query I want to write is something like this:
SELECT ( ((SUM(items) * 3) WHERE id="123"))
+ ((SUM(items) * 2) WHERE items="124")) )
FROM mytable
And I'd like to get a single number out. So for example, if my table looked like this:
org_id items
123 10
123 3
124 12
I'd like to get back the single number 63 (i.e. 13*3 + 12*2).
I'm pretty sure I need a CASE statement, but I'm not sure how to implement it.
In PostgreSQL 9.4+:
SELECT SUM(items) FILTER (WHERE org_id = 123) * 3 +
SUM(items) FILTER (WHERE org_id = 124) * 2
FROM mytable
, or, in earlier versions,
SELECT SUM(items * CASE org_id WHEN 123 THEN 3 WHEN 124 THEN 2 END)
FROM mytable
However, if you have lots of pairs like that, it would make sense to store them in a table (rather than hardcoding) and just use this:
SELECT SUM(items * coeff)
FROM (
VALUES
(123, 3),
(124, 2)
) m (id, coeff)
JOIN mytable
USING (id)
Replace the nested query m with your actual table

PostgreSQL: set a column with the ordinal of the row sorted via another field

I have a table segnature describing an item with a varchar field deno and a numeric field ord. A foreign key fk_collection tells which collection the row is part of.
I want to update field ord so that it contains the ordinal of that row per each collection, sorted by field deno.
E.g. if I have something like
[deno] ord [fk_collection]
abc 10
aab 10
bcd 10
zxc 20
vbn 20
Then I want a result like
[deno] ord [fk_collection]
abc 1 10
aab 0 10
bcd 2 10
zxc 1 20
vbn 0 20
I tried with something like
update segnature s1 set ord = (select count(*)
from segnature s2
where s1.fk_collection=s2.fk_collection and s2.deno<s1.deno
)
but query is really slow: 150 collections per about 30000 items are updated in 10 minutes about.
Any suggestion to speed up the process?
Thank you!
You can use a window function to generate the "ordinal" number:
with numbered as (
select deno, fk_collection,
row_number() over (partition by fk_collection order by deno) as rn,
ctid as id
from segnature
)
update segnature
set ord = n.rn
from numbered n
where n.id = segnature.ctid;
This uses the internal column ctid to uniquely identify each rows. The ctid comparison is quite slow, so if you have a real primary (or unique) key in that table, use that column instead.
Alternatively without the common table expression:
update segnature
set ord = n.rn
from (
select deno, fk_collection,
row_number() over (partition by fk_collection order by deno) as rn,
ctid as id
from segnature
) as n
where n.id = segnature.ctid;
SQLFiddle example: http://sqlfiddle.com/#!15/e997f/1