Recursive postgres query to view - postgresql

I have the following table which models a very simple hierarchical data structure with each element pointing to its parent:
Table "public.device_groups"
Column | Type | Modifiers
--------------+------------------------+---------------------------------------------------------------
dg_id | integer | not null default nextval('device_groups_dg_id_seq'::regclass)
dg_name | character varying(100) |
dg_parent_id | integer |
I want to query the recursive list of subgroups of a specific group.
I constructed the following recursive query which works fine:
WITH RECURSIVE r(dg_parent_id, dg_id, dg_name) AS (
SELECT dg_parent_id, dg_id, dg_name FROM device_groups WHERE dg_id=1
UNION ALL
SELECT dg.dg_parent_id, dg.dg_id, dg.dg_name
FROM r pr, device_groups dg
WHERE dg.dg_parent_id = pr.dg_id
)
SELECT dg_id, dg_name
FROM r;
I now want to turn this into a view where I can choose which group I want to drill down for using a WHERE clause. This means I want to be able to do:
SELECT * FROM device_groups_recursive WHERE dg_id = 1;
And get all the (recursive) subgroups of the group with id 1
I was able to write a function (by wrapping the query from above), but I would like to have a view instead of the function.
Side-Node: I know of the shortcoming of an adjacency list representation, I cannot change it currently.

Related

select ID and sql query (count query) from table and write ID and result of count query to target table

My source table has query id and a sql query.
Using Talend I need to run this CUSTOM_SQL query against the database and load a target table with the counts.
source table:
QUERY_ID|CUSTOM_SQL |
--------+----------------------------------------------------------------+
1|select count(1) as ROW_COUNT from SYSTEM_PRIVILEGE_MAP |
2|select count(1) as ROW_COUNT from OGIS_SPATIAL_REFERENCE_SYSTEMS|
3|select count(1) as ROW_COUNT from SDO_COORD_SYS |
4|select count(1) as ROW_COUNT from SDO_COORD_REF_SYS |
5|select count(1) as ROW_COUNT from SDO_PREFERRED_OPS_SYSTEM |
6|select count(1) as ROW_COUNT from SDO_TIN_PC_SYSDATA_TABLE |
expected output in target table:
QUERY_ID|QUERY_RESULT |
--------+-------------+
1|290 |
2|322 |
3|784 |
4|8484 |
5|743 |
I created a job that looks as follows but it is not complete:
tdbInput -> tFlowIterate -> tDBInput -> tMap -> tDBOutput
With the above design I'm able to run the CUSTOM_SQL, capture the result from tDBInput, but unable capture and propagate the QUERY_ID.
How do I propagate both query_id and the query result in one row to the target table. What components should I use?
Please note that each CUSTOM_SQLs always return one row and one column. So this is a very specific usecase.
I simplified my scenario by using some dummy data.
I will appreciate any help on this.
Thank you!
With your first tDBInput component, make sure you extract both QUERY_ID and CUSTOM_SQL (select QUERY_iD,CUSTOM_SQL from source_table) : you should get in global variables of your tFlowToIterate two variables (something like ((String)globalMap.get("row1.QUERY_ID")) , with row1 being the name of the flow between tDBInput and tFlowToIterate). You can also check in "outline" view if those variables appear under tFlowToIterate_1 component.
Then in tMap, you can just access this query_id global variable (with the above syntax) and push it to your target table.

When aliasing a function, I do not see composite types

I create table gs:
foo=>create table gs as select generate_subscripts('{{1,2,3},{4,5,6}}'::integer[],2);
I alias the table and the column:
foo=> select s.a from gs s(a);
a
---
1
2
3
(3 rows)
If I only alias the table, I see composite types:
foo=> select s from gs s;
s
-----
(1)
(2)
(3)
(3 rows)
But when I only alias a function as if it was a table, I do not see composite types, but it is as if I had aliased a table and column:
foo=> select s from generate_subscripts('{{1,2,3},{4,5,6}}'::integer[],2) s;
s
---
1
2
3
(3 rows)
I do not understand why I do not see composite types instead.
Set returning functions (or table functions, SRF) are treated differently than actual relations (tables, views, etc.). Especially, when they return only one column (a base type):
If no table_alias is specified, the function name is used as the table name; in the case of a ROWS FROM() construct, the first function's name is used.
If column aliases are not supplied, then for a function returning a base data type, the column name is also the same as the function name. For a function returning a composite type, the result columns get the names of the individual attributes of the type.
What is not covered though, is the case, when you supply a table alias, but not a column alias (for a single-columned SRF). In that case, the column alias will be the same as the table alias, so you can't access the function's row-type (composite type) explicitly (its reference is hidden by the column alias).
select s from generate_subscripts('{{1,2,3},{4,5,6}}'::integer[],2) s;
-- "s" is both a column alias and a table alias here, so:
select s.s from generate_subscripts('{{1,2,3},{4,5,6}}'::integer[],2) s;
-- is also valid
More intriguing however, is that when you use an explicit table alias and an explicit column alias for a single-columned SRF, the table alias also becomes a column alias (its type will be the base type, not a row-type -- composite type).
select s, a from generate_subscripts('{{1,2,3},{4,5,6}}'::integer[],2) s(a);
+---+---+
| s | a |
+---+---+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+---+---+
I'm not sure though if it's a bug, or just an undocumented "feature".
http://rextester.com/VJOBI47962

