Postgresql - How to select multiple tables by specific columns and append them - postgresql

I would like to select a number of tables and select the geometry (geom) and Name columns in each of the tables and append below each other. I have gotten as far as selecting the tables and their columns as shown below:
SELECT TABLE_NAME COLUMN_NAME
FROM INFORMATION_SCHEMA.columns
WHERE (TABLE_NAME LIKE '%HESA' OR
TABLE_NAME LIKE '%HEWH') AND
(COLUMN_NAME = 'geom' AND
COLUMN_NAME = 'Name');
How do you then take the tables:
id | geom | Name | id | geom | Name |
____________________ ____________________
1 | geom1 | Name1 | 1 | geom4 | Name4 |
2 | geom2 | Name2 | 2 | geom5 | Name5 |
3 | geom3 | Name3 | 3 | geom6 | Name6 |
And append the second table below the first, like this:
id | geom | Name |
____________________
1 | geom1 | Name1 |
2 | geom2 | Name2 |
3 | geom3 | Name3 |
1 | geom4 | Name4 |
2 | geom5 | Name5 |
3 | geom6 | Name6 |
Do I use UNION ALL or something else?

https://www.db-fiddle.com/f/75fgQMEWf9LvPj4xYMGWvA/0
based on your sample data:
do
'
declare
r record;
begin
for r in (
SELECT a.TABLE_NAME
FROM INFORMATION_SCHEMA.columns a
JOIN INFORMATION_SCHEMA.columns b on a.TABLE_NAME = b.TABLE_NAME and a.COLUMN_NAME = ''geom'' and b.COLUMN_NAME = ''name''
WHERE (a.TABLE_NAME LIKE ''oranges%'' OR a.TABLE_NAME LIKE ''%_db'')
) loop
execute format(''insert into rslt select geom, name from %I'',r.table_name);
end loop;
end;
'
;

Union All will do the job just fine:
SELECT
*
FROM (
(SELECT * FROM table_one)
UNION ALL
(SELECT * FROM table_two)
) AS tmp
ORDER BY name ASC;
I have added the external SELECT, to show you how you can order the whole result.
DB Fiddle can be found here

Related

how to unnest multiple arrays and transpose table in postgres 9.5

Lets suppose i have this table:
project |leader|coordination|staff |support |
---------|------|------------|-------------------|-----------|
project_a|Bob |{Alice} |{Terry,Mandy,James}|{Bob,Peter}|
project_b|Alice |{Terry, Bob}|{Mandy,James} |{Maggie} |
project_c|Maggie|{Bob} |{Terry,Peter} |{Alice,Bob}|
and i want to transform it to something like this:
person|project_1|role_1|project_2|role_2 |project_3|role_3 |project_4|role_4 |
------|---------|------|---------|------------|---------|-------|---------|------------|
Alice |project_b|leader|project_a|coordination|project_c|support| | |
Bob |project_a|leader|project_a|support |project_b|staff |project_c|coordination|
Terry...
Maggie...
...
Big thanks to #abelisto for pointing me in the right direction in the comments.
The problem with crosstab is, that i need the project also in the row:
SELECT *
FROM crosstab(
'
select
person,
project,
role
from
tmp_projects
cross join lateral (
select
\'leader\'::text as role, leader as person
union all
select
\'coordination\', x
from
unnest(coordination) as x
union all
select
\'staff\', x
from
unnest(staff) as x
union all
select
\'support\', x
from
unnest(support) as x) as t order by 1,2' -- needs to be "ORDER BY 1,2" here
, 'SELECT DISTINCT project FROM tmp_projects ORDER BY 1'
) AS ct ("Person" text, "Project_1" text,"Project_2" text,"Project_3" text);
#Abelisto had the correct idea and a solution could be:
create view tmp_view_projects_unnest as select
person,
row_number() OVER (PARTITION by person) AS rn,
project,
role
from
tmp_projects
cross join lateral (
select
'leader'::text as role, leader as person
union all
select
'coordination', x
from
unnest(coordination) as x
union all
select
'staff', x
from
unnest(staff) as x
union all
select
'support', x
from
unnest(support) as x) as t order by 1,2
person|rn|project |role |
------|--|---------|------------|
Alice | 1|project_a|leader |
Alice | 2|project_d|coordination|
Alice | 3|project_c|support |
Bob | 1|project_d|coordination|
Bob | 2|project_c|coordination|
Bob | 3|project_c|support |
Maggie| 1|project_c|leader |
Mandy | 1|project_d|leader |
Marry | 1|project_d|staff |
Peter | 1|project_d|support |
Peter | 2|project_c|staff |
Peter | 3|project_a|coordination|
Terry | 1|project_a|coordination|
Terry | 2|project_c|staff |
select tvp.person,
tvp.project as project_1, tvp.role as role_1 ,
tvp2.project as project_2, tvp2.role as role_2,
tvp3.project as project_3, tvp3.role as role_3,
tvp4.project as project_4, tvp4.role as role_4
from tmp_view_projects_unnest tvp
left join tmp_view_projects_unnest tvp2 on tvp2.person = tvp.person and tvp2.rn =2
left join tmp_view_projects_unnest tvp3 on tvp3.person = tvp.person and tvp3.rn =3
left join tmp_view_projects_unnest tvp4 on tvp4.person = tvp.person and tvp4.rn =4
where tvp.rn=1;
person|project_1|role_1 |project_2|role_2 |project_3|role_3 |project_4|role_4|
------|---------|------------|---------|------------|---------|------------|---------|------|
Alice |project_a|leader |project_d|coordination|project_c|support | | |
Bob |project_d|coordination|project_c|coordination|project_c|support | | |
Maggie|project_c|leader | | | | | | |
Mandy |project_d|leader | | | | | | |
Marry |project_d|staff | | | | | | |
Peter |project_d|support |project_c|staff |project_a|coordination| | |
Terry |project_a|coordination|project_c|staff | | | | |

