In Oracle how to search with sub conditions on same column - sql

In Oracle how do I find Cars which must have Feature1 and have at lest, one out of Feature2 or Feature3. Sample table and expected result should look like below screenshot. Thanks Kiran

This should work:
select t1.car, t1.feature
from yourtable t1
inner join
( -- inner select returns the cars with the Feature1 and Feature2 or Feature3
select car, feature
from yourtable
where feature = 'Feature1'
and exists (select car
from yourtable
where feature in ('Feature2', 'Feature3'))
) t2
on t1.car = t2.car
where t1.feature in ('Feature1', 'Feature2', 'Feature3') -- this excludes any other features
see SQL Fiddle with Demo

I like to do this with GROUP BY and HAVING:
select car
from t
group by car
having max(case when feature = 'Feature1' then 1 else 0 end) = 1 and
max(case when feature in ('Feature1', 'Feature2') then 1 else 0 end) = 1
This query returns the cars. To get the featuers as well, you have to join tis back in:
select t.*
from (select car
from t
group by car
having max(case when feature = 'Feature1' then 1 else 0 end) = 1 and
max(case when feature in ('Feature1', 'Feature2') then 1 else 0 end) = 1
) c join
t
on t.car = c.car
I like this method, because the same idea can be used for handling many different similar queries -- AND conditions, OR conditions, different subgroups, and different counts.

Related

Change values to repeat based when a condition is met in SQL

I want to create a new column where If my Rom Indicator is 'Y', then pick the Account ID value and swap it for all the IDs as shown below
I tried using Case statments like this
CASE WHEN PRIM_IND = 'Y' THEN ACT_ID ELSE ACT_ID END
select t.*
,max(case when PRI_IND = 'Y' then ACT_ID end) over(partition by ID) as ACT_ID_NEW
from t
ID
ACT_ID
PRI_IND
ACT_ID_NEW
200
ACT01
N
ACT02
200
ACT02
Y
ACT02
201
ACT03
Y
ACT03
201
ACT04
N
ACT03
201
ACT05
N
ACT03
202
ACT06
Y
ACT06
Fiddle
Not sure if this is what you wanted, but the output matches so I guess so. It's just a basic self INNER JOIN where you only want to join on the records with pri_ind = 'Y'
SELECT a.id,
a.act_id,
a.pri_ind,
b.act_id AS act_id_new
FROM tab1 a
INNER JOIN tab1 b
ON a.id = b.id
AND b.pri_ind = 'Y'

Conditional IN Statement to be used inside Postgres function

