Oracle SQL: Retrieving a record more than once - sql

I'm using Oracle 11 and would like to be able to retrieve a record more than one in a query, which would be a good convenience saving for the next part of my code.
Let's consider this SQL statement:
SELECT ID, NAME FROM PEOPLE WHERE NAME IN ('Alice', 'Bob', 'Alice');
It returns this data:
| 1 | Alice |
| 2 | Bob |
What I'd really like to do is to un-uniquify that list and return the records with duplicates, in the order given. So the above statement would be:
| 1 | Alice |
| 2 | Bob |
| 1 | Alice |
I appreciate that Oracle is optimized to remove repetition like this, and I could re-use the data afterwards, keep it in a store object and retrieve by name etc. I was just wondering if there was a way to make this happen on the database itself.

Oracle has a couple of handy built-in functions that return lists of arguments that you can then transform to a table and join on it. In your case, odcivarchar2list can be used to return a list of varchar2s:
SELECT p.*
FROM TABLE(sys.odcivarchar2list('Alice', 'Bob', 'Alice')) dups
JOIN people p ON p.name = dups.column_value*

query below for record with duplicate
select x.id,x.name from (
select a.id,a.name from people a where a.name in ('Alice')
union all
select a.id,a.name from people a where a.name in ('Bob')
union all
select a.id,a.name from people a where a.name in ('Alice')
) x

Late to the party but just wanted to add you can use a traditional table expression:
select p.id, p.name
from (
select 'Alice' as name from dual
union all select 'Bob' from dual
union all select 'Alice' from dual
) searched s
join people p on p.name = s.name;

Here's another idea:
WITH cteNumbers as (SELECT LEVEL AS N
FROM DUAL
CONNECT BY LEVEL <= 2),
PEOPLE AS (SELECT 'Bob' AS NAME, 111 AS EMPID FROM DUAL UNION ALL
SELECT 'Carol' AS NAME, 222 AS EMPID FROM DUAL UNION ALL
SELECT 'Ted' AS NAME, 333 AS EMPID FROM DUAL UNION ALL
SELECT 'Alice' AS NAME, 444 AS EMPID FROM DUAL)
SELECT *
FROM PEOPLE p
CROSS JOIN cteNumbers
WHERE 1 = CASE
WHEN NAME = 'Alice' THEN 1
WHEN NAME = 'Bob' AND N = 1 THEN 1
WHEN NAME = 'Ted' AND N < 4 THEN 1
WHEN NAME = 'Carol' AND N = 3 THEN 1
ELSE 0
END
ORDER BY NAME, N
Basically, use cteNumbers to generate a list of number (in this case, from 1 to 2 - adjust the CONNECT BY LEVEL condition to control how many numbers are generated), then use the CASE expression in the WHERE clause to control the circumstances under which a particular record's repetitions are selected.
SQLFiddle here

Related

SQL - Problem with a query to select with some condition

I have this two tables, in the first (WRITES) there are the books that are stored in the db (DOI indetifies the book code and ORCID the author of the book, in the second (QUOTES) there are the quotes on each book).
The result I am trying to achieve is to have the identification codes of the authors who in their books have mentioned books written by other authors and not by themselves
WRITES
Doi Orcid
1 100
2 200
3 300
4 100
QUOTES
Doi DoiMentioned
1 4
2 3
3 4
Expected output:
ORCID Doi DoiMentioed
200 2 3
300 3 4
My code:
(select w.orcid
from writes w
join quotes q
on q.doi = w.doi
minus
select w.orcid
from writes w
join quotes q
on q.doimentioned = w.doi);
I tried also with inner join but the result wasn't what I hoped
Inner joins should work, but you need to use the WRITES table twice: once for the mentioning author and once for the mentioned author. Then compare them to make sure they're different.
SELECT w.orcid, w.doi, q.doimentioned
FROM writes w
INNER JOIN quotes q on q.doi = w.doi
INNER JOIN writes w2 ON w2.doi = q.doiMentioned
WHERE w2.orcid != w.orcid;
Full example, with data from OP, to show it works:
WITH writes (doi, orcid) AS
( SELECT 1, 100 FROM DUAL UNION ALL
SELECT 2, 200 FROM DUAL UNION ALL
SELECT 3, 300 FROM DUAL UNION ALL
SELECT 4, 100 FROM DUAL ),
quotes ( Doi, DoiMentioned ) AS
( SELECT 1,4 FROM DUAL UNION ALL
SELECT 2,3 FROM DUAL UNION ALL
SELECT 3,4 FROM DUAL )
SELECT w.orcid, w.doi, q.doimentioned
FROM writes w
INNER JOIN quotes q on q.doi = w.doi
INNER JOIN writes w2 ON w2.doi = q.doiMentioned
WHERE w2.orcid != w.orcid;
+-------+-----+--------------+
| ORCID | DOI | DOIMENTIONED |
+-------+-----+--------------+
| 200 | 2 | 3 |
| 300 | 3 | 4 |
+-------+-----+--------------+

Jumbling the data within the table

I have a scenario, where i have to mask the data with data within the table
let's say I have a table student_details(ID, CODE, NAME)
1 A XYZ
2 A 123
3 A QWERTY
I want the output as
1 A QWERTY
2 A XYZ
3 A 123
I want the name to be within the name list in that table
for same id I Want different name which is in the table.
select * from emp_details order by dbms_random.value;
is giving some random names which are not in list.
Can any one help me with this?
Here's one option: recalculate the ID value using ROW_NUMBER analytic function which orders rows by the hash value over concatenated name, code and id columns (that's just for example; you can pick something different).
SQL> with test (id, code, name) as
2 (select 1, 'A', 'XYZ' from dual union all
3 select 2, 'A', '123' from dual union all
4 select 3, 'A', 'QUERTY' from dual
5 ),
6 inter as
7 (select row_number() over (order by ora_hash(name || code || id)) id,
8 code, name
9 from test
10 )
11 select t.id, t.code, i.name
12 from test t join inter i on t.id = i.id;
ID C NAME
---------- - ------
1 A XYZ
2 A QUERTY
3 A 123
SQL>
If you intend to permutate selected columns in your table and leave the rest of the table unchanged, you may use a join with a key permutation table.
Assume your data as follows:
ID CODE NAME
---------- ---- ------
1001 A XYZ
1002 B 123
1004 C QUERTY
1005 A FOO
Note, that the PK is not continuous, wich is the generall case. If you have the PK a continuous sequence starting with 1, you may even simplify the solution (as proposed in other answer).
First lets define the permutation table assigning to each PK a new key in random order.
create table PERM as
with rn as (
select
id,
row_number() over (order by id) rn,
row_number() over (order by dbms_random.value) rn_new
from student)
select a.ID, b.ID ID_NEW
from rn a
join rn b
on a.RN = b.RN_NEW;
ID ID_NEW
---------- ----------
1001 1004
1002 1001
1004 1005
1005 1002
The query defines two row_number sequences, first in the order of the PK, second in random order. The final join gets the original and new (permutated) IDs.
Now to permute a selected colums is as easy as to join your table twice with the permutation table in between and choose preserved columns from the first table, the permuted columns from the second one.
select a.ID, a.code, b.name
from student a
join PERM p on a.id = p.id
join student b on p.id_new = b.id
order by a.id;
ID CODE NAME
---------- ---- ------
1001 A QUERTY
1002 B XYZ
1004 C FOO
1005 A 123
As far as you preserv the permutation table you can reconstruct the former state, if you drop it, there is no way to get the original data.