Postgres 10 lateral unnest missing null values

I have a Postgres table where the content of a text column is delimited with '|'.
ID | ... | my_column
-----------------------
1 | ... | text|concatenated|as|such
2 | ... | NULL
3 | ... | NULL
I tried to unnest(string_to_array()) this column to separate rows which works fine, except that my NULL values (>90% of all entries) are excluded. I have tried several approaches:
SELECT * from "my_table", lateral unnest(CASE WHEN "this_column" is NULL
THEN NULL else string_to_array("this_column", '|') END);
or
as suggested here: PostgreSQL unnest with empty array
What I get:
ID | ... | my_column
-----------------------
1 | ... | text
1 | ... | concatenated
1 | ... | as
1 | ... | such
But this is what I need:
ID | ... | my_column
-----------------------
1 | ... | text
1 | ... | concatenated
1 | ... | as
1 | ... | such
2 | ... | NULL
3 | ... | NULL
Use a LEFT JOIN instead:
SELECT m.id, t.*
from my_table m
left join lateral unnest(string_to_array(my_column, '|')) as t(w) on true;
There is no need for the CASE statement to handle NULL values. string_to_array will handle them correctly.
Online example: http://rextester.com/XIGXP80374

Select all columns from two tables

Lets say I have the following:
table_a
| id | date | order_id | sku | price |
--------------------------------------------
| 10 | 2016-08-18 | 111 | ABC | 10 |
table_b
| id | date | order_id | description | type | notes | valid |
-------------------------------------------------------------------
| 50 | 2016-08-18 | 111 | test | AA | | true |
I want to get get all columns from both tables, so the resulting table looks like this:
| id | date | order_id | sku | price | description | type | notes | valid |
---------------------------------------------------------------------------------
| 10 | 2016-08-18 | 111 | ABC | 10 | | | | |
---------------------------------------------------------------------------------
| 50 | 2016-08-18 | 111 | | | test | AA | | true |
I tried union:
(
SELECT *
from table_a
where table_a.date > Date('today')
)
UNION
(
SELECT *
from table_b
where table_b.date > Date('today')
)
But I get a:
ERROR: each UNION query must have the same number of columns
How can this be fixed / is there another way to do this?
Easily :)
(
SELECT id, date, order_id, sku, price, NULL AS description, NULL AS type, NULL AS notes, NULL AS valid
from table_a
where table_a.date > Date('today')
)
UNION
(
SELECT id, date, order_id, NULL AS sku, NULL AS price, description, type, notes, valid
from table_b
where table_b.date > Date('today')
)
Alternatively, instead of UNION you can just JOIN them:
SELECT *
FROM table_a A
JOIN table_b B USING ( id )
WHERE A.date > TIMESTAMP 'TODAY'
AND B.date > TIMESTAMP 'TODAY';
See more options: https://www.postgresql.org/docs/9.5/static/queries-table-expressions.html#QUERIES-JOIN

