How to merge users in PostgreSQL - sql

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.

Related

Select multiple row where one of their columns is the same

i wanna select a row where in another row with same userid is Active . like :
mytable
userid
userfield
fieldstatus
1
Name
Tom
1
account
Active
2
name
Jerry
2
account
Failed
I want to select tom in name user field where its account is active . I'm writing in python
You can use a subquery with exists:
select t.fieldstatus from mytable t where t.userfield = "name" and exists (select 1 from mytable t1 where t1.userid = t.userid and t1.userfield = "account" and t1.fieldstatus = "Active")
Output
fieldstatus
Tom
You can check the same userid value using exists
select *
from mytable t
where exists (
select * from mytable t2
where t2.userid=t.userid
and t2.userfield='account'
and t2.fieldstatus='Active'
)

SQL Select Where Opposite Match Does Not Exist

Trying to compare between two columns and check if there are no records that exist with the reversal between those two columns. Other Words looking for instances where 1-> 3 exists but 3->1 does not exist. If 1->2 and 2->1 exists we will still consider 1 to be part of the results.
Table = Betweens
start_id | end_id
1 | 2
2 | 1
1 | 3
1 would be added since it is a start to an end with no opposite present of 3,1. Though it did not get added until the 3rd entry since 1 and 2 had an opposite.
So, eventually it will just return names where the reversal does not exist.
I then want to join another table where the number from the previous problem has its name installed on it.
Table = Names
id | name
1 | Mars
2 | Earth
3 | Jupiter
So results will just be the names of those that don't have an opposite.
You can use a not exists condition:
select t1.start_id, t1.end_id
from the_table t1
where not exists (select *
from the_table t2
where t2.end_id = t1.start_id
and t2.start_id = t1.end_id);
I'm not sure about your data volume, so with your ask, below query will supply desired result for you in Sql Server.
create table TableBetweens
(start_id INT,
end_id INT
)
INSERT INTO TableBetweens VALUES(1,2)
INSERT INTO TableBetweens VALUES(2,1)
INSERT INTO TableBetweens VALUES(1,3)
create table TableNames
(id INT,
NAME VARCHAR(50)
)
INSERT INTO TableNames VALUES(1,'Mars')
INSERT INTO TableNames VALUES(2,'Earth')
INSERT INTO TableNames VALUES(3,'Jupiter')
SELECT *
FROM TableNames c
WHERE c.id IN (
SELECT nameid1.nameid
FROM (SELECT a.start_id, a.end_id
FROM TableBetweens a
LEFT JOIN TableBetweens b
ON CONCAT(a.start_id,a.end_id) = CONCAT(b.end_id,b.start_id)
WHERE b.end_id IS NULL
AND b.start_id IS NULL) filterData
UNPIVOT
(
nameid
FOR id IN (filterData.start_id,filterData.end_id)
) AS nameid1
)

Oracle -- Update the exact column referenced in the ON clause

