SQL - Problem with a query to select with some condition - sql

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

Related

Attributed null values to each ID in Athena (Presto)

Here is what my initial dataset looks like
prof_id id title
1 5 A
1 5 B
1 5 C
1 5 D
2 5 C
2 5 D
2 5 E
NA 5 F
NA 5 G
Here is what the new table should look like:
prof_id id title
1 5 A
1 5 B
1 5 C
1 5 D
1 5 F
1 5 G
2 5 C
2 5 D
2 5 E
2 5 F
2 5 G
Any row with a null value for a prof_id should be attributed to all of the prof_id. I have provided an example where there are two '
prof_id but there are also instances where there are 1 or 0 prof_id.
For 1, all of the null should be attributed to that single prof_id
For 0, leave it as is
I'm new to SQL so I'm not sure how to start. Any guidance would be much appreciated.
Thanks
In this case, you will need to do cross join, where essentially it is going to multiply 2 tables together.
First to pick out all nulls:
select id, title from table where prof_id is null
Then pick out the prof_id you want to apply to all tables
select distinct prof_id from table where prof_is is not null
Do a cross join together, then union the rest of "good" data back
(select distinct prof_id from table where prof_is is not null)
CROSS JOIN
(select id, title from table where prof_id is null)
UNION ALL
(select prof_id, id, title from table where prof_id is not null)
You can generate all the rows using a cross join. Then use union all to combine this with the rest of the data.
The following syntax should work:
select p.prof_id, i.id, t.title
from (select distinct prof_id
from t
where prof_id <> 'NA' -- or do you mean is not null
) p cross join
(select distinct id from t) i cross join
(select distinct title
from t
where prof_id = 'NA' -- or is null
) t
union all
select prof_id, id, title
from t
where prof_id <> 'NA' -- or is not null

Oracle SQL: Retrieving a record more than once

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

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.

comparing rows in sql on two different columns

id address retailer
1 A 11
2 A 11
3 A 11
4 A 12
5 A 13
6 B 12
7 B 12
8 B 13
My output should be
id address retailer
1 A 11
4 A 12
5 A 13
6 B 12
8 B 13
i.e my query should return id's which have same address but not same retailer.
How toget this?
Try to use group by clause as below:
select min(id), address, retailer
from tab
group by address, retailer
Assuming you're joining on columns with no duplicates, which is by far the most common case:
An inner join of A and B gives the result of A intersect B, i.e. the inner part of a venn diagram intersection.
An outer join of A and B gives the results of A union B, i.e. the outer parts of a venn diagram union.
Examples:
Suppose you have two Tables, with a single column each, and data as follows:
A B
- -
1 3
2 4
3 5
4 6
Note that (1,2) are unique to A, (3,4) are common, and (5,6) are unique to B.
Inner join:
An inner join using either of the equivalent queries gives the intersection of the two tables, i.e. the two rows they have in common.
select *
from a
INNER JOIN b on a.a = b.b;
select a.*,b.*
from a,b
where a.a = b.b;
a | b
--+--
3 | 3
4 | 4
Left outer join:
A left outer join will give all rows in A, plus any common rows in B.
select *
from a
LEFT OUTER JOIN b on a.a = b.b;
select a.*,b.*
from a,b
where a.a = b.b(+);
a | b
--+-----
1 | null
2 | null
3 | 3
4 | 4
Full outer join:
A full outer join will give you the union of A and B, i.e. All the rows in A and all the rows in B. If something in A doesn't have a corresponding datum in B, then the B portion is null, and vice versa.
select *
from a
FULL OUTER JOIN b on a.a = b.b;
a | b
-----+-----
1 | null
2 | null
3 | 3
4 | 4
null | 6
null | 5
select min(id) as id,address, retailer
from table1
group by address, retailer
order by id
The query you need is:
SELECT min(id), address, retailer
FROM table1 AS t1
group by address, retailer
order by address
Here's the source
Use This: It's working:
SELECT * FROM `sampletable` GROUP BY address, retailer

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