Updating multiple rows with a certain value from the same table

So, I have the next table:
time | name | ID |
12:00:00| access | 1 |
12:05:00| select | null |
12:10:00| update | null |
12:15:00| insert | null |
12:20:00| out | null |
12:30:00| access | 2 |
12:35:00| select | null |
The table is bigger (aprox 1-1,5 mil rows) and there will be ID equal to 2,3,4 etc and rows between.
The following should be the result:
time | name | ID |
12:00:00| access | 1 |
12:05:00| select | 1 |
12:10:00| update | 1 |
12:15:00| insert | 1 |
12:20:00| out | 1 |
12:30:00| access | 2 |
12:35:00| select | 2 |
What is the most simple method to update the rows without making the log full? Like, one ID at a time.
You can do it with a sub query:
UPDATE YourTable t
SET t.ID = (SELECT TOP 1 s.ID
FROM YourTable s
WHERE s.time < t.time AND s.name = 'access'
ORDER BY s.time DESC)
WHERE t.name <> 'access'
Index on (ID,time,name) will help.
You can do it using CTE as below:
;WITH myCTE
AS ( SELECT time
, name
, ROW_NUMBER() OVER ( PARTITION BY name ORDER BY time ) AS [rank]
, ID
FROM YourTable
)
UPDATE myCTE
SET myCTE.ID = myCTE.rank
SELECT *
FROM YourTable ORDER BY ID

Recursive SQL PostgreSQL Empty Result Set

The categories table:
=# \d
List of relations
Schema | Name | Type | Owner
--------+-------------+-------+-------
public | categories | table | pgsql
public | products | table | pgsql
public | ticketlines | table | pgsql
(3 rows)
Contents of categories:
=# select * from categories;
id | name | parentid
----+--------+----------
1 | Rack |
2 | Women | 1
3 | Shorts | 2
4 | Wares |
5 | Toys | 4
6 | Trucks | 5
(6 rows)
Running the following query:
WITH RECURSIVE nodes_cte(name, id, parentid, depth, path) AS (
-- Base case?
SELECT c.name,
c.id,
c.parentid,
1::INT AS depth,
c.id::TEXT AS path
FROM categories c
WHERE c.parentid = ''
UNION ALL
-- nth case
SELECT c.name,
c.id,
c.parentid,
n.depth + 1 AS depth,
(n.path || '->' || c.id::TEXT)
FROM nodes_cte n
JOIN categories c on n.id = c.parentid
)
SELECT * FROM nodes_cte AS n GROUP BY n.name, n.id, n.parentid, n.depth, n.path ORDER BY n.id ASC
;
yields these results:
name | id | parentid | depth | path
--------+----+----------+-------+---------
Rack | 1 | | 1 | 1
Women | 2 | 1 | 2 | 1->2
Shorts | 3 | 2 | 3 | 1->2->3
Wares | 4 | | 1 | 4
Toys | 5 | 4 | 2 | 4->5
Trucks | 6 | 5 | 3 | 4->5->6
(6 rows)
Great!
But given a similar table (categories):
=# \d categories
Table "public.categories"
Column | Type | Modifiers
----------+-------------------+-----------
id | character varying | not null
name | character varying | not null
parentid | character varying |
image | bytea |
Indexes:
"categories_pkey" PRIMARY KEY, btree (id)
"categories_name_inx" UNIQUE, btree (name)
Referenced by:
TABLE "products" CONSTRAINT "products_fk_1" FOREIGN KEY (category) REFERENCES categories(id)
=# select * from categories;
id | name | parentid | image
--------------------------------------+-------+--------------------------------------+-------
611572c9-326d-4cf9-ae4a-af5269fc788e | Rack | |
22d15300-40b5-4f43-a8d1-902b8d4c5409 | Women | 611572c9-326d-4cf9-ae4a-af5269fc788e |
6b061073-96f4-49a1-9205-bab7c878f0cf | Wares | |
3f018dfb-e6ee-40d1-9dbc-31e6201e7625 | Toys | 6b061073-96f4-49a1-9205-bab7c878f0cf |
(4 rows)
the same query produces zero rows.
Why?
Is it something to do with primary / foreign keys?
WHERE COALESCE(parent_id, '') = ''
Worked. Thank you.