Calculate table column using over table on server side

Suppose, there are two tables in db:
Table registries:
Column | Type |
--------------------+-----------------------------+---------
registry_id | integer | not null
name | character varying | not null
...
uploaded_at | timestamp without time zone | not null
Table rows:
Column | Type | Modifiers
---------------+-----------------------------+-----------
row_id | character varying | not null
registry_id | integer | not null
row | character varying | not null
In real world registries is just a csv-file and rows is lines of the files. In my scala-slick application, I want to know how many lines in each file.
registries:
1,foo,...
2,bar,...
3,baz,...
rows:
aaa,1,...
bbb,1,...
ccc,2,...
desired result:
1,foo,... - 2
2,bar,... - 1
3,baz,... - 0
My code now is (slick-3.0):
def getRegistryWithLength(rId: Int) = {
val q1 = registries.filter(_.registryId===rId).take(1).result.headOption
val q2 = rows.filter(_.registryId===rId).length.result
val registry = Await.result(db.run(q1), 5.seconds)
val length = Await.result(db.run(q2), 5.seconds)
(registry, length)
}
(Await is bad idea, I know it)
How can I do getRegistryWithLength using single sql query?
I could add column row_n into table registries, but then I'll be forced to do updating column row_n after delete/insert query of rows table.
How can I do automatic calculation column row_n in table registries on db server side?
The basic query could be:
SELECT r.*, COALESCE(n.ct, 0) AS ct
FROM registry r
LEFT JOIN (
SELECT registry_id, count(*) AS ct
FROM rows
GROUP BY registry_id
) n USING (registry_id);
The LEFT [OUTER] JOIN is essential so you do not filter rows from registry without related rows in rows.
COALESCE to return 0 instead of NULL where no related rows are found.
There are many related answers on SO. One here:
SQL: How to save order in sql query?
You could wrap this in a VIEW for convenience:
CREATE VIEW reg_rn AS
SELECT ...
... which you query like a table.
Aside: It's unwise to use reserved SQL key words as identifiers. row is a no-go for a column name (even if allowed in Postgres).
Thanks Erwin Brandstetter for awesome answer, using it, I wrote code for my scala-slick application.
Scala code looks much more complicated than plain sql:
val registryQuery = registries.filter(_.userId === userId)
val rowQuery = rows groupBy(_.registryId) map { case (regId, rowItems) => (regId, rowItems.length)}
val q = registryQuery joinLeft rowQuery on (_.registryId === _._1) map {
case (registry, rowsCnt) => (registry, rowsCnt.map(_._2))
}
but it works!

Is it possible in PL/pgSQL to evaluate a string as an expression, not a statement?

I have two database tables:
# \d table_1
Table "public.table_1"
Column | Type | Modifiers
------------+---------+-----------
id | integer |
value | integer |
date_one | date |
date_two | date |
date_three | date |
# \d table_2
Table "public.table_2"
Column | Type | Modifiers
------------+---------+-----------
id | integer |
table_1_id | integer |
selector | text |
The values in table_2.selector can be one of one, two, or three, and are used to select one of the date columns in table_1.
My first implementation used a CASE:
SELECT value
FROM table_1
INNER JOIN table_2 ON table_2.table_1_id = table_1.id
WHERE CASE table_2.selector
WHEN 'one' THEN
table_1.date_one
WHEN 'two' THEN
table_1.date_two
WHEN 'three' THEN
table_1.date_three
ELSE
table_1.date_one
END BETWEEN ? AND ?
The values for selector are such that I could identify the column of interest as eval(date_#{table_2.selector}), if PL/pgSQL allows evaluation of strings as expressions.
The closest I've been able to find is EXECUTE string, which evaluates entire statements. Is there a way to evaluate expressions?
In the plpgsql function you can dynamically create any expression. This does not apply, however, in the case you described. The query must be explicitly defined before it is executed, while the choice of the field occurs while the query is executed.
Your query is the best approach. You may try to use a function, but it will not bring any benefits as the essence of the issue will remain unchanged.

Search inside full search column using certain letters

I want to search inside a full search column using certain letters, I mean:
select "Name","Country","_score" from datatable where match("Country", 'China');
Returns many rows and is ok. My question is, how can I search for example:
select "Name","Country","_score" from datatable where match("Country", 'Ch');
I want to see, China, Chile, etc.
I think that match_type phrase_prefix can be the answer, but I don't know how I can use (correct syntax).
The match predicate supports different types by use of using match_type [with (match_parameter = [value])].
So in your example using the phrase_prefix match type:
select "Name","Country","_score" from datatable where match("Country", 'Ch') using phrase_prefix;
gives you your desired results.
See the match predicate documentation: https://crate.io/docs/en/latest/sql/fulltext.html?#match-predicate
If you just need to match the beginning of a string column, you don't need a fulltext analyzed column. You can use the LIKE operator instead, e.g.:
cr> create table names_table (name string, country string);
CREATE OK (0.840 sec)
cr> insert into names_table (name, country) values ('foo', 'China'), ('bar','Chile'), ('foobar', 'Austria');
INSERT OK, 3 rows affected (0.049 sec)
cr> select * from names_table where country like 'Ch%';
+---------+------+
| country | name |
+---------+------+
| Chile | bar |
| China | foo |
+---------+------+
SELECT 2 rows in set (0.037 sec)