I think this requirement is rarely encountered so I couldn't search for similar questions.
I have a table that needs to update the ID. For example ID 123 in table1 is actually supposed to be 456. I have a separate reference table built that stores the mapping (e.g. old 123 maps to new id 456).
I used the below query but apparently it returned error 38104, columns referenced in the ON clause cannot be updated.
MERGE INTO table1
USING ref_table ON (table1.ID = ref_table.ID_Old)
WHEN MATCHED THEN UPDATE SET table.ID = ref_table.ID_New;
Is there other way to achieve my purpose?
Thanks and much appreciated for your answer!
Use the ROWID pseudocolumn:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE TABLE1( ID ) AS
SELECT 1 FROM DUAL UNION ALL
SELECT 2 FROM DUAL UNION ALL
SELECT 3 FROM DUAL;
CREATE TABLE REF_TABLE( ID_OLD, ID_NEW ) AS
SELECT 1, 4 FROM DUAL UNION ALL
SELECT 2, 5 FROM DUAL;
MERGE INTO TABLE1 dst
USING ( SELECT t.ROWID AS rid,
r.id_new
FROM TABLE1 t
INNER JOIN REF_TABLE r
ON ( t.id = r.id_old ) ) src
ON ( dst.ROWID = src.RID )
WHEN MATCHED THEN
UPDATE SET id = src.id_new;
Query 1:
SELECT * FROM table1
Results:
| ID |
|----|
| 4 |
| 5 |
| 3 |
You can't update a column used in the ON clause in a MERGE. But if you don't need to make other changes that MERGE allows like WHEN NOT MATCHED or deleting, etc. you can just use a UPDATE to achieve this.
You mentioned this is an ID that needs an update. Here's an example using a scalar subquery. As it is an ID, this presumes UNIQUE ID_OLD values in REF_TABLE. I wasn't sure if Every row needs an update or only a sub-set, so set the update here to only update rows that have a value in REF_TABLE.
CREATE TABLE TABLE1(
ID NUMBER
);
CREATE TABLE REF_TABLE(
ID_OLD NUMBER,
ID_NEW NUMBER
);
INSERT INTO TABLE1 VALUES (1);
INSERT INTO TABLE1 VALUES (2);
INSERT INTO TABLE1 VALUES (100);
INSERT INTO REF_TABLE VALUES (1,10);
INSERT INTO REF_TABLE VALUES (2,20);
Initial State:
SELECT * FROM TABLE1;
ID
1
2
100
Then make the UPDATE
UPDATE TABLE1
SET TABLE1.ID = (SELECT REF_TABLE.ID_NEW
FROM REF_TABLE
WHERE REF_TABLE.ID_OLD = ID)
WHERE TABLE1.ID IN (SELECT REF_TABLE.ID_OLD
FROM REF_TABLE);
2 rows updated.
And check the change:
SELECT * FROM TABLE1;
ID
10
20
100

SELECT DISTINCT Users in Table 1 which don't exist in Table 2

I have a table with 4 columns of user information. Each table has the following columns:
Username | Full_Name | Job_Name | Current_Job_Allowed
Table 1 includes all users and the Job_Name which they have permissions to view. This means that there are multiple lines of the same username in Table 1 with different Job_Name values.
Table 2 contains a list of all possible users.
Username |Full_Name
--------------+-----------------
amunoz |Andrew Munoz
csmith |Carl Smith
cwatkins |Cat Watkins
ggriffiths |Garmin Griffiths
jcarr |Jason Carr
jhothi |Jark Hothi
jphillips |Jim Phillips
lbradfield |Lisa Bradfield
ntaylor |Noria Taylor
rfelipe |Ralf Felipe
Query 1 contains all users specified by a query parameter which I specify, i.e. 'KML_20160531'.
I would like to now select a DISTINCT list of all users which have a different Job_Name from the parameter I specify for Job_Name. For example Table 1 contains:
Username|Full_Name |Job_Name |Current_Job_Allowed
--------+------------+------------+----------------------
amunoz |Andrew Munoz|KML_20160531|1
jcarr |Jason Carr |KML_20160531|1
rfelipe |Ralf Felipe |KML_20140531|1
amunoz |Andrew Munoz|KML_20160431|1
I would then like to return the below when I enter 20160531 for Job_Name. This will return all possible new users for the Job_Name value I entered.
Username |Full_Name
--------------+---------------
csmith |Carl Smith
cwatkins |Cat Watkins
ggriffiths |Garmin Griffiths
jhothi |Jark Hothi
jphillips |Jim Phillips
lbradfield |Lisa Bradfield
ntaylor |Noria Taylor
rfelipe |Ralf Felipe
This parameter query will show you which users have a Table 1 row with Job_Name matching the parameter value:
PARAMETERS which_job Text ( 255 );
SELECT t1.[Username], t1.Job_Name
FROM [Table 1] AS t1
WHERE t1.Job_Name=[which_job];
So you can use that as a subquery, left join Table 2 to the subquery, and select the rows where the "right side" is Null:
PARAMETERS which_job Text ( 255 );
SELECT t2.[Username]
FROM
[Table 2] AS t2
LEFT JOIN
(
SELECT t1.[Username]
FROM [Table 1] AS t1
WHERE t1.Job_Name=[which_job]
) AS sub
ON t2.[Username] = sub.[Username]
WHERE sub.[Username] Is Null;
Assuming that query returns the correct rows, add the other field you want to see to SELECT t2.[Username].
You should not need DISTINCT unless Table 2 allows duplicate Username values, or Table 1 allows more than one row with the same combination of Username and Job_Name.
You can use not in and distinct
select distinct a.username, a.fullname
from table1 as a
where a.username not in (select distinct username
from table2 where job_name ='my_value');
and for only the job
select distinct a.username, a.fullname
from table1 as a
where a.username not in (select username
from table2
group by username
having count(job_name) = 1
and job_name ='my_value' );
You don't actually need DISTINCT for this query at all:
select t.*
from table1 as t
where not exists (select 1
from table2 as t2
where t2.username = t.username and t2.job_name = "KML_20160531"
);
From your description of the problem, Current_Job_Allowed does not seem relevant.
This will be syntax for your problem
SELECT DISTINCT UserName
FROM Table1
WHERE UserName NOT IN (
SELECT UserName From Table2
WHERE job_name = ''
);

