SQL Left JOINING 3 Tables - sql

I have 1 Table:
Contacts (ID integer, Name text, ATT_ID integer)
My Table is filled with these values:
(1, 'Alice', 1)
(2, 'Bob', 1)
(3, 'Carol', 1)
(4, 'Dave', 4)
(5, 'Eve', 4)
(6, 'Frank', 6)
The goal is to join these Contacts ID with the paired ATT_ID.
This is my current SQL-Code:
SELECT t1.ID as ID, t1.Name , tt.Name , tt2.Name
FROM Contacts as t1
LEFT JOIN (
SELECT MIN(t2.ID), t2.Name, t2.ATT_ID FROM Contacts as t2
WHERE t2.ID <> t2.ATT_ID)
AS tt ON t1.ID = tt.ATT_ID
LEFT JOIN (
SELECT MAX(t3.ID), t3.Name, t3.ATT_ID FROM Contacts as t3
WHERE t3.ID <> t3.ATT_ID)
AS tt2 ON t1.ID = tt2.ATT_ID
WHERE t1.ID = t1.ATT_ID;
and my Result is:
Alice | Bob | null
Dave | null | Eve
Frank | null | null
But the desired result should look like this:
Alice | Bob | Carol
Dave | Eve | null
Frank | null | null
How can I accomplish this?

Assuming your RDBMS supports Window Functions...
This can be accomplished by generating a row number of for each sub group and only displaying the smallest row number.
This also assumes you have no more than 3 in each group.
The below is ORACLE specific but should work with MS SQL and Postgresql and DB2. It will not work with MySQL as window functions are not supported.
Note the with block as built will only work in ORACLE.
With contacts (ID, Name, ATT_ID) as (
Select 1, 'Alice', 1 from dual union all
Select 2, 'Bob', 1 from dual union all
Select 3, 'Carol', 1 from dual union all
Select 4, 'Dave', 4 from dual union all
Select 5, 'Eve', 4 from dual union all
Select 6, 'Frank', 6 from dual)
--FROM HERE ON should work if window functions supported.
Select * from (
Select T1.Name N1, T2.Name N2, T3.Name N3, Row_Number() Over (partition by T1.ATT_ID order by T1.ID) rn
FROM Contacts T1
LEFT JOIN Contacts T2
on T1.ATT_ID = T2.ATT_ID
and T1.ID < T2.ID
LEFT JOIN contacts T3
on T2.ATT_ID = T3.ATT_ID
and T2.ID < T3.ID
and T1.ID < T3.ID) B
WHERE RN =1

Related

Oracle - Count a select based on a subquery

I need to count the result from a sub query based on the main query
I would like to count how many Vehicle there are with the year 2021.
How would I generate the result of this query with a column counting each green car that has the year 2021. Example below
Table 1
ID
Vehicle
1
Car
2
motorcycle
3
bicycle
Table 2
ID
ID_TABLE1
COLOR
1
2
RED
2
1
GREEN
3
3
BLACK
4
1
GREEN
Table 3
ID
ID_TABLE1
YEAR
1
2
2021
2
1
2020
3
3
2021
3
1
2020
My try, its not working
select t1.Vehicle, t2.color from table1 t1 inner join table2 t2 on t1.id = t2.id_table1
left joint(select count* table 3 t3 where t3.year = 2020 ) tbyear on t1.id = t3.id_table1
The final table would look like this:
NAME
NAME
COUNT
CAR
green
2
motorcycle
red
0
bicycle
black
0
It should works like this if you want to see Year, Vehicle, color
select t3.Year, t1.Vehicle, t2.color, COUNT(*)
from table1 t1
inner join table2 t2 on t1.id = t2.id_table1
inner join table3 t3 on t1.id = t3.id_table1
GROUP BY t3.Year, t1.Vehicle, t2.color
If you don't need some filed - delete it in select and group by.
But it's not the best solution - when you haven't sales in 2021 for Green Car (for example)- you will not see it in a result query that's why i should ask: Do you need to see it? (for example 0 instead of skipping this row)
Am i correct about the task?
Looks like you want outer apply():
select
t1.Vehicle, t2.color, tbyear.*
from table1 t1
inner join table2 t2
on t1.id = t2.id_table1
outer apply(
select count(*) cnt
from table3 t3
where t1.id = t3.id_table1
and t3.year = 2020
) tbyear;
Full example with test data:
with -- test data:
Table1(ID,Vehicle) as (
select 1, 'Car' from dual union all
select 2, 'motorcycle' from dual union all
select 3, 'bicycle' from dual
)
,Table2(ID, ID_TABLE1, COLOR) as (
select 1, 2, 'RED' from dual union all
select 2, 1, 'GREEN' from dual union all
select 3, 3, 'BLACK' from dual union all
select 4, 1, 'GREEN' from dual
)
,Table3(ID,ID_TABLE1,YEAR) as (
select 1, 2, 2021 from dual union all
select 2, 1, 2020 from dual union all
select 3, 3, 2021 from dual union all
select 3, 1, 2020 from dual
)
-- end test data
select
t1.Vehicle, t2.color, tbyear.*
from table1 t1
inner join table2 t2
on t1.id = t2.id_table1
outer apply(
select count(*) cnt
from table3 t3
where t1.id = t3.id_table1
and t3.year = 2020
) tbyear
Results:
EHICLE COLOR CNT
---------- ----- ----------
motorcycle RED 0
Car GREEN 2
bicycle BLACK 0
Car GREEN 2

sql select values from another table if exist

Please help, need to select from table 1, but if entry with the same id exists in table2 should return name and last name from there otherwise values from table1
table1
id|name|lastname
1 | |
2 | |
3 | |
table2
id|name|lastname
3 | |
Tried this, but not working
SELECT ID, NAME, LASTNAME
FROM table1
WHERE EXISTS
(SELECT 1 FROM table2 WHERE table2.ID = table1.ID)
if entry with the same id exists in table2 should return name and last name from there otherwise values from table1
You want a LEFT OUTER JOIN and then to use COALESCE:
SELECT t1.id,
COALESCE( t2.name, t1.name ) AS name,
COALESCE( t2.lastname, t1.lastname ) AS last_name
FROM table1 t1
LEFT OUTER JOIN table2 t2
ON ( t1.id = t2.id )
Which, for your sample data:
CREATE TABLE table1 ( id, name, lastname ) AS
SELECT 1, 'Alice1', 'Abbot1' FROM DUAL UNION ALL
SELECT 2, 'Betty1', 'Baron1' FROM DUAL UNION ALL
SELECT 3, 'Carol1', 'Casey1' FROM DUAL;
CREATE TABLE table2 ( id, name, lastname ) AS
SELECT 3, 'Carol2', 'Casey2' FROM DUAL;
Outputs:
ID
NAME
LAST_NAME
3
Carol2
Casey2
2
Betty1
Baron1
1
Alice1
Abbot1
db<>fiddle here

Combining and checking table value on SQL (ORACLE)

Table 1
no name col1
1 a a_1
2 b b_1
Table 2
id name parent
a_1 zz c_1
b_1 yy d_1
c_1 aa null
d_1 bb e_1
e_1 dd1 null
what i want to show is showing the all list name. for example table 1 name a has col1 name a_1 it will show the name on table 2, and then check the parent in the table 2 and show it and keep checking until it found null. the example is like below.. im sorry for my bad explanation
t1_name t2_name t2_name t2_name
a zz aa
b yy bb dd1
or shows like below
t1_name t2_name
a aa/zz
b dd1/bb/yy
what I've done is this query
select t1.name,t2.name as folder from table1 as t1 inner join table2 as t2 on t1.col1=t2.id
and I don't know how to check again in query... I am using oracle version 12.2.0.1.0 in SQL developer any help?
You want to get the rows from the first table and then recursively fetch all the rows from the second table until you reach a null parent, so you do:
with cte(NAME,
PARENT,
CURRENTPATH) as
(select t1.NAME,
t2.PARENT,
t2.NAME as CURRENTPATH
from TABLE1 t1
join TABLE2 t2 on t1.COL1 = t2.ID
union all
select t1.NAME,
t2.PARENT,
t1.CURRENTPATH || '/' || t2.NAME as CURRENTPATH
from cte t1
join TABLE2 t2 on t2.ID = t1.PARENT)
select NAME,
CURRENTPATH
from cte
where PARENT is null;
You can use the hierarchical query as following:
SQL> -- Your data
SQL> with table1(no,name,col1) as
2 (SELECT 1, 'a','a_1' FROM DUAL UNION ALL
3 SELECT 2, 'b','b_1' FROM DUAL
4 ),
5 table2 (id, name, parent) as
6 (select 'a_1', 'zz', 'c_1' from dual union all
7 select 'b_1', 'yy', 'd_1' from dual union all
8 select 'c_1', 'aa', null from dual union all
9 select 'd_1', 'bb', 'e_1' from dual union all
10 select 'e_1', 'dd1', null from dual)
11 -- Your query starts from here
12 SELECT
13 T1.NAME AS T1_NAME,
14 T2.NAMES AS T2_NAMES
15 FROM TABLE1 T1
16 JOIN (
17 SELECT
18 T2.ID,
19 SYS_CONNECT_BY_PATH(T2.NAME, '/') AS NAMES,
20 ROW_NUMBER() OVER(PARTITION BY ID ORDER BY LEVEL DESC) AS L
21 FROM TABLE2 T2
22 CONNECT BY T2.PARENT = PRIOR T2.ID
23 ) T2 ON T1.COL1 = T2.ID
24 WHERE L = 1;
T1_NAME T2_NAMES
------- ---------------
a /aa/zz
b /dd1/bb/yy
SQL>
Cheers!!
Which Oracle version are you using?

