Oracle SQL - Left join on "in" statements - sql

The point of the query below is to return a list of people and whether or not they are unhappy. But, If I don't know if they are happy or not, I want to list their name and their unhappy as "happy". This is complicated by the fact that I have some additional requirements for what defines unhappy.
I'm writing a query like this...
Select name.firstName,
CASE
WHEN Mood.mood is not null
THEN 'Unhappy'
ELSE 'Happy'
END Unhappy
From name
Mood
WHERE mood.firstName(+) = name.firstName
AND mood.type IN ('Hungry','Tired','Fatigued','Bored',null)
AND mood.value(+) > 5;
So I want to return every single name from the table name, and either a value of "happy" or "unhappy", even though those names may or may not be in mood table. When I run this as I've written it, I get no rows. I figure this might involve my mood.type line because I can't use a left join on an "in" statement.

I think you have a couple of problems:
You have a spurious semi-colon in your WHERE clause.
The CASE expression is in the wrong place.
You shouldn't use NULL in an IN expression.
You could remove the semi-colon and change your IN expression to this instead:
AND (mood.type IN ('Hungry','Tired','Fatigued','Bored') OR mood.type IS NULL)
Also, I'd strongly advise you not to use that obsolete join syntax. It's probably a good idea to use the JOIN keyword as it makes this sort of query a lot easier.
SELECT
name.firstName,
CASE
WHEN Mood.mood IS NOT NULL
THEN 'Unhappy'
ELSE 'Happy'
END Unhappy
FROM name
LEFT JOIN Mood ON
mood.firstName = name.firstName AND
mood.type IN ('Hungry','Tired','Fatigued','Bored') AND
mood.value > 5

I would do it like this:
select n.firstname,
nvl(m.mood, 'Happy') mood
from
name n
left outer join mood m
on n.firstname = m.firstname
where
nvl(m.type,'Hungry') in ('Hungry', 'Tired', 'Fatigued', 'Bored')

Related

Join under condition in Oracle SQL SELECT statement

