如何根据条件从每一列中获取唯一值?

问题描述 投票:0回答:3

我一直在尝试找到一个最佳解决方案来从每列中选择唯一值。我的问题是我事先不知道列名,因为不同的表有不同的列数。所以首先,我必须找到列名称,我可以使用下面的查询来做到这一点:

select column_name from information_schema.columns
where table_name='m0301010000_ds' and column_name like 'c%' 

列名称的示例输出:

c1, c2a, c2b, c2c, c2d, c2e, c2f, c2g, c2h, c2i, c2j, c2k, ...

然后我将使用返回的列名称来获取每列中的唯一/不同值,而不仅仅是不同行

我知道一个最简单且糟糕的方法是为每个列编写 select distect column_name from table where column_name = 'something'(大约 20-50 次),而且也非常耗时。由于我不能在每个column_name中使用多个不同的值,所以我坚持使用这种老式的解决方案。

我确信会有一种更快、更优雅的方式来实现这一目标,但我只是不知道如何实现。我将非常感谢对此的任何帮助。

sql postgresql postgresql-9.1 postgresql-performance
3个回答
4
投票

您不能只返回行,因为不同的值不再组合在一起。

可以返回数组,它可以比你想象的更简单:

SELECT array_agg(DISTINCT c1)  AS c1_arr
      ,array_agg(DISTINCT c2a) AS c2a_arr
      ,array_agg(DISTINCT c2b) AS c2ba_arr
      , ...
FROM   m0301010000_ds;

这将返回每列不同的值。每列一个数组(可能很大)。列中值之间的所有连接(以前位于同一行中)都将在输出中丢失。

自动构建SQL

CREATE OR REPLACE FUNCTION f_build_sql_for_dist_vals(_tbl regclass)
  RETURNS text AS
$func$
SELECT 'SELECT ' || string_agg(format('array_agg(DISTINCT %1$I) AS %1$I_arr'
                                     , attname)
                              , E'\n      ,' ORDER  BY attnum)
        || E'\nFROM   ' || _tbl
FROM   pg_attribute
WHERE  attrelid = _tbl            -- valid, visible table name 
AND    attnum >= 1                -- exclude tableoid & friends
AND    NOT attisdropped           -- exclude dropped columns
$func$  LANGUAGE sql;

致电:

SELECT f_build_sql_for_dist_vals('public.m0301010000_ds');

返回如上所示的 SQL 字符串。

我使用系统目录

pg_attribute
而不是信息模式。表名称的对象标识符类型为
regclass
。此相关答案中有更多解释:
PLpgSQL 函数查找给定表中仅包含 NULL 值的列


3
投票

如果您“实时”需要此数据,您将无法使用需要执行全表扫描来归档它的 SQL 来归档它。

我建议您创建一个单独的表,其中包含每列的不同值(使用 @Erwin Brandstetter 的 SQL 初始化;)并使用原始表上的触发器来维护它。

您的新表的每个字段将有一列。行数将等于一个字段的不同值的最大数量。

插入时:对于每个字段要维护检查该值是否已经存在。如果没有,请添加。

对于更新:对于每个要维护的具有旧值!=新值的字段,检查新值是否已经存在。如果没有,请添加。对于旧值,检查是否有任何其他行具有该值,如果没有,则将其从列表中删除(将字段设置为空)。

对于删除:对于要维护的每个字段,检查是否有任何其他行具有该值,如果没有,则将其从列表中删除(将值设置为空)。

这样负载主要转移到触发器上,值列表表上的SQL会超级快。

P.S.:确保将所有 SQL 从触发器传递到解释计划,以确保它们尽可能使用最佳索引和执行计划。对于更新/删除,只需检查旧值是否存在(限制 1)。


0
投票

DECLARE @TableName NVARCHAR(MAX) = 'table_name'; -- 在此指定您的表名称 声明 @SQLQuery NVARCHAR(MAX) = '';

选择@SQLQuery = @SQLQuery + 'SELECT ''' + COLUMN_NAME + ''' AS Column_Name, COUNT(DISTINCT ' + QUOTENAME(COLUMN_NAME) + ') AS Unique_Values FROM ' + QUOTENAME(@TableName) + ' UNION ALL ' 来自 INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @TableName;

-- 从查询中删除最后一个“UNION ALL” SET @SQLQuery = LEFT(@SQLQuery, LEN(@SQLQuery) - LEN(' UNION ALL '));

-- 追加 order by 子句 SET @SQLQuery = @SQLQuery + ' ORDER BY Unique_Values DESC;';

-- 执行动态查询 EXEC sp_executesql @SQLQuery;

从表名中选择计数(*)作为Total_value

© www.soinside.com 2019 - 2024. All rights reserved.