Getting list of table comments in PostgreSQL - sql

Postgresql allows adding comments to objects such as tables. For example I've added a comment to table "mytable" by using this SQL command:
COMMENT ON TABLE mytable IS 'This is my table.';
My question is:
If I want to use a SQL-command to get all tables along with their respective comment - how would I do this? What would be the appropriate query for this?
Thanks in advance!
Cheers!

All comments are stored in pg_description
To get the comments on a table, you need to join it to pg_class
As an alternative you can also use the function obj_description() to retrieve this information:
SELECT obj_description(oid)
FROM pg_class
WHERE relkind = 'r'
Edit
In psql you can simply use the \d+ command to show all tables including their comments. Or use the \dd command to show all comments in the system

The main problem with "show comments" is to remember the name of specific fucntions, catalog names, etc. to retrieve the comment... Or its pages on the Guide. At this answer we solve in 2 ways: by a summary of the ordinary way (the pg-way) to show comments; and by offering shortcut functions, to reduce the "remember problem".
The pg-way
The simplest, on psql, is to use \dt+ to show table comments and \d+ to show column comments. Some for function comments?
To get on SQL, and for people that remember all parameters, the pg-way is to use the obj_description() function (Guide) in conjunction with adequate reg-type:
Function: select obj_description('mySchema.myFunction'::regproc, 'pg_proc')
Table or View: ("... and most everything else that has columns or is otherwise similar to a table",guide) select obj_description('mySchema.myClass'::regclass, 'pg_class')
other generic: select obj_description('mySchema.myObject'::regName, pg_regName), where regName is 1 in 10 of datatype-oid references Guide, and pg_regName is the same replacing prefix reg by prefix pg_.
other specific: similar select obj_description('schema.myObject'::regName, catalog_name), where catalog_name is to be more specific about a (1 in 95) key-word at catalogs Guide. It can reduce some "namespace pollution". For example pg_proc for functions, pg_aggregate for aggregate functions.
to get comment for a shared database object, analog but using the function shobj_description() (same page Guide).
Column: select col_description('mySchema.myObject'::regClass, column_number), where column_number is the column's ordinal position (at the CREATE TABLE). No column-name... See col_description(table,column_name) complement bellow.
IMPORTANT: the use of same reg-type and _catalog_name_ (e. g. ::regclass and pg_class) seems redundant and sometimes obj_description('obj'::regObj) works fine, with only reg-type! ...But, as the Guide say:
it is deprecated since there is no guarantee that OIDs are unique across different system catalogs; therefore, the wrong comment might be returned.
Shortcut functions to get comments
if you are finding it difficult to remember all the type-casts and parameters, the best is to adopt a new and simplest function to retrieve comments.
CREATE FUNCTION rel_description(
p_relname text, p_schemaname text DEFAULT NULL
) RETURNS text AS $f$
SELECT obj_description((CASE
WHEN strpos($1, '.')>0 THEN $1
WHEN $2 IS NULL THEN 'public.'||$1
ELSE $2||'.'||$1
END)::regclass, 'pg_class');
$f$ LANGUAGE SQL;
-- EXAMPLES OF USE:
-- SELECT rel_description('mytable');
-- SELECT rel_description('public.mytable');
-- SELECT rel_description('otherschema.mytable');
-- SELECT rel_description('mytable', 'otherschema');
-- PS: rel_description('public.mytable', 'otherschema') is a syntax error,
-- but not generates exception: returns the same as ('public.mytable')
We need also something less ugly to show column comments. There are no kind of pg_get_serial_sequence() function to get ordinal position of a column from its name. The native col_description('mySchema.myObject'::regClass, column_number) needs a complement:
CREATE FUNCTION col_description(
p_relname text, -- table name or schema.table
p_colname text, -- table's column name
p_database text DEFAULT NULL -- NULL for current
) RETURNS text AS $f$
WITH r AS (
SELECT CASE WHEN array_length(x,1)=1 THEN array['public',x[1]] ELSE x END
FROM regexp_split_to_array(p_relname,'\.') t(x)
)
SELECT col_description(p_relname::regClass, ordinal_position)
FROM r, information_schema.columns i
WHERE i.table_catalog = CASE
WHEN $3 IS NULL THEN current_database() ELSE $3
END and i.table_schema = r.x[1]
and i.table_name = r.x[2]
and i.column_name = p_colname
$f$ LANGUAGE SQL;
-- SELECT col_description('tableName','colName');
-- SELECT col_description('schemaName.tableName','colName','databaseName);
NOTES:
As recommended by this answer: "If you want to know which queries does psql run when you do \dt+ or \d+ customers, just launche it with psql -E".
It is possible to express multiline comment, using any multiline string (with E\n or $$...$$)... But you can't apply trim() or use another dynamic aspect. Must use dynamic SQL on COMMENT clause for it.
No comments to see? PostgreSQL programmers not use COMMENT clause because it is ugly to use: there are no syntax to add comment on CREATE TABLE or on CREATE FUNCTION; and there are no good IDE to automatize it.
The modern http://postgREST.org/ interface show comments on the Web!

You can use pg_catalog.obj_description function and information_schema.tables schema view:
SELECT t.table_name, pg_catalog.obj_description(pgc.oid, 'pg_class')
FROM information_schema.tables t
INNER JOIN pg_catalog.pg_class pgc
ON t.table_name = pgc.relname
WHERE t.table_type='BASE TABLE'
AND t.table_schema='public';
FUNCTIONS-INFO-COMMENT-TABLE
INFORMATION_SCHEMA Support in MySQL, PostgreSQL

If you still have tables with mixed case names you can use the following to get a complete list of all tables in a database with their comment, as well as catalog, schema, table type, etc. (tested in PostGIS - pgAdmin 4):
select *,
obj_description((table_schema||'.'||quote_ident(table_name))::regclass)
from information_schema.tables where table_schema <> 'pg_catalog'

Related

Postgres ATOMIC stored procedure INSERT INTO . . . SELECT with one parameter and one set of rows from a table

I am trying to write a stored procedure to let a dev assign new user identities to a specified group when they don't already have one (i.e. insert a parameter and the output of a select statement into a joining table) without hand-writing every pair of foreign keys as values to do so. I know how I'd do it in T-SQL/SQL Server but I'm working with a preexisting/unfamiliar Postgres database. I would strongly prefer to keep my stored procedures as LANGUAGE SQL/BEGIN ATOMIC and this + online examples being simplified and/or using constants has made it difficult for me to get my bearings.
Apologies in advance for length, this is me trying to articulate why I do not believe this question is a duplicate based on what I've been able to find searching on my own but I may have overcorrected.
Schema (abstracted from the most identifying parts; these are not the original table names and I am not in a position to change what anything is called; I am also leaving out indexing for simplicity's sake) is like:
create table IF NOT EXISTS user_identities (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL,
[more columns not relevant to this query)
)
create table IF NOT EXISTS user_groups (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL,
name TEXT NOT NULL
)
create table IF NOT EXISTS group_identities (
user_id BIGINT REFERENCES user_identities(id) ON DELETE RESTRICT NOT NULL,
group_id BIGINT REFERENCES user_groups(id) ON DELETE RESTRICT NOT NULL
)
Expected dev behavior:
Add all predetermined identities intended to belong to a group in a single batch
Add identifying information for the new group (it is going to take a lot of convincing to bring the people involved around to using nested stored procedures for this if I ever can)
Bring the joining table up to date accordingly (what I've been asked to streamline).
If this were SQL Server I would do (error handling omitted for time and putting aside whether EXCEPT or NOT IN would be best for now, please)
create OR alter proc add_identities_to_group
#group_name varchar(50) NULL
as BEGIN
declare #use_group_id int
if #group_name is NULL
set #use_group_id = (select Top 1 id from user_groups where id not in (select group_id from group_identities) order by id asc)
ELSE set #use_group_id = (select id from user_groups where name = #group_name)
insert into group_identities (user_id, group_id)
select #use_group_id, id from user_identities
where id not in (select user_id from group_identities)
END
GO
Obviously this is not going to fly in Postgres; part of why I want to stick with atomic stored procedures is staying in "neutral" SQL, both to be closer to my comfort zone and because I don't know what other languages the database is currently set up for, but my existing education has played kind of fast and loose with differentiating what was T-SQL specific at any point.
I am aware that this is not going to run for a wide variety of reasons because I'm still trying to internalize the syntax, but the bad/conceptual draft I have written so that I have anything to stare at is:
create OR replace procedure add_identities_to_groups(
group_name text default NULL ) language SQL
BEGIN ATOMIC
declare use_group_id integer
if group_name is NULL
set use_group_id = (select Top 1 id from user_groups
where id not in (select user_id from group_identities)
order by id asc)
ELSE set use_group_id = (select id from user_groups where name = group_name) ;
insert into group_identities (group_id, user_id)
select use_group_id, id from user_identities
where id not in (select user_id from group_identities)
END ;
GO ;
Issues:
Have not found either answers for how to do this with the combination of a single variable and a column with BEGIN ATOMIC or hard confirmation that it wouldn't work (e.g. can atomic stored procedures just not accept parameters? I cannot find an answer to this on my own). (This is part of why existing answers that I can find here and elsewhere haven't been clarifying for me.)
~~Don't know how to compensate for Postgres's not differentiating variables and parameters from column names at all. (This is why examples using a hardcoded constant haven't helped, and they make up virtually all of what I can find off StackOverflow itself.)~~ Not a problem if Postgres will handle that intelligently within the atomic block but that's one of the things I hadn't been able to confirm on my own.
Google results for "vanilla" SQL unpredictably saturated with SQL Server anyway, while my lack of familiarity with Postgres is not doing me any favors but I don't know anyone personally who has more experience than I do.
because I don't know what other languages the database is currently set up for
All supported Postgres versions always include PL/pgSQL.
If you want to use procedural elements like variables or conditional statements like IF you need PL/pgSQL. So your procedure has to be defined with language plpgsql - that removes the possibility to use the ANSI standard BEGIN ATOMIC syntax.
Don't know how to compensate for Postgres's not differentiating variables and parameters from column names at all.
You don't. Most people simply using naming conventions to do that. In my environment we use p_ for parameters and l_ for "local" variables. Use whatever you prefer.
Quote from the manual
By default, PL/pgSQL will report an error if a name in an SQL statement could refer to either a variable or a table column. You can fix such a problem by renaming the variable or column, or by qualifying the ambiguous reference, or by telling PL/pgSQL which interpretation to prefer.
The simplest solution is to rename the variable or column. A common coding rule is to use a different naming convention for PL/pgSQL variables than you use for column names. For example, if you consistently name function variables v_something while none of your column names start with v_, no conflicts will occur.
As documented in the manual the body for a procedure written in PL/pgSQL (or any other language that is not SQL) must be provided as a string. This is typically done using dollar quoting to make writing the source easier.
As documented in the manual, if you want to store the result of a single row query in a variable, use select ... into from ....
As documented in the manual an IF statement needs a THEN
As documented in the manual there is no TOP clause in Postgres (or standard SQL). Use limit or the standard compliant fetch first 1 rows only instead.
To avoid a clash between names of variables and column names, most people use some kind of prefix for parameters and variables. This also helps to identify them in the code.
In Postgres it's usually faster to use NOT EXISTS instead of NOT IN.
In Postgres statements are terminated with ;. GO isn't a SQL command in SQL Server either - it's a client side thing supported by SSMS. To my knowledge, there is no SQL tool that works with Postgres that supports the GO "batch terminator" the same way SSMS does.
So a direct translation of your T-SQL code to PL/pgSQL could look like this:
create or replace procedure add_identities_to_groups(p_group_name text default NULL)
language plpgsql
as
$$ --<< start of PL/pgSQL code
declare --<< start a block for all variables
l_use_group_id integer;
begin --<< start the actual code
if p_group_name is NULL THEN --<< then required
select id
into l_use_group_id
from user_groups ug
where not exists (select * from group_identities gi where gi.id = ug.user_id)
order by ug.id asc
limit 1;
ELSE
select id
into l_use_group_id
from user_groups
where name = p_group_name;
end if;
insert into group_identities (group_id, user_id)
select l_use_group_id, id
from user_identities ui
where not exists (select * from group_identities gi where gi.user_id = ui.id);
END;
$$
;

SQL - conditionally select a column if exists

I need to select a column only if it exists in table, else it can be set to null.
Sample table below, lets say the marks col is not necessary be there, so need to be checked if it exists
Table1:
name marks
joe 10
john 11
mary 13
Query:
select
name,
marks if it exists else null as marks1 -- pseudo code
from
table1
What should go in line to select marks ?
SQL Doesn't permit that. Your result set has two options:
Static inclusion
All from table or subquery through column-expansion with * and tbl.*
Perhaps this will suit your needs, SELECT * FROM table1; You'll always get that column, if it exists.
try this
IF COL_LENGTH('your_table_name','column_name_you_want_to_select') IS NULL BEGIN
--This means columns does not exist or permission is denied
END
else
--Do whatever you want
It is possible to achieve this in PostgreSQL using JSON. Consider the following SQL query:
SELECT c.relname, c.relkind, c.relispartition
FROM pg_class c
WHERE c.relkind IN ('r','p') AND
c.relnamespace=(SELECT oid FROM pg_namespace WHERE nspname='public')
In PostgreSQL 10+, that will show you the names of all the tables in public schema, including whether they are partitioned and if so whether the table is the partitioned table or one of the partitions of it. However, if you try to run the same query on PostgreSQL 9.6 or earlier, it will fail since the relispartition column does not exist on the pg_class table prior to PostgreSQL 10.
An obvious solution would be to dynamically generate the SQL based on a condition, or have two different versions of the SQL. However, suppose you don't want to do that, you want to have a single query which works on both versions – in other words, you want to conditionally select the relispartition column if it exists.
The core SQL language does not have any facility to conditionally select a column, but it is achievable in PostgreSQL using the row_to_json function, as follows:
SELECT c.relname, c.relkind,
(row_to_json(c)->>'relispartition')::boolean AS relispartition
FROM pg_class c
WHERE c.relkind IN ('r','p') AND
c.relnamespace=(SELECT oid FROM pg_namespace WHERE nspname='public')
If you try running that, you will find on PostgreSQL 10+ the relispartition column is returned as true/false, whereas in pre-10 versions it is NULL. You could make it return false instead of NULL in pre-10 versions by doing COALESCE((row_to_json(c)->>'relispartition')::boolean,false).
What this is doing, is row_to_json(c) turns all the data of the row into JSON. Next, ->>'relispartition' selects the value of the relispartition JSON object key as text, which will be the same as the value of the relispartition column; if there is no such key in the JSON, the result of that will be NULL. Then, ::boolean converts the string value true/false back into a PostgreSQL boolean value. (If your column is of some other type, use the appropriate cast for the type of your column.)
(Obviously this approach will not work in Postgres versions which are too old to have the necessary JSON support – I have tested it works in Postgres 9.4; while I haven't tested it in Postgres 9.3, it probably works there. However, I would not expect it to work in 9.2 or earlier – the ->> operator was added in 9.3, and the JSON type and row_to_json function was added in 9.2. However, I expect few people will need to support those old unsupported versions–9.3 was released in 2013, and 9.2 supported ended in 2017.)
Try this:
IF EXISTS( SELECT 1
FROM information_schema.columns
WHERE table_name='your_table' and column_name='your_column') THEN
SELECT your_column as 'some_column'
ELSE
SELECT NULL as 'some_column'
END IF
Replying to an old question yet again but here's my hacky solution to this problem since I don't know how to write SQL functions... yet! %I formats the string as an identifier, and if there is no such table the return value is NULL and the alias is used!
SELECT (SELECT format('%I', 'my_column')
AS my_column_alias
FROM information_schema.columns
WHERE table_name='my_table'
AND column_name='my_column')
FROM source_table
Hope this helps everybody out there =)

Oracle Table Usage Across Stored Procedures

I have a 11G database. I need to examine a number of stored procedures to see if they use a particular table. (Both front end and back end sps) I have full access to the database, and I also have a copy of all the individual sps for the project which are stored on a TFS.
I would like a way to generate a list of all the sps that interact with this particular table. I'm unfamiliar with how to go about searching for these. Can anyone advise the most logical way of obtaining this data?
Thanks.
If I understand this correctly, you're trying to search for occurrence of a table in all stored procs. In that case, you can use this query:
When searching for occurrences of SP in your schema
SELECT * FROM user_source WHERE text LIKE '%tab_name%';
When searching for occurrences of SP in all schemas
SELECT * FROM all_source WHERE text LIKE '%tab_name%';
Two things, in PL/SQL there are some changes which will require the recompilation of pl/sql object, other don't.
To see the first one, you have the ALL_DEPENDENCIES view. Or DBA_ if you prefer.
If you just want to see where the table name appears in all the pl/sql code, whether a change to the table will require recompilation or not, you can use ALL_SOURCE using a upper and %, but it might take some time.
I use PLSQL Developer, in which you can browse to a table (or other object), and view 'Referenced by', to see all objects that refer to the table. That's about as easy as it gets.
I can imagine other tools have similar features.
I don't know if this pre-parsed information is readily available in Oracle, but I can imagine so, since those tools seem to work pretty fast.
This information is available in the viewAll_DEPENDENCIES, which these tools probably use.
The source of stored procedures can be found in the USER_SOURCE (or ALL_SOURCE) view, in which the structure of the entire database is stored. Nevertheless, fetching and parsing the code from there would be quite cumbersome.
Here is snippet I wrote to perform impact analysis (ONLY MERGE, INSERT and UPDATE) for a given #schema (upper case only) and #table (upper case only). It will return all the procedure name, procedure code, from line number and to line number along with other details. It can be easily used to include functions objects as well instead of package. am working on a utility that can run across all schema or selected schema (that will include SELECT rows as well). Though this will be good enough for you to start working.
I know you can use the dependency and references available in Oracle to perform similarly. But for package level impact this is good addition. We can also use regex for more complex searches. But like operator is simple and efficient for my needs.
Note, this doesn't work on any dynamic code that may be working in your environment. This is just a appropriate starting point for quick one on impact with static PL/SQL code in your packages.
WITH TableDep as
-- This table returns references where the table is used within the code for UPDATE OR INSERT
(
SELECT
owner as schemaname,
name as packagename,
type as typename,
TEXT as refcodeline,
CASE WHEN upper(text) LIKE '%INSERT%' THEN 'INSERT'
WHEN upper(text) LIKE '%UPDATE%' THEN 'UPDATE'
WHEN upper(text) LIKE '%MERGE%' THEN 'MERGE'
END AS opr,
:Tablename AS Tablename,
line refline
FROM dba_source WHERE upper(owner) = upper(:OWNER)
AND type = 'PACKAGE BODY'
AND (
upper(text) LIKE ('%INSERT INTO '||:Tablename||'%')
OR
upper(text) LIKE ('%UPDATE%'||:Tablename||' %')
OR
upper(text) LIKE ('%MERGE%'||:Tablename||' %')
)
),
ProcedureDetails as
-- This code build all procedures within the package for references that is found in above query
(
SELECT
owner as schemaname,
name as packagename,
type as typename,
TEXT,
trim(REGEXP_SUBSTR(TEXT, '(PROCEDURE [[:print:]]+)\(',1,1,null,1)) as procedure_name,
line startline,
LEAD(line, 1) OVER (partition by name order by line)-1 as endline
FROM dba_source
WHERE owner = upper(:OWNER)
AND type = 'PACKAGE BODY'
AND upper(text) LIKE '%PROCEDURE%(%'
and exists (SELECt 1 FROM TableDep WHERE TableDep.packagename=name)
)
,ProcCode as
-- This code builds procedures into one cell per program for a given package. Later to find the effected procedures
(
SELECT
ProcTag.packagename ,
ProcTag.schemaname,
ProcTag.typename,
ProcTag.PROCEDURE_NAME,
ProcTag.startline,
ProcTag.endline,
TO_CLOB(rtrim(xmlagg(xmlelement(e,codeline.text).extract('//text()') order by line).GetClobVal(),',')) as Procedure_Code
FROM
ProcedureDetails ProcTag
INNER JOIN dba_source codeline ON ProcTag.packagename=codeline.name
AND ProcTag.schemaname=codeline.owner
and ProcTag.typename=codeline.type
and codeline.line between ProcTag.startline and ProcTag.endline
--WHERE PROCEDURE_NAME='PROCEDURE TRANS_KAT_INSO'
group by
ProcTag.packagename ,
ProcTag.schemaname,
ProcTag.typename,
ProcTag.PROCEDURE_NAME,
ProcTag.startline,
ProcTag.endline
)
-- extract all the reference code for the given table selected with it complete procedure code.
SELECT
ProcHeader.Packagename, ProcHeader.schemaname, ProcHeader.typename, ProcHeader.procedure_name, ProcHeader.Procedure_Code ,ProcHeader.startline,ProcHeader.endline,ProcReference.Tablename, ProcReference.opr
FROM
ProcCode ProcHeader
INNER JOIN
(
SELECT DISTINCT ProcCode.Packagename, ProcCode.schemaname, ProcCode.typename, ProcCode.procedure_name , TableDep.Tablename, TableDep.opr
FROM ProcCode
INNER JOIN TableDep ON ProcCode.packagename=TableDep.packagename
AND ProcCode.schemaname=TableDep.schemaname
and ProcCode.typename=TableDep.typename
and TableDep.refline between ProcCode.startline and ProcCode.endline
) ProcReference
ON ProcHeader.Packagename=ProcReference.Packagename
AND ProcHeader.schemaname=ProcReference.schemaname
AND ProcHeader.typename=ProcReference.typename
AND ProcHeader.procedure_name=ProcReference.procedure_name
;
This question already have an accepted answer but anyhow the query used inside the accepted answer will pick all the user sources which uses the particular table.
Because the question is specific about Procedures you can go for the below query to get the result
SELECT * FROM user_source WHERE text LIKE '%YourTableName%' and TYPE='PROCEDURE';

Selecting chained records from a table [duplicate]

I have a table similar to this:
CREATE TABLE example (
id integer primary key,
name char(200),
parentid integer,
value integer);
I can use the parentid field to arrange data into a tree structure.
Now here's the bit I can't work out. Given a parentid, is it possible to write an SQL statement to add up all the value fields under that parentid and recurse down the branch of the tree ?
UPDATE: I'm using posgreSQL so the fancy MS-SQL features are not available to me. In any case, I'd like this to be treated as a generic SQL question.
BTW, I'm very impressed to have 6 answers within 15 minutes of asking the question! Go stack overflow!
Here is an example script using common table expression:
with recursive sumthis(id, val) as (
select id, value
from example
where id = :selectedid
union all
select C.id, C.value
from sumthis P
inner join example C on P.id = C.parentid
)
select sum(val) from sumthis
The script above creates a 'virtual' table called sumthis that has columns id and val. It is defined as the result of two selects merged with union all.
First select gets the root (where id = :selectedid).
Second select follows the children of the previous results iteratively until there is nothing to return.
The end result can then be processed like a normal table. In this case the val column is summed.
Since version 8.4, PostgreSQL has recursive query support for common table expressions using the SQL standard WITH syntax.
If you want a portable solution that will work on any ANSI SQL-92 RDBMS, you will need to add a new column to your table.
Joe Celko is the original author of the Nested Sets approach to storing hierarchies in SQL. You can Google "nested sets" hierarchy to understand more about the background.
Or you can just rename parentid to leftid and add a rightid.
Here is my attempt to summarize Nested Sets, which will fall woefully short because I'm no Joe Celko: SQL is a set-based language, and the adjacency model (storing parent ID) is NOT a set-based representation of a hierarchy. Therefore there is no pure set-based method to query an adjacency schema.
However, most of the major platforms have introduced extensions in recent years to deal with this precise problem. So if someone replies with a Postgres-specific solution, use that by all means.
There are a few ways to do what you need in PostgreSQL.
If you can install modules, look at the tablefunc contrib. It has a connectby() function that handles traversing trees. http://www.postgresql.org/docs/8.3/interactive/tablefunc.html
Also check out the ltree contrib, which you could adapt your table to use: http://www.postgresql.org/docs/8.3/interactive/ltree.html
Or you can traverse the tree yourself with a PL/PGSQL function.
Something like this:
create or replace function example_subtree (integer)
returns setof example as
'declare results record;
child record;
begin
select into results * from example where parent_id = $1;
if found then
return next results;
for child in select id from example
where parent_id = $1
loop
for temp in select * from example_subtree(child.id)
loop
return next temp;
end loop;
end loop;
end if;
return null;
end;' language 'plpgsql';
select sum(value) as value_sum
from example_subtree(1234);
A standard way to make a recursive query in SQL are recursive CTE. PostgreSQL supports them since 8.4.
In earlier versions, you can write a recursive set-returning function:
CREATE FUNCTION fn_hierarchy (parent INT)
RETURNS SETOF example
AS
$$
SELECT example
FROM example
WHERE id = $1
UNION ALL
SELECT fn_hierarchy(id)
FROM example
WHERE parentid = $1
$$
LANGUAGE 'sql';
SELECT *
FROM fn_hierarchy(1)
See this article:
Hierarchical queries in PostgreSQL
If your using SQL Server 2005, there is a really cool way to do this using Common Table Expressions.
It takes all of the gruntwork out of creating a temporary table, and basicly allows you to do it all with just a WITH and a UNION.
Here is a good tutorial:
http://searchwindevelopment.techtarget.com/tip/0,289483,sid8_gci1278207,00.html
use a common table expression.
May want to indicate this is SQL Server 2005 or above only. Dale Ragan
here's an article on recursion by SqlTeam without common table expressions.
The following code compiles and it's tested OK.
create or replace function subtree (bigint)
returns setof example as $$
declare
results record;
entry record;
recs record;
begin
select into results * from example where parent = $1;
if found then
for entry in select child from example where parent = $1 and child parent loop
for recs in select * from subtree(entry.child) loop
return next recs;
end loop;
end loop;
end if;
return next results;
end;
$$ language 'plpgsql';
The condition "child <> parent" is needed in my case because nodes point to themselves.
Have fun :)
Oracle has "START WITH" and "CONNECT BY"
select
lpad(' ',2*(level-1)) || to_char(child) s
from
test_connect_by
start with parent is null
connect by prior child = parent;
http://www.adp-gmbh.ch/ora/sql/connect_by.html
Just as a brief aside although the question has been answered very well, it should be noted that if we treat this as a:
generic SQL question
then the SQL implementation is fairly straight-forward, as SQL'99 allows linear recursion in the specification (although I believe no RDBMSs implement the standard fully) through the WITH RECURSIVE statement. So from a theoretical perspective we can do this right now.
None of the examples worked OK for me so I've fixed it like this:
declare
results record;
entry record;
recs record;
begin
for results in select * from project where pid = $1 loop
return next results;
for recs in select * from project_subtree(results.id) loop
return next recs;
end loop;
end loop;
return;
end;
is this SQL Server? Couldn't you write a TSQL stored procedure that loops through and unions the results together?
I am also interested if there is a SQL-only way of doing this though. From the bits I remember from my geographic databases class, there should be.
I think it is easier in SQL 2008 with HierarchyID
If you need to store arbitrary graphs, not just hierarchies, you could push Postgres to the side and try a graph database such as AllegroGraph:
Everything in the graph database is stored as a triple (source node, edge, target node) and it gives you first class support for manipulating the graph structure and querying it using a SQL like language.
It doesn't integrate well with something like Hibernate or Django ORM but if you are serious about graph structures (not just hierarchies like the Nested Set model gives you) check it out.
I also believe Oracle has finally added a support for real Graphs in their latest products, but I'm amazed it's taken so long, lots of problems could benefit from this model.

SQL/JDBC : select query on variable tablenames

I'm using Oracle DB and I would like to write a SQL query that I could then call with JDBC. I'm not very familiar with SQL so if someone can help me, that could be great ! Here is the problem. I have a table MY_TABLE wich contains a list of another tables, and I would like to keep only the nonempty tables and those that their names start by a particular string.
The query I wrote is the following :
select TABLE_NAME
from MY_TABLE
where TABLE_NAME like '%myString%'
and (select count(*) from TABLE_NAME where rownum=1)<>0
order by TABLE_NAME;`
The problem comes from the second SELECT, but I don't know how can I do to use the TABLE_NAME value.
Does someone have an idea ?
Thanks.
[Added from comments]
Actually, I need to test the V$ views contained in the ALL_CATALOG table. But if I can find another table where all these views are contained too and with a NUM_ROWS column too, it would be perfect !
Standard versions of SQL do not allow you to replace 'structural elements' of the query, such as table name or column name, with variable values or place-holders.
There are a few ways to approach this.
Generate a separate SQL statement for each table name listed in MY_TABLE, and execute each in turn. Brute force, but effective.
Interrogate the system catalog directly.
Investigate whether there are JDBC metadata operations that allow you to find out about the number of rows in a table without being tied to the system catalog of the specific DBMS you are using.
Can you use oracle view USER_TABLES? then query will be much easier
select TABLE_NAME
from USER_TABLES
where TABLE_NAME like '%myString%'
and Num_ROWS > 0
order by TABLE_NAME;`