PostgreSQL multi-column unique

I have a strange situation in PostgreSQL 9.4.
I have a table:

id integer NOT NULL DEFAULT nextval('users_userpropmeta_id_seq'::regclass)
name character varying(255) NOT NULL
cls character varying(4) NOT NULL
app_id integer NOT NULL

A unique constraint:
UNIQUE(app_id, name)

Now I query the table:

SELECT COUNT(*), app_id, name FROM users_userpropmeta GROUP BY app_id, name HAVING COUNT(*)> 1;

Get:

count | app_id | name
---- ---+--------+-------------------------
2 | 6019 | Создание серии писем< br /> 2 | 6019 | Увеличение объемов базы
(2 rows)

So unique doesn’t work? I further said:

SELECT * FROM users_userpropmeta WHERE app_id=6019 AND name in ('Создание серии писем','Увеличение объемов базы');
id | name | cls | app_id
------+-------------------------+-----+--- -----
7308 | Создание серии писем | str | 6019
7309 | Увеличение объемов базы | str | 6019
(2 rows)

Only 2 rows. There is some magic here. Let us find the line with the hack:

SELECT MAX(id), MIN(id), COUNT(*), app_id, name FROM users_userpropmeta GROUP BY app_id, name HAVING COUNT(*)> 1;

max | min | count | app_id | name
------+------+--- ----+--------+-------------------------
7308 | 4633 | 2 | 6019 | Создание серии писем
7309 | 4636 | 2 | 6019 | Увеличение объемов базы
(2 rows)

Here is the line:

< pre>SELECT * FROM users_userpropmeta WHERE id IN (7308, 7309, 4633, 4636);
id | name | cls | app_id
——+——— —————-+—–+——–
4633 | Создание серии писем | str | 6019
4636 | Увеличение объемов базы | str | 6019
7308 | Создание серие писем | str
(4 rows)

The row-by-row comparison is correct, they are the same:

SELECT a.id, b.id, a.name, b.name, a.name = b.name FROM users_userpropmeta AS a CROSS JOIN users_userpropmeta AS b WHERE a.id IN (7308, 7309, 4633, 4636) AND b.id IN (7308, 7309, 4633, 4636);
id | id | name | name | ?column?
------+------+-------------- -----------+-------------------------+----------
4633 | 4633 | Создание серии писем | Создание серии писем | t ​​
4633 | 4636 | Создание серии писем | Увеличение объемов базы | f
4633 | 7308 | Создание серии писем | Создание серии писем | t ​​< br /> 4633 | 7309 | Sign in 6 | 4636 | Увеличение объемов базы | Увеличение объемов базы | t
4636 | 7308 | Увеличение объемов базы | Создание серии писем | f
4636 | 7309 | Увеличение объемов базы | Увеличение объемов базы | t
7308 | 4633 | Создание серии писем | Создание серии писем | t ​​
7308 | 4636 | Создание серии писем | Увеличение объемов базы | f
7308 | 7308 | Создание серии писем | Создание серии писем | t ​​
7308 | 7309 | Создание серии писем | Увеличение объемов базы | f
7309 | 4633 | Увеличение объемов базы | Создание серии писем | f
7309 | 4636 | Увеличение объемов базы | Увеличение объемов базы | t < br /> 7309 | 7308 | Увеличение объемов базы | Создание серии писем | f
7309 | 7309 | Увеличение объемов бе зы объчение объемов базы | Can explain, why the only constraint to insert two rows without exception? Why can't the IN operator find them (I thought of some unprintable symbols?), but the equality test shows that they are the same?

P. s. Initially, I have used pglogical to create a PostgreSQL 9.6 subscription to create a logical copy from postgres to another server. It failed with an error, and it cannot insert duplicate rows =).

This must be a broken index.

Try using enable_seqscan = off, and see if you still find duplicate entries.

Maybe you have to delete duplicate entries through ctid and re-index the table.

I am There is a strange situation in PostgreSQL 9.4.
I have a table:

id integer NOT NULL DEFAULT nextval('users_userpropmeta_id_seq'::regclass)
name character varying(255) NOT NULL
cls character varying(4) NOT NULL
app_id integer NOT NULL

A unique constraint:
UNIQUE(app_id, name )

Now I query the table:

SELECT COUNT(*), app_id, name FROM users_userpropmeta GROUP BY app_id, name HAVING COUNT(*)> 1 ;

Get:

count | app_id | name
-------+--------+ -------------------------
2 | 6019 | Создание серии писем
2 | 6019 | Увеличение объемов базы
(2 rows)

So unique does not work? I further said:

SELECT * FROM users_userpropmeta WHERE app_id=6019 AND name in ('Создание серии писем','Увеличение объемов базы');
id | name | cls | app_id
------+-------------------------+-----+--- -----
7308 | Создание серии писем | str | 6019
7309 | Увеличение объемов базы | str | 6019
(2 rows)

Only 2 rows. There is some magic here. Let us find the line with the hack:

SELECT MAX(id), MIN(id), COUNT(*), app_id, name FROM users_userpropmeta GROUP BY app_id, name HAVING COUNT(*)> 1;

max | min | count | app_id | name
------+------+--- ----+--------+-------------------------
7308 | 4633 | 2 | 6019 | Создание серии писем
7309 | 4636 | 2 | 6019 | Увеличение объемов базы
(2 rows)

Here is the line:

< pre>SELECT * FROM users_userpropmeta WHERE id IN (7308, 7309, 4633, 4636);
id | name | cls | app_id
------+--------- ----------------+-----+--------
4633 | Создание серии писем | str | 6019
4636 | Увеличение объемов базы | str | 6019
7308 | Создание серии писем | str | 6019
4636 | Увеличение объемов базы | str | 6019
7308 | Создание серии писем | str | >(4 rows)

The row-by-row comparison is correct, they are the same:

SELECT a.id, b.id, a.name , b.name, a.name = b.name FROM users_userpropmeta AS a CROSS JOIN users_userpropmeta AS b WHERE a.id IN (7308, 7309, 4633, 4636) AND b.id IN (7308, 7309, 4633, 4636); 
id | id | name | name | ?column?
------+------+----------------- --------+-------------------------+----------
4633 | 4633 | Создание серии писем | Создание серии писем | t ​​
4633 | 4636 | Создание серии писем | Увеличение объемов базы | f
4633 | 7308 | Создание серии писем | Создание серии писем | t ​​
4633 | 7309 | Создание серии писем | Увеличение объемов базы | f
4636 | 4633 | Увеличение объемов базем | f Увеличение объемов базы | Увеличение объемов базы | t
4636 | 7308 | Увеличение объемов базы | Создание серии писем | f
4636 | 7309 | Увеличение объемов базы | Увеличение объемов базы | t
7308 | 4633 | Создание серии писем | Создание серии писем | t ​​
7308 | 4636 | Создание серии писем | Увеличение объемов базы | f
7308 | 7308 | Создание серии писем | Создание серии писем | t ​​
7308 | 7309 | Создание серии писем | Увеличение объемов базы | f
7309 | 4633 | Увеличение объемов базы | Создание серии писем | f
7309 | 4636 | Увеличение объемов базы | Увеличение объемов базы | t
7309 | 7308 | Увеличение объемов базы | Создание серии писем | f
7309 | 7309 | Увеличение объемов базов базы

P. s. Initially, I have used pglogical to create a PostgreSQL 9.6 subscription to create a logical copy from postgres to another server. It failed with an error, it cannot insert duplicate rows =).

This must be a damaged index.

Try enable_seqscan = off and see if you still find duplicates.

Maybe you have to delete duplicate entries through ctid and re-index the table.

Leave a Comment

Your email address will not be published.