How to pulls related values in PostgreSQL? - sql

Here is my problem.
I have two tables like user and customer. Users can be related to some customer(s).
For example, I have a user who relates to two customers, and the other user relates to three customers, etc.
This is my code that creating tables and inserting values;
create table tbl_user
(
id int,
username varchar(100),
relatedcustom_id int
)
create table tbl_custom
(
id int,
name varchar(100)
)
insert into tbl_custom values(1,'john'),
(2,'adam'),
(3,'steve'),
(4,'marliyn'),
(5,'coco'),
(6,'George');
insert into tbl_user values (1,'cst_moose',1),
(1,'cst_moose',2),
(2,'cst_moose',3),
(3,'cst_kevin',2),
(4,'cst_kevin',5),
(5,'cst_donald',1),
(6,'cst_donald',2),
(7,'cst_donald',4),
(8,'cst_henry',1),
(9,'cst_henry',6),
(10,'cst_michel',1),
(11,'cst_michel',2);
I want to pull the user name(s) that relates to customers that id is 1 and 2.
Here is my select code;
select username from tbl_user where exists (select 1 from tbl_custom
where
id in(1,2))
except
select username from tbl_user where exists (select 1 from tbl_custom
where
id not in(1,2))
But this query demonstrates nothing.
The other select code ;
select username from tbl_user where relatedcustom_id in (1,2)
except
select username from tbl_user where relatedcustom_id not in (1,2)
This query shows like this;
username
--------
cst_michel
What I want to do is a Select - As - From with the following fields
username
--------
cst_michel
cst_moose
cst_donald

I want to pull the user name(s) that relates to customers that id is 1 and 2.
You can use aggregation and having:
select username
from tbl_user
group by username
having sum( (relatedcustom_id = 1)::int ) > 0 and
sum( (relatedcustom_id = 2)::int ) > 0 and
sum( (relatedcustom_id not in (1, 2)::int ) ) = 0;

One of the possible solution is to use arrays:
select username
from tbl_user
where relatedcustom_id in (1,2) -- to filter out unrelated data
group by username
having array[1,2] <# array_agg(relatedcustom_id);
-- or '{1,2}' <# array_agg(relatedcustom_id);

Related

How to merge users in PostgreSQL

I need to make something to merge some users in PGSQL but I think that pgsql don't own the MERGE property. I just want to know how to make two users to be matched like this :
id | name | username | mail
1 | toto | tata | toto.tata#gmail.com
2 | titi | tutu | titi.tutu#gmail.com
Here I want to chose which data I would like I want to say that I want to merge only username from 2 to 1 so the result would be :
id | name | username | mail
1 | toto | tutu | toto.tata#gmail.com
You just need to select all the columns for first id and the column you need with second id will be a subquery in select list. Please check below answer for selecting merged result.
Schema and insert statements:
create table users (id int , name varchar(50), username varchar(50), mail varchar(50));
insert into users values (1 , 'toto' , 'tata' , 'toto.tata#gmail.com');
insert into users values (2 , 'titi' , 'tutu' , 'titi.tutu#gmail.com');
Query:
select id,name,(select username from users where id=2) username,mail from users where id=1
Output:
id
name
username
mail
1
toto
tutu
toto.tata#gmail.com
db<fiddle here
To merge the rows within the table you can first update first row with data from second row then delete the second row. Try this:
Schema and insert statements:
create table users (id int , name varchar(50), username varchar(50), mail varchar(50));
insert into users values (1 , 'toto' , 'tata' , 'toto.tata#gmail.com');
insert into users values (2 , 'titi' , 'tutu' , 'titi.tutu#gmail.com');
Update query:
update users set username=(select username from users where id=2) where id=1;
delete from users where id=2;
Select query:
select * from users
id
name
username
mail
1
toto
tutu
toto.tata#gmail.com
db<fiddle here
You could use aggregation:
select min(id) as id,
max(name) filter (where id = 1) as name,
max(username) filter (where id = 2) as username,
max(mail) filter (where id = 1) as mail
from t
where id in (1, 2);
This assumes that you want to pull particular column values from particular ids.
Or you could use join:
select t1.id, t1.name, t2.username, t1.mail
from t t1 join
t t2
on t1.id = 1 and t2.id = 2;
If you actually want to change the data, use update and delete:
update t t1
set username = t2.username
from t t2
where t1.id = 1 and t2.id = 2;
delete from t
where t.id = 2;
Here is a db<>fiddle.

Postgresql - Insert when select return something

