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>
Related
Table 1
where we have ID as primary key and his own salary
ID
Name
Salary
1
" X "
500
2
" Y "
1000
3
" Z "
1500
table 2
where we have data from the payment system
ID
Date
Salary
1
" 6/22/2020 "
500
2
" 6/25/2020 "
1000
3
" 8/05/2021 "
1500
i want a query to compare items from table 2 with table 1 where my goal is to make sure every employee gets his exact salary paid as it in table 1
example
employee "1" with salary 500 let say on a month he received only 300 want to retrieve employee
* preferred : calculate how many months out of his employment time where he didn't get his exact salary and calculate how much difference $
* Note : the employee might get paid less or more than his salary
Something like this should do it:
select
t1.salary as t1_salary,
t2.*
from
t1 left outer join t2 on t1.id = t2.id
Join tables on ID and put the difference into the where clause.
Sample data (you have it & you don't have to type that):
SQL> with
2 table1 (id, name, salary) as
3 (select 1, 'x', 500 from dual union all
4 select 2, 'y', 1000 from dual
5 ),
6 table2 (id, datum, salary) as
7 (select 1, date '2022-01-01', 500 from dual union all
8 select 1, date '2022-02-01', 300 from dual union all
9 select 1, date '2022-03-01', 500 from dual union all
10 select 1, date '2022-04-01', 700 from dual union all
11 select 2, date '2022-01-01', 1000 from dual union all
12 select 2, date '2022-02-01', 1000 from dual union all
13 select 2, date '2022-03-01', 900 from dual union all
14 select 2, date '2022-04-01', 1200 from dual
15 )
Query begins here:
16 select a.id,
17 a.name,
18 b.datum,
19 b.salary - a.salary as difference
20 from table1 a join table2 b on a.id = b.id
21 where b.salary - a.salary <> 0;
ID NAME DATUM DIFFERENCE
---------- ---- ---------- ----------
1 x 01.02.2022 -200
1 x 01.04.2022 200
2 y 01.03.2022 -100
2 y 01.04.2022 200
SQL>
I have a query which gives me 2 columns,
select
name as name,
code as code
from
table1
UNION
select
name as name,
code as code
from
table2
I would like to apply one more union in the result if from the above query i did not get a row with name as 'Default'. So if above query didnt have a record with default name i need to have 1 more union with above query:
select
'Default' as name,
code as code
from
table1
where condition = condition
I tried putting first query in a view and use not exists function in second query but it gives column not found error.
This is how I understood the question: lines #1 - 9 represent sample data; one of rows contains the Default name, so your resulting query should return the union-ed result as is:
SQL> with
2 table1 (name, code) as
3 (select 'Little', 1 from dual union all
4 select 'Foot' , 2 from dual
5 ),
6 table2 (name, code) as
7 (select 'Default', 3 from dual union all --> Default is here
8 select 'Oracle' , 4 from dual
9 ),
10 -- the "original" union
11 oriun as
12 (select name, code from table1
13 union
14 select name, code from table2
15 )
16 select name, code from oriun
17 union
18 select 'Default' name, null code from table1
19 where not exists (select null from oriun
20 where name = 'Default'
21 )
22 order by code;
NAME CODE
------- ----------
Little 1
Foot 2
Default 3
Oracle 4
SQL>
But, if there's no Default in those tables (see change made in line #7), then you'd get an "extra" Default row:
SQL> with
2 table1 (name, code) as
3 (select 'Little', 1 from dual union all
4 select 'Foot' , 2 from dual
5 ),
6 table2 (name, code) as
7 (select 'xxx', 3 from dual union all --> No more Default here
8 select 'Oracle' , 4 from dual
9 ),
10 -- the "original" union
11 oriun as
12 (select name, code from table1
13 union
14 select name, code from table2
15 )
16 select name, code from oriun
17 union
18 select 'Default' name, null code from table1
19 where not exists (select null from oriun
20 where name = 'Default'
21 )
22 order by code;
NAME CODE
------- ----------
Little 1
Foot 2
xxx 3
Oracle 4
Default
SQL>
When you have order by in union you need to put it inside a view or create a block with select * from (.. order by)
This is somewhat a complex problem to describe, but I'll try to explain it with an example. I thought I would have been able to use the Oracle Instr function to accomplish this, but it does not accept queries as parameters.
Here is a simplification of my data:
Table1
Person Qualities
Joe 5,6,7,8,9
Mary 7,8,10,15,20
Bob 7,8,9,10,11,12
Table2
Id Desc
5 Nice
6 Tall
7 Short
Table3
Id Desc
8 Angry
9 Sad
10 Fun
Table4
Id Desc
11 Boring
12 Happy
15 Cool
20 Mad
Here is somewhat of a query to give an idea of what I'm trying to accomplish:
select * from table1
where instr (Qualities, select Id from table2, 1,1) <> 0
and instr (Qualities, select Id from table3, 1,1) <> 0
and instr (Qualities, select Id from table3, 1,1) <> 0
I'm trying to figure out which people have at least 1 quality from each of the 3 groups of qualities (tables 2,3, and 4)
So Joe would not be returned in the results because he does not have the quality from each of the 3 groups, but Mary and Joe would since they have at least 1 quality from each group.
We are running Oracle 12, thanks!
Here's one option:
SQL> with
2 table1 (person, qualities) as
3 (select 'Joe', '5,6,7,8,9' from dual union all
4 select 'Mary', '7,8,10,15,20' from dual union all
5 select 'Bob', '7,8,9,10,11,12' from dual
6 ),
7 table2 (id, descr) as
8 (select 5, 'Nice' from dual union all
9 select 6, 'Tall' from dual union all
10 select 7, 'Short' from dual
11 ),
12 table3 (id, descr) as
13 (select 8, 'Angry' from dual union all
14 select 9, 'Sad' from dual union all
15 select 10, 'Fun' from dual
16 ),
17 table4 (id, descr) as
18 (select 11, 'Boring' from dual union all
19 select 12, 'Happy' from dual union all
20 select 15, 'Cool' from dual union all
21 select 20, 'Mad' from dual
22 ),
23 t1new (person, id) as
24 (select person, regexp_substr(qualities, '[^,]+', 1, column_value) id
25 from table1 cross join table(cast(multiset(select level from dual
26 connect by level <= regexp_count(qualities, ',') + 1
27 ) as sys.odcinumberlist))
28 )
29 select a.person,
30 count(b.id) bid,
31 count(c.id) cid,
32 count(d.id) did
33 from t1new a left join table2 b on a.id = b.id
34 left join table3 c on a.id = c.id
35 left join table4 d on a.id = d.id
36 group by a.person
37 having ( count(b.id) > 0
38 and count(c.id) > 0
39 and count(d.id) > 0
40 );
PERS BID CID DID
---- ---------- ---------- ----------
Bob 1 3 2
Mary 1 2 2
SQL>
What does it do?
lines #1 - 22 represent your sample data
T1NEW CTE (lines #23 - 28) splits comma-separated qualities into rows, per every person
final select (lines #29 - 40) are outer joining t1new with each of "description" tables (table2/3/4) and counting how many qualities are contained in there for each of person's qualities (represented by rows from t1new)
having clause is here to return only desired persons; each of those counts have to be a positive number
Maybe this will help:
{1} Create a view that categorises all qualities and allows you to SELECT quality IDs and categories . {2} JOIN the view to TABLE1 and use a join condition that "splits" the CSV value stored in TABLE1.
{1} View
create or replace view allqualities
as
select 1 as category, id as qid, descr from table2
union
select 2, id, descr from table3
union
select 3, id, descr from table4
;
select * from allqualities order by category, qid ;
CATEGORY QID DESCR
---------- ---------- ------
1 5 Nice
1 6 Tall
1 7 Short
2 8 Angry
2 9 Sad
2 10 Fun
3 11 Boring
3 12 Happy
3 15 Cool
3 20 Mad
{2} Query
-- JOIN CONDITION:
-- {1} add a comma at the start and at the end of T1.qualities
-- {2} remove all blanks (spaces) from T1.qualities
-- {3} use LIKE and the qid (of allqualities), wrapped in commas
--
-- inline view: use UNIQUE, otherwise we may get counts > 3
--
select person
from (
select unique person, category
from table1 T1
join allqualities A
on ',' || replace( T1.qualities, ' ', '' ) || ',' like '%,' || A.qid || ',%'
)
group by person
having count(*) = ( select count( distinct category ) from allqualities )
;
-- result
PERSON
Bob
Mary
Tested w/ Oracle 18c and 11g. DBfiddle here.
So I have got two columns in an Oracle database:
Name / count
I would like to print the name x times, x being the count.
E.g. Paul / 5 would mean Paul being printed 5 times.
Sam / 6 would mean Sam being printed 6 times
Tried row_number over but not sure how it works?
You can use connect by as following:
SQL> WITH YOUR_TABLE AS
2 (SELECT 'paul' as NAME, 5 AS COUNT FROM DUAL UNION ALL
3 SELECT 'sam' as NAME, 6 AS COUNT FROM DUAL
4 ) -- YOUR ACTUAL QUERY STARTS FROM LINE#5
5 Select t.name, m.lvl
6 from your_table t
7 join
8 (Select level as lvl
9 from
10 (Select max(count) as maxcount
11 from your_table)
12 Connect by level <= maxcount) m
13 On (t.count >= m.lvl)
14 ORDER BY 1,2;
NAME LVL
---- ----------
paul 1
paul 2
paul 3
paul 4
paul 5
sam 1
sam 2
sam 3
sam 4
sam 5
sam 6
11 rows selected.
SQL>
Cheers!!
you need recursive query to achieve this.
with cte(nam, ctr) as (
select 'Paul' as nam, 5 as ctr from dual
union all
select 'Sam', 6 as ctr from dual
),
cte2(nam, ct, ctr) as (
select nam, 1 as ct, ctr from cte
union all
select nam, ct + 1, ctr from cte2
where ct<ctr
)select nam, ct from cte2
order by nam asc
output:
See sqlfiddle
this will work:
select name
from Table1,
(select level lvl
from dual
connect by level <= (select max(cnt) from Table1 )
)
where lvl <= cnt
order by name;
check fiddle:http://sqlfiddle.com/#!4/14a67/1
Thanks!!!
Yet another option (your query starts at line #4):
SQL> with your_table as
2 (select 'paul' as name, 5 as count from dual union all
3 select 'sam' as name, 6 as count from dual)
4 select name
5 from your_table cross join table (cast (multiset (select level from dual
6 connect by level <= count
7 ) as sys.odcinumberlist));
NAME
----
paul
paul
paul
paul
paul
sam
sam
sam
sam
sam
sam
11 rows selected.
SQL>
You can use a connect by level <= some_number logic containing cross join to link with your table tab :
with tab(Name,"count") as
( select 'Paul', 5 from dual union all select 'Sam', 6 from dual )
select name, level as seq
from dual d
cross join tab t
connect by level <= t."count"
and prior name = name
and prior sys_guid() is not null;
Demo
I have a table that has two columns. Employee_id (which is unique per employee) and next column for employee skillset. One employee can have multiple skillset. How do I retrieve the list of distinct employees who don't have skillset 'c' if A,B,C,D,E are the five types of skillset that employees can have.
employee_id skillset
1 A
1 C
2 E
3 A
3 B
3 C
4 D
4 C
5 B
I have tried self join and other methods but it is not working.
select distinct employee_id from employee_skillset where skillset not like 'C'
When I run my query, it is still giving me employee_ids that have skillset of "c"
You can group by employee_id and set a condition in the HAVING clause:
select employee_id
from employee_skillset
group by employee_id
having sum(case when skillset = 'C' then 1 else 0 end) = 0
Or with NOT EXISTS:
select distinct s.employee_id
from employee_skillset s
where not exists (
select 1 from employee_skillset
where employee_id = s.employee_id and skillset = 'C'
)
What are your expected results from your data set? 2 and 5?
Why not something like below
SELECT DISTINCT employee_id
FROM Table1
WHERE skillset <> 'C';
MINUS set operator is one option:
SQL> with employee_skillset (employee_id, skillset) as
2 (select 1, 'a' from dual union all
3 select 1, 'c' from dual union all
4 select 2, 'e' from dual union all
5 select 3, 'a' from dual union all
6 select 3, 'b' from dual union all
7 select 3, 'c' from dual union all
8 select 4, 'd' from dual union all
9 select 4, 'c' from dual union all
10 select 5, 'b' from dual
11 )
12 select employee_id from employee_skillset
13 minus
14 select employee_id from employee_skillset where skillset = 'c';
EMPLOYEE_ID
-----------
2
5
SQL>
Yet another option:
<snip>
12 select employee_id
13 from (select employee_id,
14 case when skillset = 'c' then 1 else 0 end flag
15 from employee_skillset
16 )
17 group by employee_id
18 having sum(flag) = 0;
EMPLOYEE_ID
-----------
2
5
SQL>
Or:
<snip>
12 select employee_id
13 from (select employee_id,
14 listagg(skillset, ',') within group (order by null) lagg
15 from employee_skillset
16 group by employee_id
17 )
18 where instr(lagg, 'c') = 0;
EMPLOYEE_ID
-----------
2
5
SQL>