Build tree from database - sql

I have two tables.
Table 1. (tbl_1)
| ID | Name |
| -- | -------------|
| 1 | Company1 |
| -- | -------------|
| 2 | Company2 |
| -- | -------------|
| 3 | Company2 |
Table 2. (tbl_2)
| ID | Company_group |
| -- | ------------- |
| 1 | Company2 |
| -- | ------------- |
| 2 | Company2 |
| -- | ------------- |
| 3 | Company2 |
I now that Company_2 is parent company an i want to get next result.
| ID | Name | RootName | RootId |
| -- | -------------| --------- | ------ |
| 1 | Company1 | Company2 | 2 |
| -- | -------------| ----------|--------|
| 3 | Company3 | Company2 | 2 |
I don't know parentId. But i can select all parent companies with follow query:
SELECT DISTINCT id parentId,
name parent_name FROM tbl_1 WHERE name in (
SELECT DISTINCT
Company_group
FROM tbl_2)
How can i build tree for this hierarchy? I can not think, please help.
It is a strange architecture for this case but architect of database is not me.
Also i wrote query but it works not correct. It returns more records.
SELECT ac.id_c parentId, acc.id, ac.Company_group parent_name
FROM tbl_2 ac
JOIN tbl_2 acc
ON ac.Company_group = acc.Company_group
AND ac.id in (
SELECT DISTINCT id parentId
FROM tbl_1 WHERE name in (
SELECT DISTINCT
id parentId
FROM tbl_2)
)
WHERE ac.Company_group iS NOT NULL AND acc.id IS NOT NULL
and ac.id <> acc.id
ORDER BY ac.Company_group

create table tbl_1 (ID int,Name varchar(100));
insert into tbl_1 (ID,Name) values (1,'Company1'),(2,'Company2'),(3,'Company3');
create table tbl_2 (ID int,Company_group varchar(100));
insert into tbl_2 (ID,Company_group) values (1,'Company2'),(2,'Company2'),(3,'Company2');
select t1.ID
,t1.Name
,t2.Company_group as RootName
,t1_b.ID as RootId
from tbl_1 t1
join tbl_2 t2
on t2.ID =
t1.ID
join tbl_1 t1_b
on t1_b.Name =
t2.Company_group
where t1.ID <> t1_b.ID
;

You simply need to join the table with itself:
SELECT *
FROM Company c1
LEFT OUTER JOIN Company c2 ON c1.ParnetID = c2.ID

Related

SQL Optimization, Nested Query on the same table

i have to create a query that return employees having mutliple territories parent for the same function code :
Table employee_territory_function
employee_id| employee_function_id | territory_id
+----------+-----------------------+-------------+
| 12345 | C1 | t1 |
| 12345 | C1 | t2 |
| 12346 | C2 | t3 |
| 12346 | C2 | t4 |
| 12347 | C4 | t8 |
Table territory
territory_id| territory_parent_id
+-----------+-------------------+
| t1 | P1 |
| t2 | P1 |
| t3 | P2 |
| t4 | P3 |
| t8 | P8 |
the result must be the employee_id 12346 which have multiple parents
my query was :
select * from employee_territory_function tr1 where tr1.employee_id in (
select ee.employee_id from (
select et.employee_id from employee_territory_function et
join territory territory on territory.id = et.territory_id
where et.employee_id in (
select etf.employee_id ,etf.employee_function_id from employee_territory_function etf
group by etf.employee_id ,etf.employee_function_id having count(*)>1)) ee
group by ee.employee_id ,ee.employee_function_id ,ee.territory_parent_id having count(*) =1)
The query takes much time execution with 10k for the couple ( employee , function code )
is there a way to optimize or rewrite the query differently ?
SELECT E.EMPLOYEE_ID,E.EMPLOYEE_FUNCTION_ID
FROM EMPLOYEE AS E
JOIN TERRITORY AS T ON E.TERRITORY_ID=T.TERRITORY_ID
GROUP BY E.EMPLOYEE_ID,E.EMPLOYEE_FUNCTION_ID
HAVING MIN(T.TERRITORY_PARENT_ID)<>MAX(T.TERRITORY_PARENT_ID)
Based on your sample data

Update the column of table1 and get it on table2

How can I create a query where I can update the table1 column date that I get it on table2?
Here is some example of the tables
Table1:
| stud_id | start_date | birt_date | name | exam_date |
| s001 | 11/19/2018 | 05/20/1999 | john | 10/20/2018 |
| s003 | 01/01/2018 | 05/25/1995 | mike | 10/20/2018 |
| s005 | 12/23/2018 | 02/20/1999 | ed | 10/20/2018 |
| s005 | 12/23/2018 | 02/20/1999 | ed | 10/05/2017 |
Table2:
| stud_id | start_date | exam_date |
| s005 | 01/01/2017 | 10/20/2018 |
| s001 | 01/01/2017 | 10/20/2018 |
| s003 | 01/01/2017 | 10/20/2018 |
Basically I want to change just the start_date of the 3 so s006 will not change.
How can I accomplish that using query? I was thinking using in then select the table but I think its not gonna work.
I need to based on two or more column for the condition of my update so I want to update the table 1 where table1.stud_id = table2.stud_id and table1.exam_date = table2.exam_date
do join and update
update t1
set t1.stardate=t2.startdate,
t1.exam_date=t2.exam_date
from table1 t1 join table2 t2
on t1.stud_id=t2.stud_id
where t2.stud_id='s003' -- if you just need s003 update
You can also use CTE as well to update:
;With cte as
(
select t1.start_date as t1date,t2.start_date as t2date from table1 t1
join table2 t2
on t1.stud_id=t2.stud_id
and t1.exam_date=t2.exam_date
)
update cte set t1date=t2date
You can join the 2 tables:
UPDATE T1
SET Start_Date = T2.Start_Date
FROM Table1 AS T1
INNER JOIN Table2 AS T2
ON T1.stud_id = T2.stud_id
AND T1.exam_date = T2.exam_date

