join order step by step in oracle - oracle

I have a trouble with below query.I want to join columns in step.
For example a.code first b.name1 if not match then join name2 etc.But i cant handle the query.
SELECT * FROM TABLE A
LEFT JOIN TABLE B
ON A.CODE = B.NAME1
OR A.CODE = B.NAME2
OR UPPER(B.NAME3) = UPPER(A.NAME)
Thanks
edit
with sample below,
if TABLEB.CODE = TABLEA.NAME1 match then dont want to look OR TABLEB.CODE = TABLEA.NAME2.
And if not matching any TABLEB.CODE = TABLEA.NAME1 then step by step match the columns on that order.
WITH TABLEA AS (SELECT 13445 AS ID,'A' AS TYPE,'DFSF' AS NAME1 , 'PCK' AS NAME2 FROM DUAL
UNION ALL
SELECT 13445 AS ID,'A' AS TYPE,'PCK' AS NAME1 , 'PCK' AS NAME2 FROM DUAL),
TABLEB AS (SELECT 56544 AS ID, 'PCK' AS CODE, 'PCK' AS FRST_NM FROM DUAL)
SELECT * FROM TABLEA
LEFT JOIN TABLEB
ON TABLEB.CODE = TABLEA.NAME1
OR TABLEB.CODE = TABLEA.NAME2
OR UPPER(TABLEB.FRST_NM) = UPPER(TABLEA.NAME2)

I believe your query will do what you want. There is no step by step in join conditions if you have already mentioned OR. So if either of the condition match, the rows will be matched. So if A.CODE <> B.NAME1 but A.CODE = B.NAME2, they will match. If both are true, then also they will match. I create a CTE with sample data to see the output. It will return 3 rows for 3 matches.
WITH TBL(SEQ,CODE,NAME,NAME1,NAME2,NAME3) AS
(
SELECT 1,'A1','B','A1','D','E' FROM DUAL UNION ALL -- as A.CODE(A1)=B.NAME1, it will match
SELECT 2, 'B2','B','D','B2','F' FROM DUAL UNION ALL --as A.CODE (B2) <> B.NAME1 , it will match for B.NAME2
SELECT 3, 'G','H1','D','A','H1' FROM DUAL UNION ALL --match for UPPER(B.NAME3) = UPPER(A.NAME)
SELECT 4,'P4','Q4','R4','S4','T4' FROM DUAL
)
SELECT * FROM TBL A
LEFT JOIN TBL B
ON (A.CODE = B.NAME1
OR A.CODE = B.NAME2
OR UPPER(B.NAME3) = UPPER(A.NAME)
)
Column SEQ is just to see what rows are returning.
Output
+-----+------+------+-------+-------+-------+-------+--------+--------+---------+---------+---------+
| SEQ | CODE | NAME | NAME1 | NAME2 | NAME3 | SEQ_1 | CODE_1 | NAME_1 | NAME1_1 | NAME2_1 | NAME3_1 |
+-----+------+------+-------+-------+-------+-------+--------+--------+---------+---------+---------+
| 1 | A1 | B | A1 | D | E | 1 | A1 | B | A1 | D | E |
| 2 | B2 | B | D | B2 | F | 2 | B2 | B | D | B2 | F |
| 3 | G | H1 | D | A | H1 | 3 | G | H1 | D | A | H1 |
| 4 | P4 | Q4 | R4 | S4 | T4 | | | | | | |
+-----+------+------+-------+-------+-------+-------+--------+--------+---------+---------+---------+

Related

LISTAGG gave error "not a GROUP BY expression"