how to find collegue Id in table

i have one table as Employee that have two column Id and Branch. i have to find its collegue Id except his own id from table without using subquery.
Id Branch
==============
1 Delhi
2 Mumbai
3 Delhi
4 Delhi
5 Mumbai
6 Mumbai
if i enter e.g. 3 then my answer has to be 1 and 4.
Without a subquery means with a self join:
select tt.*
from tablename t inner join tablename tt
on t.Branch = tt.Branch and tt.id <> t.id
where t.id = 3
See the demo.
use self join
select t2.id from table_name t1
join table_name t2 on t1.Branch=t2.Branch
where t1.id=3 and t2.id!=3
for data preparation easily i used CTE version below with result
with cte as
(
select 1 as id, 'Delhi' as b
union all
select 2, 'Mumbai'
union all
select 3 , 'Delhi'
union all
select 4 , 'Delhi'
) select t2.id from cte t1 join cte t2 on t1.b=t2.b
where t1.id=3 and t2.id!=3
demo link
here is the output
id
1
4
This query should give you the answer you're after:
SELECT E1.Id
FROM Employee E1
INNER
JOIN Employee E2
ON E1.[Branch] = E2.[Branch]
WHERE E2.Id = 3
AND E1.Id <> 3
It works by joining the Employee table onto itself, by Branch and then:
WHERE E2.Id = 3 - Where the Employee is Id # 3
AND E1.Id <> 3 - excluding any records from the original Employee table where Id is 3
Here's the script I used to create your test data (in SQL Server) for the purposes of validating the query:
CREATE TABLE [Employee]
(
[Id] INT,
[Branch] NVARCHAR(10)
)
INSERT
INTO [Employee]
(
Id,
[Branch]
)
VALUES (1, 'Delhi'),
(2, 'Mumbai'),
(3, 'Delhi'),
(4, 'Delhi'),
(5, 'Mumbai'),
(6, 'Mumbai')

Eliminating NOT IN from query