Showing only duplicate rows from table in postgres

I have table like this:
--------------------------------------
| id | name | phone_number | address |
--------------------------------------
| 1 | Ram | 9090909090 | Delhi |
| 2 | Shyam| 9865444456 | Mumbai |
| 3 | Mohan| 9756543455 | Chennai |
| 4 | Ram | 9090909090 | Delhi |
--------------------------------------
I want to return the rows having same column data. The result will be like this:
--------------------------------------
| id | name | phone_number | address |
--------------------------------------
| 1 | Ram | 9090909090 | Delhi |
| 4 | Ram | 9090909090 | Delhi |
--------------------------------------
This can be done using window functions which avoids the join on the aggregated data and is usually the faster way:
select *
from (
select id,name,phone_number,address
count(*) over (partition by name,phone_number,address) as cnt
from the_table
) t
where cnt > 1;
SELECT t2.id,t2.name,t2.phone_number,t2.address
FROM
(
SELECT name,phone_number,address
FROM tableName
GROUP BY name,phone_number,address
HAVING COUNT(*) > 1
) AS t1
INNER JOIN tableName t2
ON t1.name=t2.name AND t1.phone_number=t2.phone_number AND t1.address=t2.address
Please run the below query, (consider table name to be "data"), to get the desired result as follows:
SELECT * FROM data where name IN (SELECT name FROM data GROUP BY name HAVING COUNT(*) > 1);
SELECT T1.*
FROM
table_name T1
INNER JOIN table_name T2 ON
T1.name= T2.nam` AND
T1.phone_number= T2.phone_number AND T1.address= T2.address
WHERE T2.id <> T1.id

How to make select with extra columns that depends on existing ones

There goes example:
select * from table
and got
ID | NAME | MAIL | BOSS_ID |
1 | Mike | mike#mike.com | 2 |
2 | Josh | josh#hotmail.com | null |
What I actually want to do is make SELECT statement (somehow?) to show two more columns like:
ID | NAME | MAIL | BOSS_ID | BOSS_NAME | BOSS_MAIL
1 | Mike | mike#dfsfs.com | 2 | Josh | josh#dsa.com
2 | Josh | josh#dfsa.com | null | null | null
I know it looks silly, but that what I exactly have been asked to do...
All hints are much appreciatted!
You do an outer join to the table itself on ID and BOSS_ID.
SQL Fiddle
MS SQL Server 2012 Schema Setup:
create table YourTable
(
ID int,
Name varchar(10),
Mail varchar(20),
BOSS_ID int
)
insert into YourTable values
(1 , 'Mike' , 'mike#mike.com' , 2),
(2 , 'Josh' , 'josh#hotmail.com' , null)
Query 1:
select T1.ID,
T1.Name,
T1.Mail,
T2.Name as BossName,
T2.Mail as BossMail
from YourTable as T1
left outer join YourTable as T2
on T1.BOSS_ID = T2.ID
Results:
| ID | NAME | MAIL | BOSSNAME | BOSSMAIL |
|----|------|------------------|----------|------------------|
| 1 | Mike | mike#mike.com | Josh | josh#hotmail.com |
| 2 | Josh | josh#hotmail.com | (null) | (null) |
You cannot SELECT columns that do not exist. You will need to add them. You would want an ALTER TABLE statement like this:
ALTER TABLE yourtable ADD BOSS_NAME VARCHAR( 255 )

How can I write a select statement for this use case?

Please help me compose a SELECT statement. I have these two tables:
Table1 Table2
---------------- ------------------------------------------------
ID | PName | | ID | NameID | DateActive | HoursActive |
---------------- ------------------------------------------------
1 | Neil | | 1 | 1 | 8/2/2013 | 3 |
2 | Mark | | 2 | 1 | 8/3/2013 | 4 |
3 | Onin | | 3 | 2 | 8/2/2013 | 2 |
---------------- | 4 | 2 | 8/6/2013 | 5 |
| 5 | 3 | 8/7/2013 | 1 |
| 6 | 3 | 8/8/2013 | 10 |
------------------------------------------------
And I just want to retrieve the earliest DateActive but no duplicate PName. Like this:
PName | DateActive | HoursActive |
----------------------------------------
Neil | 8/2/2013 | 3 |
Mark | 8/2/2013 | 2 |
Onin | 8/7/2013 | 1 |
----------------------------------------
Something like this might do it. You need to find the min date for each NameID first, then join back to the table to get the hours.
SELECT
PName, MaxDate as DataActive, HoursActive
From
Table1 t1
inner Join Table2 t2 on t1.ID = t2.NameID
Inner Join (Select min(DateActive) as mindate, NameID from Table2 Group by NameID) as t3 on t3.mindate = t2.ActiveDate and t3.NameID = t2.NameId
This should be a pretty standard solution:
select t.pname,
t2.dateactive,
t2.hoursac
from table1 t
join table2 t2 on t.id = t2.nameid
join (
select nameid, min(dateactive) mindateactive
from table2
group by nameid
) t3 on t2.nameid = t3.name
and t3.mindateactive = t2.dateactive
If you are using an RDBMS that supports partition by statements, then this would be more efficient:
select pname, dateactive, HoursActive
from (
select t.pname,
t2.dateactive,
t2.hoursactive,
rank() over (partition by t.id order by t2.dateactive) rownum
from table1 t
join table2 t2 on t.id = t2.nameid
) t
where rownum = 1