Is it possible to run select query, check if row exist and then insert some values? I would like to do that in one query. I think about SELECT .. CASE .. THEN, for example:
SELECT user_id, CASE when user_id > 0 then (INSERT INTO another_table ...) ELSE return 0 END
FROM users WHERE user_id = 10
Now I'm able to do that with 2 queries, first do SELECT and second INSERT values (if first query return something).
Thanks!
in general the construct is:
INSERT INTO another_table
SELECT value1,value2..etc
where exists (SELECT user_id FROM users WHERE user_id = 10)
or in this particular case:
INSERT INTO another_table
SELECT value1,value2..etc
FROM users WHERE user_id = 10
If no such user, no rows will be selected and so inserted

Find the rows that has the same column

I want to know how to do the following in SQL :
SELECT *
FROM table_A
WHERE id IN(:myValues)
AND other_colum has the same value
For example, if i've a conversation table(iduser,idconversation), I want SQL query that returns some of Ids that have the same conversation id. It should return
35;105
37;105
35;106
37;106
With 35,37 the idUsers and 105,106 the conversations they have in common.
To go further, i work with Doctrine and PostegreSQL, and the table that I want to query is generated (many to many relation) but i've difficulty to integrate sub-query.
**public function getAllCommonConversationByUserId($ids)
{
return $this->createQueryBuilder('c')
->select('c.id')
->innerJoin('c.idUser', 'recievedConversation')
->where('recievedConversation IN (:ids)')
->andWhere('$qb->expr()->eq("SELECT id FROM table GROUP BY(id) HAVING COUNT(*) >1")')
->setParameter(':ids', $ids)
->getQuery()
->getResult();
}**
Just:
SELECT *
FROM table_A
WHERE idconversation in ('105','106') and iduser in ('35','37')
UPDATE:
Are you saying if the idconversation is duplicate? (showing multiple times?)
If so:
Select *
From table
where idconversation in
(
Select idconversation
From table
group by (idconversation)
Having count(*) >1
)
--where iduser in ('35','37')
Try to do this:
select id, conversation
from [your table name]
where
conversation in (
select conversation
from [your table name]
where id in (35)
)
It return all of the participants of the conversation with user id = 35
If you have duplicates in your table, please add distinct to select statement.
You can get the conversations using group by and having:
SELECT conversationid
FROM table_A
WHERE userid in (35, 37)
GROUP BY userid
HAVING count(distinct userid) = 2;
If you want the original rows, you can join back to the original table.

insert 2 select sql result into one insert sql

i have a table with 2 fields. How can i insert those 2 fields from result of 2 sql result.
insert into access (user,page)
(select id as user from users where id =5,
select pagename as page from pages where id =10)
There is no relation between 2 tables . i dont think i can join .
insert into access ("user", page) values
( (select id as user from users where id =5),
(select pagename as page from pages where id =10)
)
insert into access (user,page)
select users.id as user,
pages.pagename as page
from users,pages
where users.id = 5
and pages.id = 10

SQL - Is there a query that will do "foreach A in table, if !B, insert B"?

I have a table with 2 columns:
nid realm
1 domain_id
1 domain_site
2 domain_id
3 domain_id
I want every entry to have 1 entry for domain id, and 1 for domain site. So I want to end up with:
nid realm
1 domain_id
1 domain_site
2 domain_id
2 domain_site
3 domain_id
3 domain_site
If I was doing this in PHP, I'd just foreach through the whole list and insert the extra line whenever it didn't exist. Unfortunately I only have PHPmyAdmin access to this DB. Is there a way to do this in straight SQL?
(If it makes a difference: The table has about 3000 rows currently, of which I think about 2000 will need the extra line inserted. Also, this is a one-time thing so it does not need to be optimized/uber-slick.)
INSERT IGNORE INTO `table`
SELECT `alt1`.`nid`, `alt2`.`realm`
FROM `table` AS `alt1`, `table` AS `alt2`
I think this will do it, but I don't have a place to test it right now and I'm used to Sql Server rather than MySQL:
INSERT INTO `table`
SELECT id.nid, r.realm
FROM (SELECT nid FROM `table` GROUP BY nid) id
CROSS JOIN (SELECT realm FROM `table` GROUP BY realm) r
LEFT JOIN `table` t ON t.nid=id.nid AND t.realm=r.realm
WHERE t.realm IS NULL
insert into MyTable
(nid, realm)
select nid, 'domain_id'
from MyTable m where not exists (
select 1
from MyTable
where MyTable.nid = m.nid and realm = 'domain_id'
)
union all
select nid, 'domain_site'
from MyTable m where not exists (
select 1
from MyTable
where MyTable.nid = m.nid and realm = 'domain_site'
)
If you have a UNIQUE constraint over (nid, realm), you could do this:
INSERT IGNORE INTO nidTable (nid, realm)
SELECT nid, 'domain_site'
FROM nidTable WHERE realm = 'domain_id';