I'm trying to eliminate the need to use NOT IN in my query:
select count(*)
FROM TABLE1 T1
LEFT OUTER JOIN TABLE2 T2
ON T1.DATAID = T2.EXISTING_DOCUMENT
AND T1.ownerid = -2000
AND T1.SUBTYPE = 144
AND T1.dataid NOT IN (SELECT T3.dataid
FROM TABLE3 T3
WHERE T3.ID = 123)
Reason: I read that NOT IN is slow (+500k rows) and doesn't use indices
I tried:
select count(*)
FROM TABLE1 T1
LEFT OUTER JOIN TABLE2 T2
ON T1.DATAID = T2.EXISTING_DOCUMENT
AND T1.ownerid = -2000
AND T1.SUBTYPE = 144
left outer join TABLE3 T3
on T3.ancestorid = T1.dataid
where T3.ID = 123
NOT IN does use indices, at least in a competent database such as Oracle. However, you can write this using joins if you prefer.
But, why doesn't this do what you want?
select count(*)
FROM TABLE1 T1
WHERE T1.ownerid = -2000 AND T1.SUBTYPE = 144;
You are using a LEFT JOIN, so the only difference is that your version counts duplicates in TABLE2. But that may not really apply.
Your query doesn't really make sense, because you are comparing T1.dataid to T1.dataid. But, further, the comparison to Table3 has no impact on the result. So, you can just remove it:
select count(*)
FROM TABLE1 T1 LEFT OUTER JOIN
TABLE2 T2
ON T1.DATAID = T2.EXISTING_DOCUMENT AND
T1.ownerid = -2000 AND
T1.SUBTYPE = 144 ;
Because of the LEFT JOIN, filtering in the ON clause will not remove any rows. And because it is NOT IN, there is no possibility of duplication.
Use a WHERE x IS NULL filter to emulate a NOT IN.
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE t1 ( ownerid int, subtype int, dataid int, note varchar(100) ) ;
INSERT INTO t1 ( ownerid, subtype, dataid, note )
SELECT 1 as ownerid, 1 as subtype, 1 as dataid, 'WHERE Filter' as note FROM DUAL UNION ALL
SELECT -2000, 1,1, 'IN WHERE Filter' FROM DUAL UNION ALL
SELECT -2000,144,1, 'IN WHERE, NOT IN t3' FROM DUAL UNION ALL
SELECT -2000,144,2, 'IN WHERE, IN t3' FROM DUAL UNION ALL
SELECT -2000,144,3, 'IN WHERE, NOT IN t3' FROM DUAL
;
CREATE TABLE t2 ( existing_document int, note varchar(100) ) ;
INSERT INTO t2 (existing_document, note)
SELECT 1 as existing_document, 'JOIN t1' as note FROM DUAL UNION ALL
SELECT 2, 'JOIN t1' FROM DUAL UNION ALL
SELECT 2, 'JOIN t1, DUPE' FROM DUAL UNION ALL
SELECT 3, 'JOIN t1' FROM DUAL UNION ALL
SELECT 3, 'JOIN t1, DUPE' FROM DUAL UNION ALL
SELECT 4, 'NOT JOIN t1' FROM DUAL
;
CREATE TABLE t3 ( id int, dataid int, note varchar(100) ) ;
INSERT INTO t3 (id, dataid, note)
SELECT 1 as id, 1 as dataid, 'No filter. No match.' as note FROM DUAL UNION ALL
SELECT 1, 4, 'No filter. No match t1.' FROM DUAL UNION ALL
SELECT 123,2,'Match JOIN filter. Match t1' FROM DUAL
;
Read the notes in the setup to view how I'm building up the data. It's very simple and not a lot to count, but it should give you an idea on how this data works together.
Query:
SELECT * /* Not counting here so you can see what's supposed to be counted. */
FROM t1
INNER JOIN t2 ON t1.dataid = t2.EXISTING_DOCUMENT
LEFT OUTER JOIN t3 ON t1.dataid = t3.dataid
AND t3.ID = 123
WHERE t1.ownerid = -2000
AND t1.subtype = 144
AND t3.dataid IS NULL /* This is the NOT IN */
Results:
| OWNERID | SUBTYPE | DATAID | NOTE | EXISTING_DOCUMENT | NOTE | ID | DATAID | NOTE |
|---------|---------|--------|---------------------|-------------------|---------------|--------|--------|--------|
| -2000 | 144 | 1 | IN WHERE, NOT IN t3 | 1 | JOIN t1 | (null) | (null) | (null) |
| -2000 | 144 | 3 | IN WHERE, NOT IN t3 | 3 | JOIN t1 | (null) | (null) | (null) |
| -2000 | 144 | 3 | IN WHERE, NOT IN t3 | 3 | JOIN t1, DUPE | (null) | (null) | (null) |
The optimizer usually runs very well with the WHERE x IS NULL syntax and indexes should still apply, but if Oracle is able to make use of the indexes in the NOT IN, that is a big plus. If you're dealing with a lot of data, the IS NULL method can be a lot faster. The best check is to just test it with your actual data.
how about NOT EXISTS?
select count(*)
FROM TABLE1 T1
LEFT OUTER JOIN TABLE2 T2
ON T1.DATAID = T2.EXISTING_DOCUMENT
AND T1.ownerid = -2000
AND T1.SUBTYPE = 144
AND NOT EXISTS (SELECT 1
FROM TABLE3 T3
WHERE T3.ID = 123
AND T3.dataid = T1.dataid)