oracle sql query 3table join - sql

1 table
| tel_no | client_no |
|-------------|---------------|
| 01011111234 | aa011234 |
| 01022221234 | aa021234 |
2 table
| client_no | client_name |
|-----------|-------------|
| 1234 | kim |
3 table
| client_no | client_name |
| 1234 | wa |
I want to:
If the front of client_no of 1table starts with aa01, the client_name of 2table will be changed.
If the front of client_no of 1table starts with aa02, the client_name of 3table will be changed.
Expected result:
| tel_no | client_no | client_name |
|-------------|------------|-------------|
| 01011111234 | aa011234 | kim |
| 01022221234 | aa021234 | wa |

What an awful data model ...
Sample data:
SQL> with
2 t1 (tel_no, client_no) as
3 (select '01011111234', 'aa011234' from dual union all
4 select '01022221234', 'aa021234' from dual
5 ),
6 t2 (client_no, client_name) as
7 (select '1234', 'kim' from dual),
8 t3 (client_no, client_name) as
9 (select '1234', 'wa' from dual)
10 --
Query:
11 select a.tel_no, a.client_no,
12 case when substr(a.client_no, 1, 4) = 'aa01' then b.client_name
13 when substr(a.client_no, 1, 4) = 'aa02' then c.client_name
14 end client_name
15 from t1 a join t2 b on substr(a.client_no, 5) = b.client_no
16 join t3 c on substr(a.client_no, 5) = c.client_no;
TEL_NO CLIENT_N CLI
----------- -------- ---
01011111234 aa011234 kim
01022221234 aa021234 wa
SQL>

You can use UNION ALL to join 2 selective joins:
SELECT tel_no, t1.client_no, client_name
FROM t1 JOIN t2
ON t1.client_no = CONCAT('aa01',t2.client_no)
UNION ALL
SELECT tel_no, t1.client_no, client_name
FROM t1 JOIN t3
ON t1.client_no = CONCAT('aa02',t3.client_no);

Related

Filtering a table via another table's values

I have 2 tables:
Value
+----+-------+
| id | name |
+----+-------+
| 1 | Peter |
| 2 | Jane |
| 3 | Joe |
+----+-------+
Filter
+----+---------+------+
| id | valueid | type |
+----+---------+------+
| 1 | 1 | A |
| 2 | 1 | B |
| 3 | 1 | C |
| 4 | 1 | D |
| 5 | 2 | A |
| 6 | 2 | C |
| 7 | 2 | E |
| 8 | 3 | A |
| 9 | 3 | D |
+----+---------+------+
I need to retrieve the values from the Value table where the related Filter table does not contain the type 'B' or 'C'
So in this quick example this would be only Joe.
Please note this is a DB2 DB and i have limited permissions to run selects only.
Or also a NOT IN (<*fullselect*) predicate:
Only that my result is 'Joe', not 'Jane' - and the data constellation would point to that ...
WITH
-- your input, sans reserved words
val(id,nam) AS (
SELECT 1,'Peter' FROM sysibm.sysdummy1
UNION ALL SELECT 2,'Jane' FROM sysibm.sysdummy1
UNION ALL SELECT 3,'Joe' FROM sysibm.sysdummy1
)
,
filtr(id,valueid,typ) AS (
SELECT 1,1,'A' FROM sysibm.sysdummy1
UNION ALL SELECT 2,1,'B' FROM sysibm.sysdummy1
UNION ALL SELECT 3,1,'C' FROM sysibm.sysdummy1
UNION ALL SELECT 4,1,'D' FROM sysibm.sysdummy1
UNION ALL SELECT 5,2,'A' FROM sysibm.sysdummy1
UNION ALL SELECT 6,2,'C' FROM sysibm.sysdummy1
UNION ALL SELECT 7,2,'E' FROM sysibm.sysdummy1
UNION ALL SELECT 8,3,'A' FROM sysibm.sysdummy1
UNION ALL SELECT 9,3,'D' FROM sysibm.sysdummy1
)
-- real query starts here
SELECT
*
FROM val
WHERE id NOT IN (
SELECT valueid FROM filtr WHERE typ IN ('B','C')
)
;
-- out id | nam
-- out ----+-------
-- out 3 | Joe
Or also, a failing left join:
SELECT
val.*
FROM val
LEFT JOIN (
SELECT valueid FROM filtr WHERE typ IN ('B','C')
) filtr
ON filtr.valueid = val.id
WHERE valueid IS NULL
You can use EXISTS, as in:
select *
from value v
where not exists (
select null from filter f
where f.valueid = v.id and f.type in ('B', 'C')
);
Result:
ID NAME
--- -----
3 Joe
See running example at db<>fiddle.

Add randomly selected column to result

I'd like to know if there is a more optimal query to get what I want from the database.
My database schema is as follows :
Table1:
(NUM_T1C1;ID_T1C2)
Table2:
(ID_T2C1;RES_T2C2)
First, I want to get a random row defined as:
SELECT NUM_T1C1, ID_T2C1
FROM (
SELECT T1.NUM_T1C1, T2.ID_T2C1, DBMS_RANDOM.VALUE
FROM TABLE1 T1, TABLE2 T2
WHERE T1.NUM_T1C1 IS NOT NULL
AND T2.RES_T2C2 IS NOT NULL
AND T1.ID_T1C2 = T2.ID_T2C1
ORDER BY 3
) WHERE ROWNUM = 1
I will call this query: QUERY1.
My question is as follows, I want to get a row as (random NUM_T1C1 != QUERY1.NUM_T1C1; ID_T2C1 == QUERY1.ID_T2C1), so I have tried:
SELECT NUM_T1C1, ID_T2C1
FROM (
SELECT R2.NUM_T1C1, R1.ID_T2C1, DBMS_RANDOM.VALUE
FROM (
SELECT NUM_T1C1, ID_T2C1
FROM (
SELECT T1.NUM_T1C1, T2.ID_T2C1, DBMS_RANDOM.VALUE
FROM TABLE1 T1, TABLE2 T2
WHERE T1.NUM_T1C1 IS NOT NULL
AND T2.RES_T2C2 IS NOT NULL
AND T1.ID_T1C2 = T2.ID_T2C1
ORDER BY 3
) WHERE ROWNUM = 1
) R1, TABLE1 R2
WHERE R2.NUM_T1C1 <> R1.NUM_T1C1
ORDER BY 3
) WHERE ROWNUM = 1
It's working, but I think this is not the optimal way to do so.
Is there a better way to get the expected result?
EDIT:
I found another way to get those random rows but i still don't know if it's optimal:
SELECT NUM_T1C1, ID_T2C1
FROM (
SELECT R1.NUM_T1C1, R2.ID_T2C1
FROM
(
SELECT T1.NUM_T1C1, T2.ID_T2C1, DBMS_RANDOM.VALUE
FROM TABLE1 T1, TABLE2 T2
WHERE T1.NUM_T1C1 IS NOT NULL
AND T2.T2C2 IS NULL
AND T1.ID_T1C2 = T2.ID_T2C1
ORDER BY 3
) R1,
(
SELECT T2.ID_T2C1, DBMS_RANDOM.VALUE
FROM TABLE1 T1, TABLE2 T2
WHERE T1.NUM_T1C1 IS NOT NULL
AND T2.T2C2 IS NOT NULL
AND T1.ID_T1C2 = T2.ID_T2C1
ORDER BY 2
) R2
) WHERE ROWNUM = 1
Here is an example :
Table1 Table2
+----------+---------+ +---------+----------+
| NUM_T1C1 | ID_T1C2 | | ID_T2C1 | RES_T2C2 |
+----------+---------+ +---------+----------+
| 23 | 5 | | 9 | NULL |
| 521 | 4 | | 4 | DG_513 |
| 71 | 7 | | 7 | FN_731 |
| 97 | 9 | | 5 | NULL |
+----------+---------+ +---------+----------+
Result would be one of those (select one randomly) :
+----------+---------+
| NUM_T1C1 | ID_T2C1 |
+----------+---------+
| 23 | 4 |
| 23 | 7 |
| 97 | 4 |
| 97 | 7 |
+----------+---------+
Here is one way with only a single (cross) join:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE Table1 ( NUM_T1C1, ID_T1C2 ) AS
SELECT 23, 5 FROM DUAL UNION ALL
SELECT 521, 4 FROM DUAL UNION ALL
SELECT 71, 7 FROM DUAL UNION ALL
SELECT 97, 9 FROM DUAL;
CREATE TABLE Table2 ( ID_T2C1, RES_T2C2 ) As
SELECT 9, NULL FROM DUAL UNION ALL
SELECT 4, 'DG_513' FROM DUAL UNION ALL
SELECT 7, 'FN_731' FROM DUAL UNION ALL
SELECT 5, NULL FROM DUAL;
Query 1:
SELECT NUM_T1C1, ID_T2C1
FROM (
SELECT NUM_T1C1,
ID_T2C1,
COUNT( CASE WHEN T1.ID_T1C2 = T2.ID_T2C1 THEN 1 END )
OVER ( PARTITION BY T1.NUM_T1C1 ) AS num_matches
FROM Table1 T1
CROSS JOIN
(
SELECT *
FROM Table2
WHERE RES_T2C2 IS NOT NULL
) T2
ORDER BY DBMS_RANDOM.VALUE
)
WHERE num_matches = 0
--AND ROWNUM = 1
Results:
| NUM_T1C1 | ID_T2C1 |
|----------|---------|
| 23 | 7 |
| 97 | 4 |
| 97 | 7 |
| 23 | 4 |

Recursive CTE (T-SQL) Returns Un-expected Result

I've been staring at this code for WAY too long, trying to figure out why my final query returns unexpected results.
Any help would be much appreciated. Thanks in advance.
Given the following code (running on SQL Server 2008 R2):
USE tempdb;
DECLARE #emp--loyee
TABLE (
EmployeeID int NOT NULL
,EmployeeName nvarchar(50) NOT NULL
PRIMARY KEY(EmployeeID)
)
INSERT INTO #emp
SELECT 1,'Fred'
UNION
SELECT 2,'Mary'
UNION
SELECT 3,'Joe'
UNION
SELECT 4,'Bill'
DECLARE #grp TABLE (
GroupID int NOT NULL
,GroupName nvarchar(50)
PRIMARY KEY(GroupID)
)
INSERT INTO #grp
SELECT 1,'Group 1'
UNION
SELECT 2,'Group 2'
UNION
SELECT 3,'Group 3'
DECLARE #empgrp TABLE (
EmployeeID int NOT NULL
,GroupID int NOT NULL
PRIMARY KEY (EmployeeID,GroupID)
)
INSERT INTO #empgrp
SELECT 1,1
UNION
SELECT 2,1
UNION
SELECT 3,1
UNION
SELECT 4,2
DECLARE #grpgrp TABLE (
GroupID int NOT NULL
,ParentGroupID int
,UNIQUE CLUSTERED(GroupID,ParentGroupID)
)
INSERT INTO #grpgrp
SELECT 1,2
UNION
SELECT 2,3;
WITH AllEmpGroups (EmployeeID,GroupID,RootGroupID)
AS
(
SELECT CAST(NULL as int) as EmployeeID,pgrp.GroupID,pgrp.ParentGroupID
FROM #grpgrp pgrp LEFT JOIN #grpgrp ggrp
ON pgrp.ParentGroupID = ggrp.GroupID
UNION ALL
SELECT e.EmployeeID,eg.GroupID,aeg.RootGroupID
FROM #emp e JOIN #empgrp eg
ON e.EmployeeID = eg.EmployeeID
JOIN #grpgrp ggrp
ON eg.GroupID = ggrp.GroupID
JOIN AllEmpGroups aeg
ON aeg.GroupID = ggrp.ParentGroupID
)
SELECT EmployeeID,GroupID,RootGroupID
FROM AllEmpGroups
What I get is:
+------------+---------+-------------+
| EmployeeID | GroupID | RootGroupID |
+------------+---------+-------------+
| NULL | 1 | 2 |
| NULL | 2 | 3 |
| 1 | 1 | 3 |
| 2 | 1 | 3 |
| 3 | 1 | 3 |
+------------+---------+-------------+
What I would expect/want to get is this:
+------------+---------+-------------+
| EmployeeID | GroupID | RootGroupID |
+------------+---------+-------------+
| NULL | 1 | 2 |
| NULL | 2 | 3 |
| 4 | 2 | 3 |
| 1 | 1 | 3 |
| 2 | 1 | 3 |
| 3 | 1 | 3 |
+------------+---------+-------------+
Bottom line, I want the full recursive stack of all employees beneath a given root group(s), with the root group id on every row.
What am I missing?
First:
You need a row for the root node in #grpgrp with values 3, null
The anchor (part before the union all) of your recursive cte needs to be the root node (3, null) for ancestor first recursion.
...
INSERT INTO #grpgrp
SELECT 1,2
UNION all
SELECT 2,3
UNION all
select 3, null;
WITH AllEmpGroups (EmployeeID,GroupID,RootGroupID)
AS
(
SELECT CAST(NULL as int) as EmployeeID,pgrp.GroupID, ParentGroupID = pgrp.GroupID
FROM #grpgrp pgrp LEFT JOIN #grpgrp ggrp
ON pgrp.ParentGroupID = ggrp.GroupID
where pgrp.ParentGroupId is null
UNION ALL
SELECT e.EmployeeID,eg.GroupID,aeg.RootGroupID
FROM #emp e JOIN #empgrp eg
ON e.EmployeeID = eg.EmployeeID
JOIN #grpgrp ggrp
ON eg.GroupID = ggrp.GroupID
JOIN AllEmpGroups aeg
ON aeg.GroupID = ggrp.ParentGroupID
)
SELECT EmployeeID,GroupID,RootGroupID
FROM AllEmpGroups
rextester demo: http://rextester.com/CBWY80387
returns:
+------------+---------+-------------+
| EmployeeID | GroupID | RootGroupID |
+------------+---------+-------------+
| NULL | 3 | 3 |
| 4 | 2 | 3 |
| 1 | 1 | 3 |
| 2 | 1 | 3 |
| 3 | 1 | 3 |
+------------+---------+-------------+
Beyond that, I would build the groups hierarchy first, then join the employees like so:
WITH AllEmpGroups (GroupID,ParentGroupID,RootGroupID)
AS
(
SELECT pgrp.GroupID, pgrp.ParentGroupID, RootGroupId = GroupID
FROM #grpgrp pgrp
where pgrp.ParentGroupId is null
UNION ALL
SELECT ggrp.GroupID,ggrp.ParentGroupID,aeg.RootGroupID
FROM #grpgrp ggrp
inner JOIN AllEmpGroups aeg
ON aeg.GroupID = ggrp.ParentGroupID
)
SELECT eg.EmployeeID,aeg.*
FROM AllEmpGroups aeg
left JOIN #empgrp eg
ON eg.GroupID = aeg.GroupID
rextester demo: http://rextester.com/FAK76354
returns:
+------------+---------+---------------+-------------+
| EmployeeID | GroupID | ParentGroupID | RootGroupID |
+------------+---------+---------------+-------------+
| NULL | 3 | NULL | 3 |
| 4 | 2 | 3 | 3 |
| 1 | 1 | 2 | 3 |
| 2 | 1 | 2 | 3 |
| 3 | 1 | 2 | 3 |
+------------+---------+---------------+-------------+
Start with
WITH AllGroups (RootGroupID,GroupID,ParentGroupID, level)
AS
(
SELECT GroupID RootGroupID, GroupID, Cast(NULL as int) ParentGroupID, 0 level
FROM #grp g
WHERE NOT EXISTS (SELECT 1 FROM #grpgrp gg WHERE gg.GroupID = g.GroupID)
UNION ALL
SELECT ag.RootGroupID, gg.GroupID, gg.ParentGroupID, level+1
FROM #grpgrp gg
JOIN AllGroups ag
ON ag.GroupID = gg.ParentGroupID
)
SELECT EmployeeID, ag.GroupID, ParentGroupID, RootGroupID
FROM AllGroups ag
LEFT JOIN #empgrp eg ON eg.GroupID = ag.GroupID
ORDER BY RootGroupID, level, ParentGroupID, GroupID;
Not sure why you need the row:
| NULL | 2 | 3 |

Create view from multiple tables, combine values from multiple rows into one row

I have 3 tables as below:
Area table:
UserID | Area
---------------
1 | 10001
2 | 10002
3 | 10003
Info table:
UserID | Info
-----------------
1 | U1_Info1
1 | U1_Info2
1 | U1_Info3
2 | U2_Info1
3 | U3_Info1
Company table:
UserID | Company
-----------------
1 | ComA
2 | ComB
3 | ComC
After that, I want group by UserID. My expected result as below:
UserID | Area | Info1 | Info2 | Info3 | Company
----------------------------------------------------------
1 | 10001 | U1_Info1 | U1_Info2 | U1_Info3 | ComA
2 | 10002 | U2_Info1 | | | ComB
3 | 10003 | U3_Info1 | | | ComC
User 3 doesn't have Info2 and Info3 so I set them = ' '.
Can I make a View like that?
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE Area ( UserID, Area ) AS
SELECT 1, 10001 FROM DUAL
UNION ALL SELECT 2, 10002 FROM DUAL
UNION ALL SELECT 3, 10003 FROM DUAL;
CREATE TABLE Info ( UserID, Info ) AS
SELECT 1, 'U1_Info1' FROM DUAL
UNION ALL SELECT 1, 'U1_Info2' FROM DUAL
UNION ALL SELECT 1, 'U1_Info3' FROM DUAL
UNION ALL SELECT 2, 'U2_Info1' FROM DUAL
UNION ALL SELECT 3, 'U3_Info1' FROM DUAL;
CREATE TABLE Company (UserID, Company ) AS
SELECT 1, 'ComA' FROM DUAL
UNION ALL SELECT 2, 'ComB' FROM DUAL
UNION ALL SELECT 3, 'ComC' FROM DUAL;
CREATE VIEW TEST AS
SELECT A.UserID,
MAX( A.Area ) AS Area,
MAX( CASE WHEN I.Info LIKE '%_Info1' THEN I.Info END ) AS Info1,
MAX( CASE WHEN I.Info LIKE '%_Info2' THEN I.Info END ) AS Info2,
MAX( CASE WHEN I.Info LIKE '%_Info3' THEN I.Info END ) AS Info3,
MAX( C.Company ) AS Company
FROM Area A
INNER JOIN
Company C
ON ( A.UserID = C.UserID )
LEFT OUTER JOIN
Info I
ON ( A.UserID = I.UserID )
GROUP BY
A.UserID
Query 1:
SELECT * FROM test
Results:
| USERID | AREA | INFO1 | INFO2 | INFO3 | COMPANY |
|--------|-------|----------|----------|----------|---------|
| 1 | 10001 | U1_Info1 | U1_Info2 | U1_Info3 | ComA |
| 2 | 10002 | U2_Info1 | (null) | (null) | ComB |
| 3 | 10003 | U3_Info1 | (null) | (null) | ComC |

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