简体   繁体   English

使用SQL别名查询或将列名称连接到行值?

[英]Use query for SQL alias OR join column names to row values?

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. 我正在使用PostgreSQL中的数据,该数据使用数据字典表来提供数据集中其他表的列(变量)名称的描述。 For example: 例如:

Table 1: 表格1:

a00600 | a00900
-------+-------
row 1  | row 1
row 2  | row 2

Data Dictionary (Key) columns: 数据字典(Key)列:

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)? 出于报告目的,如何编写SQL以动态执行以下操作(不指定每个列名称)?

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. 您需要使用带有过程语言功能的动态SQL Usually plpgsql and use EXECUTE with it. 通常用plpgsql和它一起使用EXECUTE

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. 关于SO已经有很多相关的答案了。 Search for combinations of terms like [plpgsql] EXECUTE RETURN QUERY [dynamic-sql] quote_ident . 搜索[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. 如果它是真实的,我至少会将生成的视图放到一个单独的模式中,以避免名称冲突和原始模式的污染。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM