Delete matching with already matched user in all ways in PostgreSQL - postgresql

I need to delete matching of my user which are already match. So I have a table : "MatchingUser" that look like this :
id | idUser1 | idUser2
1 1 2
2 1 3
3 1 2
4 2 1
In this example I would like to delete entry 3 and 4 because the matching is the same as the entry 1 like : if 1 match with 2 I need to delete 3 because it's the same matching and also 4 because 2 matching 1 is the same as 1 matching 2.
I already have this :
DELETE FROM "WU_MatchingUsers" WHERE "id" IN (SELECT "id" FROM (SELECT "id", ROW_NUMBER() OVER( PARTITION BY "IDWU_User1", "IDWU_User2" ORDER BY "id" DESC) AS row_num FROM "WU_MatchingUsers") t WHERE t.row_num >1);
This one already delete same matching so in our example this one already delete the entry 3 but not the 4, I would like to add something to this query to also delete the entry 4.

I would use an EXISTS condition
delete from MatchingUser mu1
where exists (select *
from MatchingUser mu2
where mu2.id < mu1.id
and least(mu2.iduser1, mu2.iduser2) = least(mu1.iduser1, mu1.iduser2)
and greatest(mu2.iduser1, mu2.iduser2) = greatest(mu1.iduser1, mu1.iduser2))
This deletes all rows where the combination of iduser1/iduser2 is the same but have a higher ID value than the existing ones. So in this case rows with ID = 3 and ID = 4.

Related

Get all elements which have both values grouped by ParentId

I have the following problem:
I have a relation table like this:
ParentId | ValueId
1 1
1 2
2 3
2 4
2 1
Then, I want to get the ParentId who have exactly the values which query say, no more, no less.
I have this query actually:
SELECT "ParentId" FROM public."ParentValueRelation"
WHERE "ValueId" = 1 AND "ValueId" = 2
GROUP BY "ParentId"
Expected to receive 1 but getting null
Answer in sequelize could be great but not necessary
There are number of ways to do this in Postgres. Like this for instance:
SELECT "ParentId" FROM public."ParentValueRelation"
WHERE "ValueId" = 1 OR "ValueId" = 2
GROUP BY "ParentId"
HAVING COUNT("ValueID")=2
If there are duplicates in the table, you need to replace the having clause with
HAVING COUNT(DISTINCT "ValueID")=2
Best regards,Bjarni

How can I join table to delete duplicates entry in PostgreSQL

I actually have this which is working :
DELETE FROM "WU_MatchingUsers" WHERE "id" IN (SELECT "id" FROM (SELECT "id", ROW_NUMBER() OVER( PARTITION BY "IDWU_User1", "IDWU_User2" ORDER BY "id" ASC) AS row_num FROM "WU_MatchingUsers") t WHERE t.row_num >1);
This delete all duplicates entry by the more recent one in "WU_MatchingUsers" but now I have another table which is : "WU_UsersSpheres" which contain Sphere id associate with user ID.
Now I would like that my query can filter / delete only Users from a specific Spheres.
So Wu_UserSpheres look like this :
id | idSpheres | IDUser
1 1 1
2 1 2
3 2 3
4 2 4
5 2 5
So the goal is to only delete duplicate of my matching where id of the users are in a specific Spheres.

how to list records that conform to a sequentially incrementing id in postgres

Is there a way to select records are sequentially incremented?
for example, for a list of records
id 0
id 1
id 3
id 4
id 5
id 8
a command like:
select id incrementally from 3
Will return values 3,4 and 5. It won't return 8 because it's not sequentially incrementing from 5.
step-by-step demo:db<>fiddle
WITH groups AS ( -- 2
SELECT
*,
id - row_number() OVER (ORDER BY id) as group_id -- 1
FROM mytable
)
SELECT
*
FROM groups
WHERE group_id = ( -- 4
SELECT group_id FROM groups WHERE id = 3 -- 3
)
row_number() window function create a consecutive row count. With this difference you are able to create groups of consecutive records (id values which are increasing by 1)
This query is put into a WITH clause because we reuse the result twice in the next step
Select the recently created group_id
Filter the table for this group.
Additionally: If you want to start your output at id = 4, for example, you need to add a AND id >= 4 filter to the WHERE clause

PostgreSQL distinct and group on different fields

With the following query I can get the list of project members added in the memberships table, union'ed with the the projects owners (who may not have an entry in the memberships table)
select sub.user, sub.project, sub.role, sub.order, sub.name from
(SELECT
memberships."user",
memberships.project,
memberships.role,
roles."order",
roles.name
FROM memberships
JOIN roles ON roles.id = memberships.role
UNION
SELECT projects.owner AS "user",
projects.id AS project,
1 AS role,
0 AS "order",
'admin'::text AS name
FROM projects
) as sub
The above query yields the following result set.
8 2 1 0 "admin"
8 1 3 2 "contributor" (added through memberships table)
6 1 1 0 "admin"
8 4 1 0 "admin"
8 1 1 0 "admin" (duplicate because user #8 is the owner of project #1)
Now I want to remove the duplicate entries by taking the contents of the row that has least order. using distinct on (sub.order) does not include all rows
select distinct on (sub.order) * from
-- the same subquery
order by sub.order
The above yields
8 2 1 0 "admin"
8 1 3 2 "contributor"
Using group by sub.user, sub.project and aggregating min(sub.order) works but the other two fields like role and name is left out
select sub.user, sub.project, min(sub.order) from
-- the same subquery
group by sub.user, sub.project
I want the role, name and order of the row that has the minimum order when grouped with user, project pair
I want the role, name and order of the row that has the minimum order when grouped with user, project pair
The distinct on must enumerate the "grouping" columns - then the order by clause must contain the same columns, followed by the column(s) to use to break the ties.
You probably want:
select distinct on (t.user, t.project) *
from (
-- the same subquery --
) t
order by t.user, t.project, t.order

Does the returning clause always execute first?

I have a many-to-many relation representing containers holding items.
I have a primary key row_id in the table.
I insert four rows: (container_id, item_id) values (1778712425160346751, 4). These rows will be identical except the aforementioned unique row_id.
I subsequently execute the following query:
delete from contains
where item_id = 4 and
container_id = '1778712425160346751' and
row_id =
(
select max(row_id) from contains
where container_id = '1778712425160346751' and
item_id = 4
)
returning
(
select count(*) from contains
where container_id = '1778712425160346751' and
item_id = 4
);
Now I expected to get 3 returned from this query, but I got a 4. Getting a 4 is the desired behavior, but it is not what was expected.
My question is: can I always expect that the returning clause executes before the delete, or is this an idiosyncrasy of certain versions or specific software?
The use of a query in returning section is allowed but not documented. For the documentation:
output_expression
An expression to be computed and returned by the DELETE command after each row is deleted. The expression can use any column names of the table named by table_name or table(s) listed in USING. Write * to return all columns.
It seems logical that the query sees the table in a state before deleting, as the statement is not completed yet.
create temp table test as
select id from generate_series(1, 4) id;
delete from test
returning id, (select count(*) from test);
id | count
----+-------
1 | 4
2 | 4
3 | 4
4 | 4
(4 rows)
The same concerns update:
create temp table test as
select id from generate_series(1, 4) id;
update test
set id = id+ 1
returning id, (select sum(id) from test);
id | sum
----+-----
2 | 10
3 | 10
4 | 10
5 | 10
(4 rows)