Find two employees having the same designation attribute - in SQL using self joins [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
Display records having the same designation as "Peter" using inner join
CONDITION : without directly using the designation of Peter in the WHERE CLAUSE.
Try following:
SELECT t2.Name, t2.Designation
FROM table1 t1
INNER JOIN table1 t2
ON t1.Designation = t2.Designation
WHERE t1.Name = 'Peter'
FETCH NEXT 2 ROWS ONLY
It joins 2 tables (same table) on column Designation, filter the result for peter's designation and fetches first 2 rows only.
I guess technically since you have CONDITION : without directly using the designation of Peter in the WHERE CLAUSE. you could use:
SELECT *
FROM mytable t1
INNER JOIN mytable t2 ON t1.condition = t2.condition
AND t2.name = 'Peter'
You have records matching Peter's without using "Peter" in a WHERE clause.
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE table1 (name varchar(10), condition1 varchar(10));
INSERT INTO table1 (name, condition1)
SELECT 'Peter', 'c1' FROM dual UNION ALL
SELECT 'Paul', 'c2' FROM dual UNION ALL
SELECT 'Mary', 'c1' FROM dual UNION ALL
SELECT 'Jay', 'c2' FROM dual UNION ALL
SELECT 'SilentBob', 'c1' FROM dual UNION ALL
SELECT 'Bill', 'c1' FROM dual UNION ALL
SELECT 'Ted', 'c4' FROM dual UNION ALL
SELECT 'Rufus', 'c3' FROM dual
;
Query 1:
SELECT *
FROM table1 t1
INNER JOIN table1 t2 ON t1.condition1 = t2.condition1
AND t2.name = 'Peter'
Results:
| NAME | CONDITION1 | NAME | CONDITION1 |
|-----------|------------|-------|------------|
| Peter | c1 | Peter | c1 |
| Mary | c1 | Peter | c1 |
| SilentBob | c1 | Peter | c1 |
| Bill | c1 | Peter | c1 |
Assuming you have something like table1 table1_name,attribute1,attribute2 and a table like table2_name, attribute1,..., and the name attribute would be like a id (unqiue through the table)
then
SELECT * FROM table1 inner join table2 on table1_name = table2_name
Although this query gives result as per your criteria without where clouse please note that this query won't be good in terms of performance and I would not suggest you to use this on production.
Select * from employee e
Inner join designation d on (e.designationid=
d.designationid ) and (e.designation =(select
distinct designation from employee e1 inner
join designation d1 on (e1.designationid
=d1.designationid ) and e.name like 'peter' )
The inner join selects matching rows from both the tables. To understand the problem here, try the below SQL.
SELECT a.empname as a_empname, b.empname b_empname
FROM employee a
JOIN employee b ON(a.designation = b.designation);
I have three employees Adam, Peter and Smith in my table and Peter and Smith have the same designation. The above query returned the below records:
a_empname b_empname
Adam Adam
Smith Smith
Peter Smith
Smith Peter
Peter Peter
To get the required result if you select the column a.empname, you should filter on the column b.empname and vice versa.
so either of the following queries will work:
SELECT a.empname
FROM employee a
JOIN employee b ON(a.designation = b.designation)
WHERE b.empname="Peter";
SELECT b.empname
FROM employee a
JOIN employee b ON(a.designation = b.designation)
WHERE a.empname="Peter";

Ranking of a tuple in another table

So I have 2 tables, team A and team B, with their score. I want the rank of the score of every member of team A within team B using SQL or vertica, as shown below
Team A Table
user score
-------------
asa 100
bre 200
cqw 50
duy 50
Team B Table
user score
------------
gfh 20
ewr 80
kil 70
cvb 90
Output:
Team A Table
user score rank in team B
------------------------------
asa 100 1
bre 200 1
cqw 50 4
duy 50 4
Try this - and this only works in Vertica.
INTERPOLATE PREVIOUS VALUE is an outer-join predicate specific to Vertica that joins two tables on non-equal columns, using the 'last known' value in the outer-joined table to make a match succeed.
WITH
-- input, don't use in query itself
table_a (the_user,score) AS (
SELECT 'asa',100
UNION ALL SELECT 'bre',200
UNION ALL SELECT 'cqw',50
UNION ALL SELECT 'duy',50
)
,
table_b(the_user,score) AS (
SELECT 'gfh',20
UNION ALL SELECT 'ewr',80
UNION ALL SELECT 'kil',70
UNION ALL SELECT 'cvb',90
)
-- end of input - start WITH clause here
,
ranked_b AS (
SELECT
RANK() OVER(ORDER BY score DESC) AS the_rank
, *
FROM table_b
)
SELECT
a.the_user AS a_user
, a.score AS a_score
, b.the_rank AS rank_in_team_b
FROM table_a a
LEFT JOIN ranked_b b
ON a.score INTERPOLATE PREVIOUS VALUE b.score
ORDER BY 1
;
a_user|a_score|rank_in_team_b
asa | 100| 1
bre | 200| 1
cqw | 50| 4
duy | 50| 4
Simple correlated query should do:
select
a.*,
(select count(*) + 1 from table_b b where b.score > a.score) rank_in_b
from table_a a;
All you need to do is count the number of people with more score than current user in the table b and add 1 to it to get the rank.

Why outer join query not working?

Hallo,
my objective is to generate a table that shows the total of each CODE that belong to the owner, take note that each owner must have a CODE tied to it no matter the TOTAL value is zero. So there will be APP, REJ, CAN tied to each of the APPROVAL_ID.
APPROVAL_ID CODE TOTAL
----------- ---- -----
101 APP 2
101 REJ 1
101 CAN 3
102 APP 2
102 REJ 4
102 CAN 0
103 APP 0
103 REJ 0
103 CAN 4
Thus, here is the source code:
select approval_id, code, total
from (
select 'APP' code, '1' seq from dual
union all
select 'REJ' code, '2' seq from dual
union all
select 'CAN' code, '3' seq from dual
)
left outer join (
select m.approval_id, own.name, m.decision, count(*) total,
case own.channel
when 'CH1' then 'CH1'
when 'CH2' then 'CH2'
else 'Others Channel'
end the_channel
from tableM m, owner own
where m.decision in ('REJ', 'APP', 'CAN')
and own.id=m.approval_id
group by m.approval_id, own.name, m.decision, own.channel
order by m.approval_id
)
on code=decision
group by approval_id, code, total
order by approval_id;
The output from the above query is like below:
APPROVAL_ID CODE TOTAL
----------- ---- -----
101 APP 2
101 REJ 1
101 CAN 3
102 APP 2
102 REJ 4
103 CAN 4
The output of the inner query is like below:
APPROVAL_ID CODE TOTAL
----------- ---- -----
101 APP 2
101 REJ 1
101 CAN 3
102 APP 2
102 REJ 4
103 CAN 4
Something was not right to the query because I know that some of the row is having total value of zero, it should print something like (null) value in it. But why does it hidden from the view? Is there anything wrong to my query?
THanks #!
First, you need to do a cross join between your owner and your code table.
Then you do the left join.
I have modelised 3 table : Type for your 3 lines APP, REJ and CAN, then a user table, equivalent to your owner table, and a third table decision, equivalent to your tableM.
The query looks like this :
SELECT c.user_id, c.type_code, COUNT(d.id)
FROM
(
SELECT t.ID as type_id, u.id as user_id, t.CODE as type_code
FROM Type t, Userr u
) c
LEFT OUTER JOIN Decision d
ON d.user_id = c.user_id
AND d.type_id = c.type_id
GROUP BY c.user_id, c.type_code
Not tested but for yours set of table :
select a.id_own, a.code, count(m.approval_id)
from
(
select code, own.id as id_own
from (
select 'APP' code, '1' seq from dual
union all
select 'REJ' code, '2' seq from dual
union all
select 'CAN' code, '3' seq from dual
) , owner own
) a
left outer join tableM m
on a.code = m.decision
and a.id_own = m.approval_id
group by a.id_own, a.code
order by a.id_own
Note that the count(m.approval_id) will give you the number of approval_id that appear not null in the left join.
Your outer join is on code=decision. That means you get one row for each codes which don't occur as decision on the right side. Obviously you want to do your left join with a cross join of 3 codes and all distinct APPROVAL_IDs giving all the combinations of code and APPROVAL_ID on the left side.
My first guess is that
group by approval_id, code, total
should be
group by approval_id, code