Mar*_*tus 3 postgresql database-design order-by postgresql-9.1
出于测试目的,我需要以任意(但可重现)的顺序从一组数据库表中获取一些数据。这个想法是我以后可以使用文本差异工具比较两次运行。有成语吗?
例如,我显然可以做一个:
SELECT * FROM table_with_N_columns ORDER BY column_1, ... , column_N
Run Code Online (Sandbox Code Playgroud)
我只是在问是否有一种惯用的方法来实现相同的效果(为了我的目的),而不必费心在 ORDER BY 子句中列出每一列。只要可以在查询的后续运行中重现,任何排序都将执行。
按表的所有列排序可能(非常)昂贵或有时根本不可能。示例:具有json列 or 的表xml,或point无法在 中使用的许多其他数据类型之一ORDER BY,缺少必要的运算符。你得到一个例外。
如果它没有失败,它应该可以满足您的目的,但它仍然不能保证真正稳定的排序顺序。如果没有唯一键或主键,则允许完全重复的行,仍然可以任意排序。两个并发会话都可以在没有冲突的情况下更改一组重复中的“第一个”,从而导致分别更改两个不同的行。
使用每个表的主键列。唯一约束也可以,但你真的应该在每张桌子上都有一个 pk 开始。除了是正确的,并保证工作,这也是典型的多快。
查询系统目录pg_constraint并pg_attribute获取PRIMARY KEY或UNIQUE约束的列。
我将查询包装在一个方便的 SQL 函数中:
CREATE OR REPLACE FUNCTION f_order_cols(_tbl regclass)
RETURNS text AS
$func$
SELECT string_agg(_tbl::text || '.' || quote_ident(attname), ',')
FROM (
SELECT conrelid AS attrelid, conkey
FROM pg_catalog.pg_constraint
WHERE conrelid = $1
AND contype = ANY ('{p,u}'::"char"[]) -- primary or unique constraint
ORDER BY contype -- pk has priority ('p' < 'u')
, array_length(conkey, 1) -- else, fewest columns as quick tie breaker
LIMIT 1 -- ... but any 1 of them is good
) c
JOIN unnest(c.conkey) attnum ON TRUE -- unnest array, possibly mult. cols
JOIN pg_catalog.pg_attribute a USING (attrelid, attnum)
UNION ALL -- Default if first query returns no row
SELECT _tbl::text || '::text COLLATE "C"' -- See chapter "Default" below
LIMIT 1
$func$ LANGUAGE sql;
Run Code Online (Sandbox Code Playgroud)
这是非常快的。
该函数以表名为参数,regclass准确的说是type 。根据当前的 search_path 解析名称,如果无效则引发异常。同时在结果中自动提供一个正确引用的标识符。
任何PRIMARY KEY或UNIQUE约束都是好的。PK 具有优先权,否则选择具有最少列的唯一约束。
约束可以由多列组成,其属性编号存储在数组中pg_constraint.conkey。使用隐含JOIN LATERAL到unnest()这一点。
结果列是表限定的,以便为对多个表的查询做好语法准备。如果您在查询中使用表别名,则可能需要修改此可选功能。
还要正确引用列名以避免 SQL 注入。quote_ident()为你做那个。更多相关信息:
如果未找到约束,则函数返回NULL。将结果包装成COALESCE并使用整行的文本表示作为默认值:tbl::text。
这不会像原始值那样对行进行排序,但您会获得适用于每个表的一致排序顺序。因为无论如何你都要比较文本表示。
更好的是,添加COLLATE "C"忽略整理规则。更快,同样适合您的目的。
ORDER BY tbl::text COLLATE "C"
Run Code Online (Sandbox Code Playgroud)
仍然是一种可能非常低效的最后手段。如果可能,请使用 PK。我将默认值放入上面的函数中。一切都放在一起,查询:
SELECT * FROM u JOIN p USING (a) JOIN n ON n.b = p.b ORDER BY ?Run Code Online (Sandbox Code Playgroud)
这将返回完整的字符串?:
SELECT concat_ws(', ', f_order_cols('u')
, f_order_cols('p')
, f_order_cols('n')) AS order_by;
Run Code Online (Sandbox Code Playgroud)
SQL Fiddle演示了所有内容。
| 归档时间: |
|
| 查看次数: |
1399 次 |
| 最近记录: |