Join Query in Sql server

I am having trouble with a join in sql.
I have 3 tables.
1: Lists the user details
2: Lists the permissions the user group has
3: Lists the page that that group can access
Table1 users :
****************************************
username | group
****************************************
admin | administrator
Table2 groups :
*********************************************
user_group | create | view | system_admin
*********************************************
administrator | 1 | 0 | 1
Table3 urls:
*********************************************
create | view | system_admin
*********************************************
create.php | view.php | system.php
(apologies for my table drawing)
What I am doing via php , is grabbing the user_group they belong to.
I then need to check if they have access to the page they have just hit or redirect them back.
Can I accomplish this with the current table layout the way they are through a join?, Or shall I look to re-design these tables as they are not intuitive for this kind of thing.
I actually might redesign the tables to make them easier to query:
create table users
(
id int,
username varchar(10),
groupid int
);
insert into users values (1, 'admin', 1);
create table groups
(
groupid int,
groupname varchar(20)
);
insert into groups values (1, 'administrator');
create table permissions
(
permissionid int,
permissionname varchar(20)
);
insert into permissions values (1, 'create');
insert into permissions values (2, 'view');
insert into permissions values (3, 'system_admin');
create table urls
(
urlid int,
name varchar(10)
);
insert into urls values(1, 'create.php');
insert into urls values(2, 'view.php');
insert into urls values(3, 'system.php');
create table group_permission_urls
(
groupid int,
permissionid int,
urlid int
);
insert into group_permission_urls values(1, 1, 1);
insert into group_permission_urls values(1, 0, 2);
insert into group_permission_urls values(1, 3, 3);
Then your query would be similar to this:
select *
from users us
left join groups g
on us.groupid = g.groupid
left join group_permission_urls gpu
on us.groupid = gpu.groupid
left join permissions p
on gpu.permissionid = p.permissionid
left join urls u
on gpu.urlid = u.urlid
see SQL Fiddle with Demo
By comparing the $current_page with the results of an IN() subquery, you can do this in one query. If the page matches any listed in a column the user has permission for, this will return a row. It should not return any row if there is no match in an allowed column.
SELECT
groups.create,
groups.view,
groups.system_admin,
1 AS can_access
FROM
users
JOIN groups ON users.group = groups.user_group
WHERE
users.username = '$some_username'
AND (
/* Substitute the current page. Better, use a prepared statement placeholder if your API supports it */
(groups.create = 1 AND '$current_page' IN (SELECT DISTINCT create FROM urls))
OR
(groups.view = 1 AND '$current_page' IN (SELECT DISTINCT view FROM urls))
OR
(groups.system_admin = 1 AND '$current_page' IN (SELECT DISTINCT system_admin FROM urls))
)
This works by comparing the $current_page to the distinct set of possible values from each of your 3 columns. If it matches a column and also the user's group has permission on that type, a row is returned.
select case when count(1) > 0 then 'come in' else 'go away' end
from users, groups, urls
where
users.username = '$username' and
users.user_group = groups.user_group and
((urls.create = '$url' and groups.create = 1) or
(urls.view = '$url' and groups.view = 1) or
(urls.system_admin = '$url' and groups.system_admin = 1))