I am trying to add a text field, a.desc to a working Oracle query using LISTAGG. Can someone please help on resolving this syntax error?
I have 3 tables
1) Table A
id | Name | Desc |
1 | Name_A_1 | Desc_A_1 |
2 | Name_A_2 | Desc_A_2 |
3 | Name_A_3 | Desc_A_3 |
2) Table B
id | Name | Desc |
11 | Name_B_11 | Desc_B_11 |
12 | Name_B_12 | Desc_B_12 |
13 | Name_B_13 | Desc_B_13 |
3) Table C
id | Table_A_id | Table_B_id |
1 | 1 | 11 |
2 | 1 | 12 |
3 | 1 | 13 |
4 | 3 | 12 |
5 | 3 | 13 |
Here is what is working with current output
A id | A Name | B id(s) |
1 | Name_A_1 | 11; 12; 13 |
3 | Name_A_3 | 12; 13 |
Select A.id AS "A id", A.Name AS "A Name",
LISTAGG(C.id,'; ') WITHIN GROUP (ORDER BY A.id) AS "B id(s)"
From Table_A A
INNER JOIN Table_C C ON A.id = C.id
INNER JOIN Table_B B ON B.id = C.id
GROUP BY A.id, A.Name
ORDER BY A.id
Here is the first desired output by adding "A Desc" column:
A id | A Name | A Desc | B id(s) |
1 | Name_A_1 | Desc_A_1 | 11; 12; 13 |
3 | Name_A_3 | Desc_A_3 | 12; 13 |
Select A.id AS "A id", A.Name AS "A Name", A.Desc AS "A Desc",
LISTAGG(C.id,'; ') WITHIN GROUP (ORDER BY A.id) AS "B id(s)"
From Table_A A
INNER JOIN Table_C C ON A.id = C.id
INNER JOIN Table_B B ON B.id = C.id
GROUP BY A.id, A.Name, A.Desc
ORDER BY A.id
** I am receiving the error of "not a GROUP BY expression"
Here is the second desired output(with Name_B_#) added in last column:
A id | A Name | A Desc | B id(s) |
1 | Name_A_1 | Desc_A_1 | 11(Name_B_11); 12(Name_B_12); 13(Name_B_13) |
3 | Name_A_3 | Desc_A_3 | 12(Name_B_12); 13(Name_B_13) |
I haven't got this far, got stuck on the query above. This section I want to add the Table B desc to each B.id.
thank you in advance for your help!
I tried the below query and it works with your sample data
Select A.id1 AS "A id", A.Name1 AS "A Name", A.Desc1 AS "A Desc",
LISTAGG(C.Table_B_id,'; ') WITHIN GROUP (ORDER BY A.id1) AS "B id(s)"
From A
INNER JOIN C ON A.id1 = C.Table_A_id
INNER JOIN B ON B.id1 = C.Table_B_id
GROUP BY A.id1, A.Name1, A.Desc1
ORDER BY A.id1
The answer for your second question below
Select A.id1 AS "A id", A.Name1 AS "A Name", A.Desc1 AS "A Desc",
LISTAGG(C.Table_B_id||'('||B.Name1||')','; ') WITHIN GROUP (ORDER BY
A.id1) AS "B id(s)"
From A
INNER JOIN C ON A.id1 = C.Table_A_id
INNER JOIN B ON B.id1 = C.Table_B_id
GROUP BY A.id1, A.Name1, A.Desc1
ORDER BY A.id1

oracle - querying NULL values in unpivot query

I want to fetch records from oracle DB where column value is NULL. Also i am using unpivot in my query. Somehow NULL values are not getting selected because of unpivot keyword. Can you please help me about how to get rows for the same when using unpivot.
EDIT:
SELECT a.emp_id, a.emp_dept, b.emp_location
FROM employee a,
location b UNPIVOT (emp_id
FOR dummy_id
IN (emp_id AS 'EMP_ID', last_date AS 'LAST_DATE'))
WHERE emp_id = 123 AND b.emp_loc_id = 'india' AND b.location IS NULL;
Use UNPIVOT INCLUDE NULLS:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE test ( id, a, b, c, d ) AS
SELECT 1, 1, 2, 3, 4 FROM DUAL UNION ALL
SELECT 2, 1, NULL, 3, NULL FROM DUAL;
Query 1:
SELECT *
FROM test
UNPIVOT INCLUDE NULLS ( value FOR name IN ( a, b, c, d ) )
Results:
| ID | NAME | VALUE |
|----|------|--------|
| 1 | A | 1 |
| 1 | B | 2 |
| 1 | C | 3 |
| 1 | D | 4 |
| 2 | A | 1 |
| 2 | B | (null) |
| 2 | C | 3 |
| 2 | D | (null) |

How to fill same value with child row

In Oracle, i want to fill child to lowest parent value.
that is query and result
query :
SELECT col1,col2 FROM DATA
result :
+-----------+------+
| col1 | col2 |
+-----------+------+
| 001 | a |
| 001001 | b |
| 001002 | c |
| 001003 | d |
| 001003001 | e |
| 001003002 | f |
| 002 | g |
| 003 | h |
| 003001 | i |
| 003002 | j |
+-----------+------+
col1 is key. and child index have parent index.
i want to make this result
+------+------+------+------+
| col1 | col2 | col3 | col4 |
+------+------+------+------+
| a | b | b | b |
| a | c | c | c |
| a | d | e | e |
| a | d | f | f |
| g | g | g | g |
| h | i | i | i |
| h | j | j | j |
+------+------+------+------+
if parent have no child, child columns fill to parent.
how can i make this result?...
thank you.
One way to do this:
select c1, coalesce(c2, c1) c2, coalesce(c3, c2, c1) c3, coalesce(c4, c3, c2, c1) c4
from (select substr(path, 2, 1) c1, substr(path, 4, 1) c2,
substr(path, 6, 1) c3, substr(path, 8, 1) c4
from (select sys_connect_by_path(col2, ' ') path
from data
where connect_by_isleaf = 1
connect by substr(col1, 1, length(prior col1)) = prior col1
and length(prior col1) + 3 = length(col1)
start with length(col1) = 3 ) )
Assuming that values in col2 are 1-char long You can do it like above. Otherwise you have to change substr(path, ..., ...) using instr() or regular expressions.
The inner query is hierarchical where I'm selecting only whole path from leaf rows. Outer queries puts data into particular columns dealing with null values.
If You work on Oracle 11g or above You can probably achieve this in more elegant way using Recursive Subquery Factoring.
Test data and output:
create table data (col1 varchar2(10), col2 varchar2(1));
insert into data values ('001', 'a');
insert into data values ('001001', 'b');
insert into data values ('001002', 'c');
insert into data values ('001003', 'd');
insert into data values ('001003001', 'e');
insert into data values ('001003002', 'f');
insert into data values ('002', 'g');
insert into data values ('003', 'h');
insert into data values ('003001', 'i');
insert into data values ('003002', 'j');
C1 C2 C3 C4
-- -- -- --
a b b b
a c c c
a d e e
a d f f
g g g g
h i i i
h j j j
7 rows selected

Rewrite Hive IN clause

I am trying to execute this subquery in HIVE,but i am getting error that subquery is not supported in my HIVE version, unfortunately yes we are using the old version of HIVE.
select col1,col2 from t1 where col1 in (select x from t2 where y = 0)
Then I have rewritten the subquery using left semi join like this,
select a.col1,a.col2
FROM t1 a LEFT SEMI JOIN t2 b on (a.col1 =b.x)
WHERE b.y = 0
This query is running fine if i don't give the where condition, but its not recognising the table b when I try to use b.any column in where condition or use b.any column in select clause. Throwing this error -
Error while compiling statement: FAILED: SemanticException [Error 10004]: Line 3:6 Invalid table alias or column reference 'b': (possible column names
Any help is much appreciated.
select a.col1,a.col2
FROM t2 b RIGHT OUTER JOIN t1 a on (b.x = a.col1)
WHERE b.y = 0
-- When you use LEFT SEMI JOIN, where condition is not work on right side table column. Please change your script to above condition.
Instead of t1 a LEFT SEMI JOIN t2 b, you can do something like this: t1 a LEFT SEMI JOIN (select * from t2 where y = 0) b.
select a.col1,a.col2
FROM t1 a LEFT SEMI JOIN (select * from t2 where y = 0) b on (a.col1 =b.x);
Please see below example.
Department table:
+--------------------+----------------------+--+
| department.deptid | department.deptname |
+--------------------+----------------------+--+
| D101 | sales |
| D102 | finance |
| D103 | HR |
| D104 | IT |
| D105 | staff |
+--------------------+----------------------+--+
Employee tabe:
+-----------------+------------------+------------------+--+
| employee.empid | employee.salary | employee.deptid |
+-----------------+------------------+------------------+--+
| 1001 | 1000 | D101 |
| 1002 | 2000 | D101 |
| 1003 | 3000 | D102 |
| 1004 | 4000 | D104 |
| 1005 | 5000 | D104 |
+-----------------+------------------+------------------+--+
hive> SELECT
dept.deptid, dept.deptname
FROM
department dept
LEFT SEMI JOIN
(SELECT * FROM employee WHERE salary > 3000) emp
ON (dept.deptid = emp.deptid);
+--------------+----------------+--+
| dept.deptid | dept.deptname |
+--------------+----------------+--+
| D104 | IT |
+--------------+----------------+--+

Oracle group by SQL query by matching varchar field

I have a table structure (that I did not design nor can I change) that uses a varchar field to store an attribute about the entity. I would like to write a SQL query to search for two attributes in particular and combine multiple result rows into single rows. To illustrate, my tables are similar to this:
company
=============
| id | name |
-------------
| 1 | co1 |
| 2 | co2 |
| 3 | co3 |
=============
agent
====================================
| id | name | company_id | type |
------------------------------------
| 1 | Tom | 1 | 'type1' |
| 2 | Bob | 1 | 'type2' |
| 3 | Bill | 2 | 'type1' |
| 4 | Jack | 2 | 'type2' |
| 5 | John | 3 | 'type1' |
| 6 | Joe | 3 | 'type2' |
====================================
type1 and type2 are hard-coded into the software as valid values (again, I didn't write it), so a search for these values should be successful (null is permitted). So, I must base my search off of these values.
As a novice, I could write this SQL:
select c.name, a.name, a.type
from company c
inner join agent a on c.id = a.company_id
and sort through these results in my software (Java program):
===========================
| c.name | a.name | type |
---------------------------
| co1 | Tom | type1 |
| co1 | Bob | type2 |
| co2 | Bill | type1 |
| co2 | Jack | type2 |
| co3 | John | type1 |
| co3 | Joe | type2 |
===========================
But, I was hoping there would be a way to combine the rows into something more efficient:
-- my failed attempt at writing this query
select c.name, a.name as type_1_agent, a.name as type_2_agent
from company c
inner join agent a on c.id = a.company_id
group by c.id -- ?
where -- ?
results:
======================================
| name | type_1_agent | type_2_agent |
--------------------------------------
| co1 | Tom | Bob |
| co2 | Bill | Jack |
| co3 | John | Joe |
======================================
Is this possible?
Oracle version:
WITH company (id, name) AS (
SELECT 1, 'co1' FROM DUAL UNION ALL
SELECT 2, 'co2' FROM DUAL UNION ALL
SELECT 3, 'co3' FROM DUAL
),
agent (id, name, company_id, type) AS (
SELECT 1, 'Tom', 1, 'type1' FROM DUAL UNION ALL
SELECT 2, 'Bob', 1, 'type2' FROM DUAL UNION ALL
SELECT 3, 'Bill', 2, 'type1' FROM DUAL UNION ALL
SELECT 4, 'Jack', 2, 'type2' FROM DUAL UNION ALL
SELECT 5, 'John', 3, 'type1' FROM DUAL UNION ALL
SELECT 6, 'Joe', 3, 'type2' FROM DUAL
)
SELECT
company_name, type_1_agent, type_2_agent
FROM
(SELECT company.name company_name, agent.name agent_name, type FROM company JOIN agent ON company.id = agent.company_id)
PIVOT (
MAX(agent_name) agent
FOR type IN ('type1' type_1, 'type2' type_2)
)
ORDER BY
company_name
You can do this with PIVOT functionality, like so:
select company,type_1_agent,type_2_agent
from
(select a.name as agentname, a.type as agenttype,c.name as company
from agent a
inner join company c on c.id = a.company_id
) s
pivot
(max(agentname) for agenttype in ('type1' type_1_agent,'type2' type_2_agent)) p
order by company
Demo
Of course, in this case we hard coded the values for type. This can be made dynamic to accomodate an unknown number of these values.

Resources