oracle - querying NULL values in unpivot query - oracle

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) |

Related

Lag and Lead to next month

TABLE: HIST
CUSTOMER MONTH PLAN
1 1 A
1 2 B
1 2 C
1 3 D
If I query:
select h.*, lead(plan) over (partition by customer order by month) np from HIST h
I get:
CUSTOMER MONTH PLAN np
1 1 A B
1 2 B C
1 2 C D
1 3 D (null)
But I wanted
CUSTOMER MONTH PLAN np
1 1 A B
1 2 B D
1 2 C D
1 3 D (null)
Reason being, next month to 2 is 3, with D. I'm guessing partition by customer order by month doesn't work the way I thought.
Is there a way to achieve this in Oracle 12c?
One way to do it is to use RANGE partitioning with the MIN analytic function. Like this:
select h.*,
min(plan) over
(partition by customer
order by month
range between 1 following and 1 following) np
from HIST h;
+----------+-------+------+----+
| CUSTOMER | MONTH | PLAN | NP |
+----------+-------+------+----+
| 1 | 1 | A | B |
| 1 | 2 | B | D |
| 1 | 2 | C | D |
| 1 | 3 | D | |
+----------+-------+------+----+
When you use RANGE partitioning, you are telling Oracle to make the windows based on the values of the column you are ordering by rather than making the windows based on the rows.
So, e.g.,
ROWS BETWEEN 1 following and 1 following
... will make a window containing the next row.
RANGE BETWEEN 1 following and 1 following
... will make a window containing all the rows having the next value for month.
UPDATE
If it is possible that some values for MONTH might be skipped for a given customer, you can use this variant:
select h.*,
first_value(plan) over
(partition by customer
order by month
range between 1 following and unbounded following) np
from h
+----------+-------+------+----+
| CUSTOMER | MONTH | PLAN | NP |
+----------+-------+------+----+
| 1 | 1 | A | B |
| 1 | 3 | B | D |
| 1 | 3 | C | D |
| 1 | 4 | D | |
+----------+-------+------+----+
You can use LAG/LEAD twice. The first time to check for duplicate months and to set the value to NULL in those months and the second time use IGNORE NULLS to get the next monthly value.
It has the additional benefit that if months are skipped then it will still find the next value.
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE HIST ( CUSTOMER, MONTH, PLAN ) AS
SELECT 1, 1, 'A' FROM DUAL UNION ALL
SELECT 1, 2, 'B' FROM DUAL UNION ALL
SELECT 1, 2, 'C' FROM DUAL UNION ALL
SELECT 1, 3, 'D' FROM DUAL UNION ALL
SELECT 2, 1, 'E' FROM DUAL UNION ALL
SELECT 2, 1, 'F' FROM DUAL UNION ALL
SELECT 2, 3, 'G' FROM DUAL UNION ALL
SELECT 2, 5, 'H' FROM DUAL;
Query 1:
SELECT CUSTOMER,
MONTH,
PLAN,
LEAD( np ) IGNORE NULLS OVER ( PARTITION BY CUSTOMER ORDER BY MONTH, PLAN, ROWNUM ) AS np
FROM (
SELECT h.*,
CASE MONTH
WHEN LAG( MONTH ) OVER ( PARTITION BY CUSTOMER ORDER BY MONTH, PLAN, ROWNUM )
THEN NULL
ELSE PLAN
END AS np
FROM hist h
)
Results:
| CUSTOMER | MONTH | PLAN | NP |
|----------|-------|------|--------|
| 1 | 1 | A | B |
| 1 | 2 | B | D |
| 1 | 2 | C | D |
| 1 | 3 | D | (null) |
| 2 | 1 | E | G |
| 2 | 1 | F | G |
| 2 | 3 | G | H |
| 2 | 5 | H | (null) |
Just so that it is listed here as an option for Oracle 12c (onward), you can use an apply operator for this style of problem
select
h.customer, h.month, h.plan, oa.np
from hist h
outer apply (
select
h2.plan as np
from hist h2
where h.customer = h.customer
and h2.month > h.month
order by month
fetch first 1 rows only
) oa
order by
h.customer, h.month, h.plan
I don't know of any Oracle 12c public fiddles so, an example in SQL Server can be found here: http://sqlfiddle.com/#!18/cd95e/1
| customer | month | plan | np |
|----------|-------|------|--------|
| 1 | 1 | A | C |
| 1 | 2 | B | D |
| 1 | 2 | C | D |
| 1 | 3 | D | (null) |

join order step by step in 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 | | | | | | |
+-----+------+------+-------+-------+-------+-------+--------+--------+---------+---------+---------+

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.

Oracle 11g hierarchical query needs some inherited data

