【问题标题】:Get columns that differ between 2 rows获取两行之间不同的列
【发布时间】:2015-02-20 13:40:44
【问题描述】:

我有一张表company,有 60 列。目标是创建一个工具来查找、比较和消除此表中的重复项。

示例:我找到 2 家可能相同的公司,但我需要知道这两行之间哪些值(列)不同才能继续。

我认为可以逐列比较 x 60,但我寻找更简单、更通用的解决方案。

类似:

SELECT * FROM company where co_id=22
SHOW DIFFERENCE
SELECT * FROM company where co_id=33

结果应该是不同的列名。

【问题讨论】:

  • 你能举个例子,比如 3 列和 3 家公司吗?
  • 总是只有 2 家公司可以比较。如果有更多的公司可能被复制,这将在下一次进行比较。我不知道,也不能举个例子。

标签: postgresql postgresql-9.1 duplicate-removal


【解决方案1】:

为此,您可以使用 JSON 函数或 hstore 扩展名(现在仅对历史感兴趣)使用行的中间键/值表示。 JSON 内置在每个相当新的 PostgreSQL 版本中,而 hstore 必须使用 CREATE EXTENSION 安装在数据库中。

演示:

CREATE TABLE table1 (id int primary key, t1 text, t2 text, t3 text);

让我们插入主键不同的两行和另一列 (t3)。

INSERT INTO table1 VALUES 
 (1,'foo','bar','baz'),
 (2,'foo','bar','biz');

json 解决方案

首先获取具有原始行号的行的键/值表示,然后我们根据原始行号对行进行配对,然后 过滤掉具有相同“值”列的那些

WITH rowcols AS (
  select rn,  key, value
  from (select row_number() over () as rn,
   row_to_json(table1.*) as r from table1) AS s
  cross join lateral json_each_text(s.r)
)
select r1.key from rowcols r1 join rowcols r2
on (r1.rn=r2.rn-1 and r1.key = r2.key)
where r1.value <> r2.value;

示例结果:

钥匙 ----- ID t3

hstore 解决方案

SELECT skeys(h1-h2) from 
  (select hstore(t.*) as h1 from table1 t where id=1) h1
 CROSS JOIN
  (select hstore(t.*) as h2 from table1 t where id=2) h2;

h1-h2 逐个键计算差异,skeys() 将结果作为集合输出。

结果:

钥匙 -------- ID t3

可以使用skeys((h1-h2)-'id'::text) 细化选择列表以始终删除id,作为主键,显然行之间总是不同。

【讨论】:

  • 谢谢!就在现场。虽然我没有让 'skeys((h1-h2)-'id')' 部分工作。
  • 对于任何想删除id的人来说,它必须是skeys((h1-h2)-'id'::text)
  • @Tao:谢谢。我合并了你的建议。还使用基于 json 的解决方案重新设计了答案,现在 hstore 已经有些过时了。
【解决方案2】:

这是一个存储过程,应该可以帮助您完成大部分工作......

虽然这应该“按原样”工作,但它没有错误检查,您应该添加它。

它获取表中的所有列,并循环遍历它们。不同之处在于不同项目的计数多于一个。 另外,输出是:

  • 差异数的计数
  • 每列存在差异的消息

返回具有差异的列的行集可能更有用。无论如何,祝你好运!

用法:

SELECT showdifference('public','company','co_id',22,33)


CREATE OR REPLACE FUNCTION showdifference(p_schema text, p_tablename text,p_idcolumn text,p_firstid integer, p_secondid integer)
  RETURNS INTEGER AS
$BODY$ 
DECLARE
    l_diffcount INTEGER;
    l_column text;
    l_dupcount integer;
    column_cursor CURSOR FOR select column_name from information_schema.columns where table_name = p_tablename and table_schema = p_schema and column_name <> p_idcolumn;
BEGIN


    -- need error checking here, to ensure the table and schema exist and the columns exist

    -- Should also check that the records ids exist.

    -- Should also check that the column type of the id field is integer


    -- Set the number of differences to zero.

    l_diffcount := 0;

    -- use a cursor to iterate over the columns found in information_schema.columns
    -- open the cursor

    OPEN column_cursor;

    LOOP
        FETCH column_cursor INTO l_column;
        EXIT WHEN NOT FOUND;

        -- build a query to see if there is a difference between the columns. If there is raise a notice
        EXECUTE 'select count(distinct  ' || quote_ident(l_column) || ' ) from ' || quote_ident(p_schema) || '.' || quote_ident(p_tablename) || ' where ' || quote_ident(p_idcolumn) || ' in ('|| p_firstid || ',' || p_secondid ||')'
        INTO l_dupcount;



        IF l_dupcount > 1 THEN
        -- increment the counter
        l_diffcount := l_diffcount +1;
        RAISE NOTICE  '% has % differences', l_column, l_dupcount ; -- for "real" you might want to return a rowset and could do something here

        END IF;


    END LOOP;




    -- close the cursor
    CLOSE column_cursor;


    RETURN l_diffcount;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE STRICT
  COST 100;

【讨论】:

  • 为什么要使用 CURSOR?它不是被禁止的,但也不是必需的:plpgsql 可以处理这个而不需要额外的游标开销。
  • @FrankHeikens 因为这是我知道的迭代列名的方式。如果您知道更好的方法,如果您分享它,我将不胜感激。
  • 使用行数据类型检查此示例:postgresql.org/docs/current/interactive/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-10-03
  • 1970-01-01
  • 2015-02-06
  • 1970-01-01
  • 2020-11-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多