I am working on Postgres and I have two tables vehicles and vehicles_flag. There are no relations between the two tables and hence we can not join two tables to fetch the required data.
The table structure is below (vehicle_flag table may not contain all the id present in the vehicle table) :
[Table structure]
I am writing a function that will accept multiple input parameters. I have to select vehicle id from the vehicle_flag table only if the flag value is true: otherwise, I have to ignore the vehicel_flag table. My aim is to achieve something like this, but turns out the case statement expects scaler output:
select count(id) from vehicles
where
vehicles.id in (case
when #hasbluetooth =1 then (select distinct id from vehicle_flags where flag='bluetooth' and value = '1')
else
(select distinct id from vehicles)
end)
and
vehicles.id in (case
when #hasac =1 then (select distinct id from vehicle_flags where flag='ac' and value = '1')
else
(select distinct id from vehicles)
end)
Kindly suggest any solution to achieve this.
I suspect you want:
select v.*
from vehicle v
left join vehicle_flags vf on vf.id = v.id
group by v.id
having
(#hasbluetooth = 0 or bool_or(vf.flag = 'bluetooth' and vf.value = 1)
and (#hasac = 0 or bool_or(vf.flag = 'ac' and vf.value = 1)

Joining 2 sql selects

I have two very similar sql statements
select instrumentuniqueid, count(levelid) as errors
from dbo.testevent
join dbo.test
on dbo.test.id = dbo.testevent.testid where dbo.test.runid = 20962 and dbo.testevent.levelid = 1
group by instrumentuniqueid
select instrumentuniqueid, count(levelid) as warnings
from dbo.testevent
join dbo.test
on dbo.test.id = dbo.testevent.testid where runid = 20962 and levelid = 2
group by instrumentuniqueid
The first one produces columns of instrumentuniqueid (aggregated) and the count
The second one produces columns of aggregated instrumentuniqueid with a different count.
How can I join them together so that the final table looks like:
Instrumentuniqueid | Errors | Warnings
Use conditional aggregation:
select instrumentuniqueid,
sum(case when te.levelid = 1 then 1 else 0 end) as errors,
sum(case when te.levelid = 2 then 1 else 0 end) as warnings
from dbo.testevent te join
dbo.test t
on t.id = t2.testid
where t.runid = 20962
group by instrumentuniqueid;
Table aliases also make a query easier to write and to read.

Query to Not selecting columns if multiple

I've been scratching my head about this.
I have a table with multiple columns for the same project.
However, each project can have multiple rows of a different type.
I would like to find only projects type O and only if they don't have other types associated with them.
Ex:
Project_Num | Type
1 | O
1 | P
2 | O
3 | P
In the case above, only project 2 should be returned.
Is there a query or a method to filter this information? Any suggestions are welcome.
If I understand correctly, you want to check that the project has only record for its project number and it has type 'O'. You can use below query to implement this:
;with cte_proj as
(
select Project_Num from YourTable
group by Project_Num
having count(Project_Num) = 1)
select Project_Num from cte_proj c
inner join YourTable t on c.Project_Num = t.Project_Num
where t.Type = 'O'
You can do this using not exists:
select p.*
from projects p
where type = 'O' and
not exists (select 1
from projects p2
where p2.project_num = p.project_num and p2.type <> 'O'
);
You can also do this using aggregation:
select p.project_num
from projects p
group by p.project_num
having sum(case when p.type = 'O' then 1 else 0 end) > 0 and
sum(case when p.type <> 'O' then 1 else 0 end) = 0;
Another option (pretty fast)
SELECT p0.*
FROM project p0
LEFT JOIN project p1 ON (p0.Type<>p1.Type AND p0.Project_Num=p1.Project_Num)
WHERE p0.Type='O' AND p1.Type IS NULL;

Join using combined conditions on one join table

I have join a table joining songs to genres. The table has a 'source' column that's used to identify where the genre was found. Genres are found from blogs, artists, tags, and posts.
So,
songs | song_genre | genres
id | song_id, source, genre_id | id
What I want to build is a song SELECT query that works something like this, given I already have a genre_id:
IF exists song_genre with source='artist' AND a song_genre with source='blog'
OR exists song_genre with source='artist' AND a song_genre with source='post'
OR exists song_genre with source='tag'
I'm was going to do it by doing a bunch of joins, but am sure I'm not doing it very well.
Using Postgres 9.1.
kgu87's query is correct, but likely produces a relatively expensive plan with the numerous counts over subselects. All those counts can be accumulated with one pass over the genre table with cases on source and a group by song_id. Without sample data it's hard to say whether this is faster, but I suspect it's likely. I think it's simpler at any rate.
select g.song_id
from song_genre g
group by g.song_id
having
( sum(case when g.source = 'tag' then 1 else 0 end) > 0 )
or
( sum(case when g.source = 'artist' then 1 else 0 end) > 0
and (
sum(case when g.source = 'blog' then 1 else 0 end) > 0
or
sum(case when g.source = 'post' then 1 else 0 end) > 0
)
)
select id
from
(
select distinct
id,
(
select
count(*) from
song_genre b
where a.id = b.song_id
and b.source = 'artist'
) as artist,
(
select
count(*) from
song_genre b
where a.id = b.song_id
and b.source = 'blog'
) as blog,
(
select
count(*) from
song_genre b
where a.id = b.song_id
and b.source = 'post'
) as post,
(
select
count(*) from
song_genre b
where a.id = b.song_id
and b.source = 'tag'
) as tag
from songs A
) AA
where
(AA.artist > 0 AND AA.blog > 0)
OR
(AA.artist > 0 AND AA.post > 0)
OR
(AA.tag > 0)