table looks kind of like:
create table taco (
taco_id int primary key not null,
taco_name varchar(255),
taco_prntid int,
meat_id int,
meat_inht char(1) -- inherit meat
)
data looks like:
insert into taco values (1, '1', null, 1, 'N');
insert into taco values (2, '1.1', 1, null, 'Y');
insert into taco values (3, '1.1.1', 2, null, 'N');
insert into taco values (4, '1.2', 1, 2, 'N');
insert into taco values (5, '1.2.1', 4, null, 'Y');
insert into taco values (6, '1.1.2', 2, null, 'Y');
or...
- 1 has a meat_id=1
- 1.1 has a meat_id=1 because it inherits from its parent via taco_prntid=1
- 1.1.1 has a meat_id of null because it does NOT inherit from its parent
- 1.2 has a meat_id=2 and it does not inherit from its parent
- 1.2.1 has a meat_id=2 because it does inherit from its parent via taco_prntid=4
- 1.1.2 has a meat_id=1 because it does inherit from its parent via taco_prntid=2
Now... how in the world do I query what the meat_id is for each taco_id? What is below did work until I realized that I wasn't using the inheritance flag and some of my data was messing up.
select x.taco_id,
x.taco_name,
to_number(substr(meat_id,instr(rtrim(meat_id), ' ', -1)+1)) as meat_id
from ( select taco_id,
taco_name,
level-1 "level",
sys_connect_by_path(meat_id, ' ') meat_id
from taco
start with taco_prntid is null
connect by prior taco_id = taco_prntid
) x
I can post some failed attempts to modify my query above but they're rather embarrassing failures. I haven't worked with hierarchical queries at all before beyond the basics so I'm hoping there is some keyword or concept I'm not aware I should be searching for.
I posted an answer myself down at the bottom to show what I ended up with ultimately. I'm leaving the other answer as accepted because they were able to make the data more clear for me and without it, I wouldn't have gotten anywhere.
Your inner query is correct. All you need is to pick only the rightmost number from the meat_id column of inner query, when flag is Y.
I have used REGEXP_SUBSTR function to get the rightmost number and CASE statement to check the flag.
SQL Fiddle
Query 1:
select taco_id,
taco_name,
taco_prntid,
case meat_inht
when 'N' then meat_id
when 'Y' then to_number(regexp_substr(meat_id2,'\d+\s*$'))
end meat_id,
meat_inht
from ( select taco_id,
taco_name,
taco_prntid,
meat_id,
meat_inht,
level-1 "level",
sys_connect_by_path(meat_id, ' ') meat_id2
from taco
start with taco_prntid is null
connect by prior taco_id = taco_prntid
)
order by 1
Results:
| TACO_ID | TACO_NAME | TACO_PRNTID | MEAT_ID | MEAT_INHT |
|---------|-----------|-------------|---------|-----------|
| 1 | 1 | (null) | 1 | N |
| 2 | 1.1 | 1 | 1 | Y |
| 3 | 1.1.1 | 2 | (null) | N |
| 4 | 1.2 | 1 | 2 | N |
| 5 | 1.2.1 | 4 | 2 | Y |
| 6 | 1.1.2 | 2 | 1 | Y |
Query 2:
select taco_id,
taco_name,
taco_prntid,
meat_id,
meat_inht,
level-1 "level",
sys_connect_by_path(meat_id, ' ') meat_id2
from taco
start with taco_prntid is null
connect by prior taco_id = taco_prntid
Results:
| TACO_ID | TACO_NAME | TACO_PRNTID | MEAT_ID | MEAT_INHT | LEVEL | MEAT_ID2 |
|---------|-----------|-------------|---------|-----------|-------|----------|
| 1 | 1 | (null) | 1 | N | 0 | 1 |
| 2 | 1.1 | 1 | (null) | Y | 1 | 1 |
| 3 | 1.1.1 | 2 | (null) | N | 2 | 1 |
| 6 | 1.1.2 | 2 | (null) | Y | 2 | 1 |
| 4 | 1.2 | 1 | 2 | N | 1 | 1 2 |
| 5 | 1.2.1 | 4 | (null) | Y | 2 | 1 2 |
This is what I've ended up with so far... after applying the logic in the accepted answer. I added a few more things so that I can join the result up against my meat table. the upper case could be optimized a little bit but I am so over this part of the query so.... it's going to have to stay for now.
select x.taco_id,
x.taco_name,
x.taco_prntname,
meat_id
,case when to_number(regexp_substr(meat_id,'\d+\s*$'))=0 then null else
to_number(regexp_substr(meat_id,'\d+\s*$')) end as meat_id
from ( select taco_id,
taco_name,
taco_prntname,
level-1 "level",
sys_connect_by_path(
case when meat_inht='N' then nvl(to_char(meat_id),'0') else '' end
,' ') meat_id
from taco join jobdtl on jobdtl.jobdtl_id=taco.jobdtl_id
start with taco_prntid is null
connect by prior taco_id = taco_prntid
) x
(do you ever wonder, when you read questions like this, what the real schema is? obviously I am not working on a taco project. or does it even matter as long as the general relationships and concept is preserved?)

Joining tables with same column names - ORACLE

I am using Oracle.
I am currently working one 2 tables which both have the same column names. Is there any way in which I can combine the 2 tables together as they are?
Simple example to show what I mean:
TABLE 1:
| COLUMN 1 | COLUMN 2 | COLUMN 3 |
----------------------------------------
| a | 1 | w |
| b | 2 | x |
TABLE 2:
| COLUMN 1 | COLUMN 2 | COLUMN 3 |
----------------------------------------
| c | 3 | y |
| d | 4 | z |
RESULT THAT I WANT:
| COLUMN 1 | COLUMN 2 | COLUMN 3 |
----------------------------------------
| a | 1 | w |
| b | 2 | x |
| c | 3 | y |
| d | 4 | z |
Any help would be greatly appreciated. Thank you in advance!
You can use the union set operator to get the result of two queries as a single result set:
select column1, column2, column3
from table1
union all
select column1, column2, column3
from table2
union on its own implicitly removes duplicates; union all preserves them. More info here.
The column names don't need to be the same, you just need the same number of columns with the same datatpes, in the same order.
(This is not what is usually meant by a join, so the title of your question is a bit misleading; I'm basing this on the example data and output you showed.)

Resources