I'm working with data in PostgreSQL that uses a data dictionary table to provide descriptions for the column (variable) names of other tables in the dataset. For example:
Table 1:
a00600 | a00900
-------+-------
row 1 | row 1
row 2 | row 2
Data Dictionary (Key) columns:
Variable | Description
---------+------------
a00600 | Total population
a00900 | Zipcode
For reporting purposes, how do I write SQL to perform the following dynamically (without specifying each column name)?
SELECT 'a00600' AS (SELECT Key.Description
WHERE Key.Variable = 'a00600')
FROM Table 1;
I realize there's likely a better way to parse this question/problem and am open to any ideas for what I need to accomplish.
You need to use dynamic SQL with a procedural language function. Usually plpgsql
and use EXECUTE
with it.
The tricky part is to define the return type at creation time.
I have compiled a number of solutions in this related answer .
There are lots of related answer on SO already. Search for combinations of terms like [plpgsql]
EXECUTE
RETURN QUERY
[dynamic-sql]
quote_ident
.
Your approach is commonly frowned upon among database designers.
My personal opinion: I wouldn't go that route. I always use basic, descriptive names. You can always add more décor in your application if needed.
Another way to get the descriptions instead of the actual column names would be to create views (one for every table). This can be automated by generating the views automatically. This looks rather clumsy, but it has the huge advantage that for "complex* queries the resulting queryplans will be axactly the same as for the original columns names. (functions joined into complex queries will perform badly: the optimiser cannot take them apart, so the resulting behavior will be equivalent to "row at a time") Example:
-- tmp schema is only for testing
DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;
CREATE TABLE thedata
( a00600 varchar
, a00900 varchar
);
INSERT INTO thedata(a00600 , a00900) VALUES
('key1', 'data1')
,('key2', 'data2');
CREATE TABLE thedict
( variable varchar
, description varchar
);
INSERT INTO thedict(variable , description) VALUES
('a00600' , 'Total population')
,('a00900' , 'Zipcode' );
CREATE OR REPLACE FUNCTION create_view_definition(zname varchar)
RETURNS varchar AS
$BODY$
DECLARE
thestring varchar;
therecord RECORD;
iter INTEGER ;
thecurs cursor for
SELECT co.attname AS zname, d.description AS zdesc
FROM pg_class ct
JOIN pg_namespace cs ON cs.oid=ct.relnamespace
JOIN pg_attribute co ON co.attrelid = ct.oid AND co.attnum > 0
LEFT JOIN thedict d ON d.variable = co.attname
WHERE ct.relname = 'thedata'
AND cs.nspname = 'tmp'
;
BEGIN
thestring = '' ;
iter = 0;
FOR therecord IN thecurs LOOP
IF (iter = 0) THEN
thestring = 'CREATE VIEW ' || quote_ident('v'||zname) || ' AS ( SELECT ' ;
ELSE
thestring = thestring || ', ';
END IF;
iter=iter+1;
thestring = thestring || quote_ident(therecord.zname);
IF (therecord.zdesc IS NOT NULL) THEN
thestring = thestring || ' AS ' || quote_ident(therecord.zdesc);
END IF;
END LOOP;
IF (iter > 0) THEN
thestring = thestring || ' FROM ' || quote_ident(zname) || ' )' ;
END IF;
RETURN thestring;
END;
$BODY$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION execute_view_definition(zname varchar)
RETURNS INTEGER AS
$BODY$
DECLARE
meat varchar;
BEGIN
meat = create_view_definition(zname);
EXECUTE meat;
RETURN 0;
END;
$BODY$ LANGUAGE plpgsql;
SELECT create_view_definition('thedata');
SELECT execute_view_definition('thedata');
SELECT * FROM vthedata;
RESULT:
CREATE FUNCTION
CREATE FUNCTION
create_view_definition
---------------------------------------------------------------------------------------------------
CREATE VIEW vthedata AS ( SELECT a00600 AS "Total population", a00900 AS "Zipcode" FROM thedata )
(1 row)
execute_view_definition
-------------------------
0
(1 row)
Total population | Zipcode
------------------+---------
key1 | data1
key2 | data2
(2 rows)
Please note this is only an example. If it were for real, I would at least put the generated views into a separate schema, to avoid name collisions and pollution of the original schema.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.