I'm trying to join two tables under a condition but I haven't been able to make it work. I've been searching and reading but couldn't find an answer for my case.
This is the basic idea of what I'm trying to achieve (what I wrote in lowercase is what I want to achieve in my own words, in case you wonder):
SELECT TABLE1.STR_FULLNAME, TABLE1.STR_MAILBIZ, TABLE2.STR_IP
FROM TABLE1
INNER JOIN TABLE2
if TABLE2.STR_MACHINEUSER contains 'TEXT\' then join like this:
ON TABLE1.STR_LOGIN = SELECT SUBSTR(STR_MACHINEUSER, 6) AS STR_MACHINEUSER FROM TABLE2 WHERE
STR_MACHINEUSER LIKE 'TEXT\%'
else join like this:
ON TABLE1.STR_LOGIN = TABLE2.STR_MACHINEUSER
ORDER BY TABLE2.DT_INSERT DESC;
The content of Table1.STR_LOGIN and that of Table2.STR_MACHINEUSER are in some cases exactly the same and in some cases a prefix needs to be removed in Table2.STR_MACHINEUSER ('TEXT\'). I've seen that conditions should be handled with the CASE expression. I have tried different ways, but I couldn't make it work for what I need. I'm thinking that I might need a complete different approach, but I don't see what...
Does someone have a suggestion? Thanks in advance!
Maybe this is what you want:
SELECT TABLE1.STR_FULLNAME, TABLE1.STR_MAILBIZ, TABLE2.STR_IP
FROM TABLE1
INNER JOIN TABLE2 ON
(not TABLE2.STR_MACHINEUSER LIKE 'TEXT\%' and TABLE1.STR_LOGIN = TABLE2.STR_MACHINEUSER)
or
(TABLE2.STR_MACHINEUSER LIKE 'TEXT\%' and TABLE1.STR_LOGIN = SUBSTR(TABLE2.STR_MACHINEUSER, 6))
Sure you could shorten your statement using some case construct like:
case
when TABLE2.STR_MACHINEUSER LIKE 'TEXT\%'
then SUBSTR(TABLE2.STR_MACHINEUSER, 6) else TABLE2.STR_MACHINEUSER
end
and compare this to TABLE1.STR_LOGIN but I find (IMHO) the first (logic) join better readable.

Finding differences in queries using two criteria

I'm trying to find a way to find a way to compare two queries that use a combine sent of criteria. In this case we have Prefixes (Two letter code like DA) and Pack number 1234567. In the query I've created a field that combines these two things so it appears 1234567DA this is done with each of the queries from the separate tables they are pulled from. The idea is that if this is in one table and not the other it would show up as "False". I tried to use an Unmatched query but that doesn't seem to work. What I have currently is as follows:
SELECT
[1LagoTest].Prefix,
[1BigPicPackPref].BigPicPP,
IIf([BigPicPP]=[LagoPP],"True","False") AS Compare,
[1LagoTest].RETAIL,
[1LagoTest].MEDIA
FROM 1LagoTest
LEFT JOIN 1BigPicPackPref
ON [1LagoTest].[Prefix] = [1BigPicPackPref].[BigPicPP]
WHERE (((IIf([BigPicPP]=[LagoPP],"True","False")) Like "False")
AND (([1LagoTest].MEDIA) Not Like "*2019 FL*"))
ORDER BY [1LagoTest].RETAIL;
Right now it will show whats missing from LagoPP but doesn't give me anything from missing packs in BigPicPP. Any help in the right direction would be greatly appreciated.
Thanks!!
This gets a little tricky in Access without FULL OUTER JOIN, but the general idea to is replicate a FULL OUTER JOIN using UNION ALL, then filter from that.
Something like this:
SELECT I.Prefix,
I.BigPicPP,
I.Compare,
I.Retail,
I.Media
FROM (SELECT L.Prefix,
B.BigPicPP,
IIf([BigPicPP]=[LagoPP],"True","False") as Compare,
L.Retail,
L.Media
FROM 1LagoTest L
JOIN 1BigPicPackPref B ON L.Prefix = B.BigPicPP
WHERE L.Media NOT LIKE "*2019 FL*"
UNION ALL
SELECT L.Prefix,
B.BigPicPP,
"False", --Missing records from 1BigPicPackPref
L.Retail,
L.Media
FROM 1LagoTest L
LEFT JOIN 1BigPicPackPref B ON L.Prefix = B.BigPicPP
AND L.Media NOT LIKE "*2019 FL*"
WHERE B.Prefix IS NULL
UNION ALL
SELECT B.Prefix,
B.BigPicPP,
"False", --Missing records from 1LagoTest
L.Retail,
L.Media
FROM 1LagoTest L
RIGHT JOIN 1BigPicPackPref B ON L.Prefix = B.BigPicPP
AND L.Media NOT LIKE "*2019 FL*"
WHERE L.Prefix IS NULL
) AS I
You only need IFF in the first part of the union because in the second two parts one side will always be NULL, so we know the compare will always fail and be False.
You shouldn't need this part of your current WHERE clause at all (((IIf([BigPicPP]=[LagoPP],"True","False")) Like "False"). But if you only want to see False records, just add WHERE I.Compare = "False" to the bottom of the outer select.
The reason the "Unmatched" query (assuming through the Wizard) does not work, is because you are attempting to see the values of two separate tables / queries that do not match either table / query. This is not how the "Unmatched" works. All that will give you is a single table / query that does not match another single table / query.
This can most likely be done any number of ways, but this would probably get you where you want to be (or close to it):
SELECT
a.Prefix,
b.BigPicPP,
IIf([BigPicPP]=[LagoPP],"True","False") AS Compare,
a.RETAIL,
a.MEDIA
FROM [1LagoTest] a
LEFT JOIN [1BigPicPackPref] b ON a.Prefix = b.BigPicPP
WHERE a.MEDIA Not Like "*2019 FL*"
AND b.BigPicPP IS NULL
ORDER BY a.RETAIL
UNION
SELECT
a.Prefix,
b.BigPicPP,
IIf([BigPicPP]=[LagoPP],"True","False") AS Compare,
a.RETAIL,
a.MEDIA
FROM [1LagoTest] a
RIGHT JOIN [1BigPicPackPref] b ON a.Prefix = b.BigPicPP
WHERE a.MEDIA Not Like "*2019 FL*"
AND a.Prefix IS NULL
ORDER BY a.RETAIL
NOTE: Depending on the data structure, the ORDER BY may cause some issues.
So the way I got this to finally work was to build two separate queries. One looking at what was missing from Lago and One that was looking at what was missing from BigPic. It was the only way I could get it to give me both sets of missing data. If I can find a better way to do it through one query I will report back as I'm still gonna play around with it.

Write an additional column to query result with different values everytime

I've been searching for quite a while now and I haven't been able to find an answer for what I was looking. I have the following query:
SELECT DISTINCT o.titulo, o.fecha_estreno
FROM Obra o
WHERE (o.titulo LIKE '%Barcelona%' AND EXISTS(SELECT p.id_obra FROM Pelicula p WHERE p.id_obra = o.id_obra)) OR EXISTS(SELECT DISTINCT pa.id_obra
FROM Participa pa
WHERE pa.id_obra = o.id_obra AND EXISTS(SELECT DISTINCT l.nombre FROM Lugar l
WHERE l.nombre LIKE '%Barcelona%' AND EXISTS(SELECT DISTINCT tl.id_lugar FROM TieneLugar tl
WHERE tl.id_lugar = l.id_lugar AND tl.id_profesional = pa.id_profesional))) OR EXISTS(SELECT DISTINCT er.id_obra
FROM EstaRelacionado er
WHERE er.id_obra = o.id_obra AND EXISTS(SELECT DISTINCT k.keyword
FROM Keywords k
WHERE k.id_keyword = er.id_keyword AND k.keyword LIKE '%Barcelona%'));
What it basically does is it searches for every movie in my database which is related in some way to the city it gets. I wanted to have a third column showing for every result, with the reason the row is showing as a result (for example: TITLE CONTAINS IT, or ACTOR FROM THE MOVIE BORN THERE, etc.)
Thank you for your patience and help!
EDIT: As suggested, here are some examples of output. The column should show just the first cause related to the movie:
TITULO FECHA_ESTRENO CAUSE
---------- ---------------- ----------
Barcelona mia 1967 TITLE
https://www.postgresql.org/docs/7.4/static/functions-conditional.html
The SQL CASE expression is a generic conditional expression, similar
to if/else statements in other languages:
CASE WHEN condition THEN result
[WHEN ...]
[ELSE result]
END
CASE clauses can be used wherever an expression is valid. condition is an expression that returns a boolean result. If
the result is true then the value of the CASE expression is the result
that follows the condition. If the result is false any subsequent WHEN
clauses are searched in the same manner. If no WHEN condition is true
then the value of the case expression is the result in the ELSE
clause. If the ELSE clause is omitted and no condition matches, the
result is null.
Example for your case:
SELECT (CASE WHEN EXISTS(... l.nombre LIKE '%Barcelona%') THEN 'TITLE CONTAINS IT' WHEN <conditon for actor> THEN 'ACTOR WA BORN THERE' WHEN ... END) as reason
Here is one solution.
Create a subquery for each search condition.
include the reason in the subqueries' projections
outer join the subqueries so it doesn't matter which one hist
filter to make sure that at least one of your subqueries has a positive result
use coalesce() to get one reason.
I haven't done all your conditions, and I've probably mangled your logic but this is the general idea:
SELECT o.titulo
, o.fecha_estreno
, coalesce(t1.reason, t2.reason) as reason
FROM Obra o
left outer join ( select id_obra, 'title contains it' as reason
from Obra
where titulo LIKE '%Barcelona%' ) t1
on t1.id_obra o.id_obra
left outer join ( select distinct pa.id_obra , 'takes place there' as reason
from Participa pa
join TieneLugar tl
on tl.id_profesional = pa.id_profesional
join Lugar l
on tl.id_lugar = l.id_lugar
where l.nombre LIKE '%Barcelona%' ) t2
on t2.id_obra o.id_obra
WHERE t1.id_obra is not null
or t2.id_obra is not null
/
coalesce() just returns the first non-null value which means you won't see multiple reasons if you get more than one hit. So order the arguments to put the most powerful reasons first.
Also, you should consider consider using Oracle Text. It's the smartest way to wrangle this sort of keyword searching. Find out more.

Case using at Join

I'm trying to use case function at Join and I failed to execute the Query.
What I'm doing wrong?
left join vortex_dbo.vw_public_material_history on
CASE
WHEN vw_public_request_material_location_mir.material_request_id = (substring(vw_public_material_history.comments,12,6))
then vw_public_request_material_location_mir.material_request_id
else null
end
What I wish to get is join rows only if I have "true" from the "When"
I feel I miss something here.
Your CASE expression makes no sense. A CASE expression results in a value, in your case for example in material_request_id. So you'd get
left join vortex_dbo.vw_public_material_history on material_request_id
in case of a string match. You do notice that this ON clause is incomplete?
You are probably looking for
left join vortex_dbo.vw_public_material_history on
vw_public_request_material_location_mir.material_request_id =
(substring(vw_public_material_history.comments,12,6))
That would read better with table aliases:
from vw_public_request_material_location_mir mir
left join vortex_dbo.vw_public_material_history hist on
mir.material_request_id = (substring(hist.comments,12,6))
As to your data model: It seems strange that you have an ID hidden in a string. You may want to re-consider your table design.

sql query question inner join

LEFT JOIN PatientClinics AB ON PPhy.PatientID = AB.PatientID
JOIN Clinics CL ON CL.ID = AB.ClinicID
AND COUNT(AB.ClinicID) = 1
I get error using Count(AB.ClinicID) = 1 (ClinicID has duplicate values in the table and
I want to use only 1 value of each duplicate value of ClinicId to produce result)
What mistake am I making?
I've never seen a COUNT() being used in a JOIN before. Maybe you should use:
HAVING COUNT(AB.ClinicID) = 1
instead.
Count() can't be used as a join/filter predicate. It can be used in the HAVING clause however. You should include the entire query in order to get a better example of how to rewrite it.
maybe investigate the HAVING clause instead of using COUNT where you put it.
hard to help without the full query.