How to print missing rows in a table - sql

How to print the misisng rows from a table from expected set of values.
I am expecting the values A, B, C, D, E to be in the below employee table, if any of the value is missing, how to get the misisng record.
EMP_NAME STATUS
-------- --------
A VALID
B VALID
D VALID
In the above table C & E are missing. How can I write a sql to print that C & E are missing rows from the table

For a sample table
SQL> select * from test;
EMP_NAME STATUS
---------- ------
A VALID
B VALID
C VALID
you have to know which employees are missing. I hope you have a table that contains the full list; I created a CTE that serves that purpose, having only the first 5 letters of English alphabet (I presume you used dummy names, didn't you?). Then you'd outer join it to table you already have. Something like this:
SQL> with all_emps as
2 (select chr(65 + level - 1) emp_name
3 from dual
4 connect by level <= 5
5 )
6 select a.emp_name,
7 nvl(t.status, 'Unknown') status
8 from all_emps a left join test t on t.emp_name = a.emp_name
9 order by a.emp_name;
EMP_NAME STATUS
---------- -------
A VALID
B VALID
C VALID
D Unknown
E Unknown
SQL>
As it seems that you do know which employees you have, then - by modifying above dummy query - you'd have
SQL> select * from test;
EMP_NAME STATUS
---------- ----------
CAM VALID
POOD VALID
HAM VALID
SQL> with all_emps (emp_name) as
2 (select 'CAM' from dual union all
3 select 'POOD' from dual union all
4 select 'HAM' from dual union all
5 select 'HAL' from dual union all
6 select 'NIL' from dual
7 )
8 select a.emp_name,
9 nvl(t.status, 'Unknown') status
10 from all_emps a left join test t on t.emp_name = a.emp_name
11 order by a.emp_name;
EMP_NAME STATUS
---------- ----------
CAM VALID
HAL Unknown
HAM VALID
NIL Unknown
POOD VALID
SQL>

Related

Filter rows in Oracle select query (and not script or procedure)

I have code in my Oracle stored procedure like this:
SELECT COUNT(EMP_ID) INTO A_VARIABLE
FROM STUDENT;
IF (A_VARIABLE > 0) THEN
SELECT * FROM DEPARTMENT1;
ELSE
SELECT * FROM DEPARTMENT2;
END IF;
Basically I have to do this same filtration of rows in my SQL query (and not in the procedure or script).
Can anyone help with this Oracle query?
I have tried multiple solutions, but I'm not getting the desired output.
For 19c (19.7 and above) you may use SQL_MACRO(table) feature and inline function declaration.
But different output structure of the same query is somewhat misleading, so I would recommend to use union all approach and align columns of two tables.
Below is an example for SQL Macro.
Setup:
create table table1
as
select
level as id,
level as val
from dual
connect by level < 5
create table table2
as
select
level as id,
lpad(level, 4, '0') as val,
dbms_random.string('x', 5) as str2
from dual
connect by level < 4
create table decision
as
select 'Y' as res
from dual
Run 1:
with function f_decide_table
return varchar2
sql_macro(table)
as
l_res varchar2(100);
begin
select
coalesce (
max(q'[select * from table1]'),
q'[select * from table2]'
)
into l_res
from decision;
return l_res;
end;
select *
from f_decide_table()
order by 1
ID
VAL
1
1
2
2
3
3
4
4
Run 2:
truncate table decision
with function f_decide_table
return varchar2
sql_macro(table)
as
l_res varchar2(100);
begin
select
coalesce (
max(q'[select * from table1]'),
q'[select * from table2]'
)
into l_res
from decision;
return l_res;
end;
select *
from f_decide_table()
order by 1
ID
VAL
STR2
1
0001
DGNY9
2
0002
UHFYH
3
0003
EU12B
fiddle
Sample student table; depending on course students take, main query will return data from one table (or another).
SQL> select * from student;
ID NAME COURSE
1 Little Web
2 Foot Web
SQL> select * from department1;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
SQL> select * from department2;
DEPTNO DNAME LOC
---------- -------------- -------------
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
Query uses a CTE that counts rows in student table; it is then used in exists subquery for two similar select statements which fetch rows from one of departments tables.
Currently, nobody is in IT so departments2 table it is:
SQL> with temp as
2 (select count(*) cnt
3 from student
4 where course = 'IT')
5 --
6 select * from department1
7 where exists (select null from temp
8 where cnt > 0
9 )
10 union all
11 select * from department2
12 where exists (select null from temp
13 where cnt = 0
14 );
DEPTNO DNAME LOC
---------- -------------- -------------
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
However, if someone studies IT, department1 will provide data:
SQL> update student set course = 'IT' where id = 1;
1 row updated.
SQL> select * from student;
ID NAME COURSE
---------- ------ ----------
1 Little IT
2 Foot Web
SQL> with temp as
2 (select count(*) cnt
3 from student
4 where course = 'IT')
5 --
6 select * from department1
7 where exists (select null from temp
8 where cnt > 0
9 )
10 union all
11 select * from department2
12 where exists (select null from temp
13 where cnt = 0
14 );
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
SQL>
Closest thing to what you want to do (using just sql) would mean that you need to know all the column names and datatypes in both tables.
Next, you would have to do the datatype conversion for one union query as you have to select same number of columns with same datatypes.
The result would have all that you need but with some empty columns (those that do exist in other table but not in one you are selecting from).
Depending on total number of columns and on differences in names and types this coulld becomee too complicated and you realy shoud think about some function and reference queries.
Anyway, lets create some sample data to work with:
WITH -- sample data
students (ID, A_NAME) AS
(
Select 1, 'Mark' From Dual
),
dept_1 (ID, DEPT_NAME, DEPT_CITY) AS
(
Select '10', 'HR', 'NEW_YORK' From Dual Union All
Select '20', 'IT', 'BOSTON' From Dual
),
dept_2 (ID, DEPARTMENT) AS
(
Select 30, 'SALES' From Dual Union All
Select 40, 'PRODUCTION' From Dual
),
-- dept_1 has 3 columns (ID VARCHAR) - same column name different datatype
-- dept_2 has 2 columns (ID NUMBER)
-- The rest of the columns have different names and same datatype
create cte (namings) - forcing union of rows for all columns from both tables - dummy filling non existant and converting To_Char(ID) to get same datatype (this is needed for union to work)
-- here are total of 5 columns coded - it can be widen more if needed, this is just a sample to show handling of extra columns
namings AS
(
SELECT ROWNUM "RN", 'dept_1' "TABLE_NAME", Count(*) "CNT", To_Char(ID) "V1", DEPT_NAME "V2", DEPT_CITY "V3", 'X' "V4", 'X' "V5", 'ID' "C1", 'DEPT_NAME' "C2", 'DEPT_CITY' "C3", 'COL_X4' "C4", 'COL_X5' "C5"
From dept_1
Where CASE WHEN (Select Count(*) "CNT" From students Where ID = 999) > 0 THEN 1 ELSE 0 END = 1
Group By ROWNUM, ID, DEPT_NAME, DEPT_CITY
Union All
SELECT ROWNUM "RN", 'dept_2' "TABLE_NAME", Count(*) "CNT", To_Char(ID) "V1", DEPARTMENT "V2", 'X' "V3", 'X' "V4", 'X' "V5", 'ID' "C1", 'DEPARTMENT' "C2", 'COL_X3' "C3", 'COL_X4' "C4", 'COL_X5' "C5"
From dept_2
Where CASE WHEN (Select Count(*) "CNT" From students Where ID = 999) > 0 THEN 1 ELSE 0 END = 0
Group By ROWNUM,ID, DEPARTMENT
),
R e s u l t when count from students = 0 (filtered out with where clause)
RN TABLE_NAME CNT V1 V2 V3 V4 V5 C1 C2 C3 C4 C5
---------- ---------- ---------- ---------------------------------------- ---------- -------- -- -- -- ---------- --------- ------ ------
2 dept_2 1 40 PRODUCTION X X X ID DEPARTMENT COL_X3 COL_X4 COL_X5
1 dept_2 1 30 SALES X X X ID DEPARTMENT COL_X3 COL_X4 COL_X5
R e s u l t when count from students > 0
RN TABLE_NAME CNT V1 V2 V3 V4 V5 C1 C2 C3 C4 C5
---------- ---------- ---------- ---------------------------------------- ---------- -------- -- -- -- ---------- --------- ------ ------
1 dept_1 1 10 HR NEW_YORK X X ID DEPT_NAME DEPT_CITY COL_X4 COL_X5
2 dept_1 1 20 IT BOSTON X X ID DEPT_NAME DEPT_CITY COL_X4 COL_X5
create another cte (transposed) to unpivot the above data
transposed AS
( SELECT * FROM namings
UNPIVOT ( (NAMES, VALS) For COL_NAME IN( (C1, V1), (C2, V2), (C3, V3), (C4, V4), (C5, V5) ) )
WHERE VALS <> 'X'
)
R e s u l t when count from students = 0
RN TABLE_NAME CNT COL_NAME NAMES VALS
---------- ---------- ---------- -------- ---------- ----------------------------------------
2 dept_2 1 C1_V1 ID 40
2 dept_2 1 C2_V2 DEPARTMENT PRODUCTION
1 dept_2 1 C1_V1 ID 30
1 dept_2 1 C2_V2 DEPARTMENT SALES
R e s u l t when count from students > 0
RN TABLE_NAME CNT COL_NAME NAMES VALS
---------- ---------- ---------- -------- ---------- ----------------------------------------
1 dept_1 1 C1_V1 ID 10
1 dept_1 1 C2_V2 DEPT_NAME HR
1 dept_1 1 C3_V3 DEPT_CITY NEW_YORK
2 dept_1 1 C1_V1 ID 20
2 dept_1 1 C2_V2 DEPT_NAME IT
2 dept_1 1 C3_V3 DEPT_CITY BOSTON
Main sql - pivoting data to columns from both tables
SELECT DISTINCT
RN,
TABLE_NAME,
MAX(ID_V) OVER(Partition By RN) "ID",
MAX(DEPT_NAME_V) OVER(Partition By RN) "DEPT_NAME",
MAX(DEPT_CITY_V) OVER(Partition By RN) "DEPT_CITY",
MAX(DEPARTMENT_V) OVER(Partition By RN) "DEPARTMENT"
FROM (
SELECT * FROM transposed
PIVOT (
COUNT(NAMES), MAX(VALS) "V" FOR NAMES IN('ID' "ID", 'DEPT_NAME' "DEPT_NAME", 'DEPT_CITY' "DEPT_CITY", 'DEPARTMENT' "DEPARTMENT")
)
)
ORDER BY RN
R e s u l t when count from students = 0
RN TABLE_NAME ID DEPT_NAME DEPT_CITY DEPARTMENT
---------- ---------- ---------------------------------------- ---------------------------------------- ---------------------------------------- ----------------------------------------
1 dept_2 30 SALES
2 dept_2 40 PRODUCTION
R e s u l t when count from students > 0
RN TABLE_NAME ID DEPT_NAME DEPT_CITY DEPARTMENT
---------- ---------- ---------------------------------------- ---------------------------------------- ---------------------------------------- ----------------------------------------
1 dept_1 10 HR NEW_YORK
2 dept_1 20 IT BOSTON

How to create a unique pairwise list of a column value based on table entry and its referenced entry in oracle

Im trying to extract the following information from the oracle table below: A list of all the unique pairwise Status combinations for entries and their referenced entries. Entries with no referenced entry will be ignored. For example, for the entry 10 I expect the output to be (1,3) because its status is 1 and the status of the referenced entry 7 is 3. If the list doesn't already have this combination, it should be added to the list. Can anyone guide me in the right direction? I'm totally clueless as to how to even google what I want to achieve.
EDIT: The first column is the ID of the entry, the second column is the status of the entry, and the third column is the ID of another entry in the same table that is referenced.
Looks like a self join:
Sample data:
SQL> with test (id, status, ref_id) as
2 (select 1, 0, null from dual union all
3 select 2, 1, 3 from dual union all
4 select 3, 3, null from dual union all
5 select 4, 6, 6 from dual union all
6 select 5, 0, 1 from dual union all
7 select 6, 4, null from dual union all
8 select 7, 3, null from dual union all
9 select 8, 5, 9 from dual union all
10 select 9, 2, null from dual union all
11 select 10, 1, 7 from dual
12 )
Query:
13 select a.id, a.status, b.status
14 from test a join test b on b.id = a.ref_id
15 where a.ref_id is not null
16 order by a.id;
ID STATUS STATUS
---------- ---------- ----------
2 1 3
4 6 4
5 0 0
8 5 2
10 1 3
SQL>
If you want to get distinct pairs (but still know IDs involved), you could use listagg (it'll work as long as resulting string doesn't exceed 4000 characters; if it does, use xmlagg instead):
13 select listagg(a.id, ', ') within group (order by a.id) id,
14 a.status, b.status
15 from test a join test b on b.id = a.ref_id
16 where a.ref_id is not null
17 group by a.status, b.status
18 order by id;
ID STATUS STATUS
-------------------- ---------- ----------
2, 10 1 3
4 6 4
5 0 0
8 5 2
SQL>
If you don't care about IDs, then
13 select distinct a.status, b.status
14 from test a join test b on b.id = a.ref_id
15 where a.ref_id is not null
16 order by a.status, b.status;
STATUS STATUS
---------- ----------
0 0
1 3
5 2
6 4
SQL>

Comparing two tables each column in oracle sql server using procedure

I Need a procedure to compare two tables of same schema but differnet data
Table 1: dep_name,emp_name,sal
Table 2: dep_name,emp_name,sal,status
Here's one option (sample data in lines #1 - 10).
(Though, I'm not sure you know which database you use. Is it Oracle or Microsoft SQL Server? I presumed it is Oracle. Also, screenshot doesn't support what you described - there's no status column in table 2, but it exists in table 3 which is - I presume (again) - result you want).
SQL> with
2 t1 (dep_name, emp_name, sal) as
3 (select 'Public', 'xxxx', 20000 from dual union all
4 select 'Drug' , 'yyyy', 20000 from dual
5 ),
6 t2 (dep_name, emp_name, sal) as
7 (select 'Drug' , 'yyyy', 10000 from dual union all
8 select 'Public', 'xxxx', 20000 from dual union all
9 select 'Police', 'zzzz', 40000 from dual
10 )
11 select b.dep_name, b.emp_name, greatest(nvl(a.sal, 0), nvl(b.sal, 0)) sal,
12 case when a.sal = b.sal then 'Matched'
13 when a.sal <> b.sal then 'Unmatched'
14 else 'New'
15 end status
16 from t2 b left join t1 a on a.dep_name = b.dep_name
17 and a.emp_name = b.emp_name;
DEP_NA EMP_ SAL STATUS
------ ---- ---------- ---------
Public xxxx 20000 Matched
Drug yyyy 20000 Unmatched
Police zzzz 40000 New
SQL>

THREE ROWS LEAD FUNCTION IN ORACLE PL SQL

My Table:
ID NAME
1 SIVA
2 RAJA
3 PYTHON
4 SQL
5 ODI
I need to lead by 3 rows.
My SQL Query:
SELECT LEAD(NAME,3) OVER (ORDER by NAME) as NAME FROM TEST_TABLE where NAME='SIVA'
EXPECTED OUTPUT:
--------
| NAME |
--------
| SQL |
--------
Example:
If I pass value as SIVA, then I need to get SQL as output.
Similarly if I pass value as Raja, then I need to get ODI as output
Is there any query to get the expected output?
This might be one option: it uses row_number analytic function which calculates row numbers so that you wouldn't have to rely on ID values. What if they are acquired by a sequence? It is not gapless. Basically - it is used for safety.
SQL> WITH test (id, name)
2 AS (SELECT 1, 'siva' FROM DUAL
3 UNION ALL
4 SELECT 2, 'raja' FROM DUAL
5 UNION ALL
6 SELECT 3, 'python' FROM DUAL
7 UNION ALL
8 SELECT 4, 'sql' FROM DUAL
9 UNION ALL
10 SELECT 5, 'odi' FROM DUAL),
11 temp AS (SELECT id, name, ROW_NUMBER () OVER (ORDER BY id) rn FROM test)
12 SELECT b.name
13 FROM temp a JOIN temp b ON b.rn = a.rn + 3
14 WHERE a.name = '&name';
Enter value for name: siva
NAME
------
sql
SQL> /
Enter value for name: raja
NAME
------
odi
SQL> /
Enter value for name: sql
no rows selected
SQL>
You can simply use sub-query :
SELECT Next_NAME
FROM (SELECT NAME, LEAD(NAME, 3) OVER (ORDER by id) AS Next_NAME
FROM TEST_TABLE
) t
WHERE NAME = 'SIVA';

rows convert in oracle sql

I have values like below
1,a,b,c
2,d,e
3,f,g
Expected output
1 a
1 b
1 c
2 d
2 e
Can you please help me?
It is very much close to implementing the logic to Split comma delimited strings in a table. The only tricky thing is that you have the row number along with the string itself.
You could use ROWNUM as pseudo column, and then filter out those rows where the leading substr of the string is repeating with the ROWNUM.
For example,
Setup
SQL> CREATE TABLE t(text VARCHAR2(4000));
Table created.
SQL>
SQL> INSERT INTO t SELECT '1,a,b,c' text FROM dual;
1 row created.
SQL> INSERT INTO t SELECT '2,d,e' text FROM dual;
1 row created.
SQL> INSERT INTO t SELECT '3,f,g' text FROM dual;
1 row created.
SQL> COMMIT;
Commit complete.
SQL>
SQL> SELECT * FROM t;
TEXT
----------
1,a,b,c
2,d,e
3,f,g
SQL>
Solution:
SQL> WITH DATA AS(
2 SELECT ROWNUM rn, text FROM t
3 )
4 SELECT *
5 FROM
6 (SELECT rn,
7 trim(regexp_substr(t.text, '[^,]+', 1, lines.COLUMN_VALUE)) text
8 FROM DATA t,
9 TABLE (CAST (MULTISET
10 (SELECT LEVEL FROM dual CONNECT BY LEVEL <= regexp_count(t.text, ',')+1
11 ) AS sys.odciNumberList ) ) lines
12 )
13 WHERE TO_CHAR(rn) <> text
14 ORDER BY rn
15 /
RN TEXT
---------- ----------
1 a
1 b
1 c
2 d
2 e
3 f
3 g
7 rows selected.
SQL>