Using case on return value of subquery - sql

I'd like to write case clause which takes its input from inner query. Please let me describe this in more detail.
Say I have a table:
create table food
( fruit varchar2(50),
chips varchar2(50)
);
with values
INSERT INTO food
(fruit, chips)
VALUES ('Apple', 'Paprika');
INSERT INTO food
(fruit, chips)
VALUES ('Orange', 'Salt');
DB Fiddle
I would like to write a query that will show:
fruit, chips and 1 if fruit is 'Apple' and 0 otherwise
which would give a result (example)
'Apple', 'Paprika', 1
'Orange, 'Salt', 0
I do not want use joins for this. It has to be subquery. That's a requirement I must comply with.
I've come up with the following query:
select f.fruit,
((case (select ff.fruit from food ff)
when ff.fruit = 'Apple' then 1 else 0 end ) as is_apple) from food f;
However, I get the following error ORA-00905: missing keyword

You don't need a subquery for this:
select fruit, chips,
case when fruit = 'Apple'
then 1
else 0
end as is_apple
from food
If the value must be the result of a subquery, you may use:
select fruit, chips,
(select case when f2.fruit = 'Apple'
then 1
else 0
end
from food f2
where f.rowid = f2.rowid
)
from food f

If it has to be subquery then use dual:
select fruit, chips,
(select case food.fruit when 'Apple' then 1 else 0 end from dual) is_apple
from food;
or subselect from food using primary key (if your table contains it), or rowid:
select fruit, chips,
case (select fruit from food ff where ff.rowid = food.rowid)
when 'Apple' then 1
else 0
end is_apple
from food;
demo

Related

how can I add a new column where there may be matches on an id field

I currently have a table that contains an id, and a count of a criteria for that id field. For example my table looks like this:
ID Banana_count
1 13
2 23
3 56
The original counts came from a join and a query from other tables.
create FRUIT_TABLE as
select id, count (fruit)
from my_table a
where exists (select null from DATE_FED b
where a.id = b.id
and date = (2/11/17)
and fruit_type = 'banana')
group by id;
My question is, how can i add other attributes to this particular table so that it looks like:
ID Banana_count Apple_count Orange_count
1 13 35 22
2 23 44
3 56
4 33 55
5 11
I will have to add more ids to FRUIT_TABLE that may not already be in the current table, but for fruits that are currently associated with an id, i'd like to add them in the same row.
This is a classic use case for merge:
merge into fruit_table
using apple_table
on (fruit_table.id = apple_table.id)
when matched then update set
fruit_table.apples = apple_table.apples
when not matched then insert (id,apples)
values(
apple_table.id,
apple_table.apples
);
I have simplified the problem slightly so that you are inserting from a table that simply has ids and a count of apples, so that the structure of the merge is clearer. But you can insert a subquery instead into the using... section of the statement to meet your actual requirements.
I would look into something like the following [you didn't provide your table definitions, or other application or requirements constraints so an exact answer is not possible]:
create FRUIT_TABLE as
select id
, sum(case when fruit_type = 'banana' then 1 else 0 end ) Banana_count
, sum(case when fruit_type = 'apple' then 1 else 0 end ) apple_count
, sum(case when fruit_type = 'orange' then 1 else 0 end ) orange_count
from my_table a
group by id;

How can I return a column that says "No" for values missing from the result?

Sorry for the poor phrasing in the title; I can't figure out how to explain my question succinctly.
I have a list of fruit (apple, banana, pear) and want to create a table which looks like:
fruit eaten
apple yes
banana yes
pear no
from a table that looks like
fruit quantity
apple 120
banana 30
(Note that the original table does not include pears). So far I've figured out how to get to:
fruit eaten
apple yes
banana yes
by the query:
select
fruit,
case when quantity > 0 then "yes" else no end as eaten
from original_table
But I can't figure out how to get those pesky pears to be included.
If you have a fruit table you could do something like this:
SELECT
f.fruit
,CASE WHEN ot.quantity > 0 THEN 'yes' ELSE 'no' END as Eaten
FROM
fruit f
LEFT JOIN original_table ot
ON f.fruit = ot.fruit
If you do not have a fruit table then you need to make one. I am not too familiar with hive but this technique should work in lots of different platforms:
SELECT
f.fruit
,CASE WHEN ot.quantity > 0 THEN 'yes' ELSE 'no' END as Eaten
FROM
(
SELECT 'apple' as fruit
UNION ALL
SELECT 'banana'
UNION ALL
SELECT 'pear'
) f
LEFT JOIN original_table ot
ON f.fruit = ot.fruit

Simple SQL Query 1

I'm using PL/SQL if that matters.
Table = Stuff
ID: FRUIT:
100 Apple
100 Grape
200 Apple
200 Orange
550 Apple
700 Orange
800 Orange
900 Grape
... ...
I want to list all of the Apples and their IDs that do NOT share the same ID as Orange. How do I go about doing this?
The output should be:
100 Apple
550 Apple
You can do this with a subquery so you effectively pick all of the ID's for Oranges out in this subquery then pick all of the fruit which are Apples and ID's aren't in the subquery. Something like this;
SELECT *
FROM stuff
WHERE fruit = 'Apple'
AND ID NOT IN (SELECT ID FROM stuff WHERE fruit = 'Orange')
You can select only once from the table using CASE EXPRESSION and a GROUP BY WITH HAVING clause like this :
SELECT t.id,
MAX(CASE WHEN t.FRUIT = 'Apple' THEN t.FRUIT end) as fruit
FROM stuff t
GROUP BY t.id
HAVING MAX(CASE WHEN t.FRUIT = 'Orange' THEN 1 ELSE 0 END) = 0
You are subtracting one set of records from another and a subquery will do the job.
Edited for your new data set
select *
from stuff
where fruit = 'Apple'
and id not in (
select ID from stuff where fruit != 'Apple'
);
Or you could use a MINUS query as well.
There are no need to build up full list of Orange's IDs, just use not exist:
select *
from
stuff apple_list
where
fruit = 'Apple'
and
not exists (
select null
from stuff orange_instance
where orange_instance.id = apple_list.id
)
or do same thing with outer join:
select
id, fruit
from (
select
apple_list.id,
apple_list.fruit,
nvl2(orange_instance.id, 'orange_here', 'no_orange') orange_flag
from
stuff apple_list,
stuff orange_instance
where
apple_list.fruit = 'Apple'
and
orange_instance.id (+) = apple_list.id
and
orange_instance.fruit (+) = 'Orange'
)
where
orange_flag = 'no_orange'
Second variant needs distinct in select if there are possibility of having two Oranges with same id.
Or you could do it using the MINUS set operator:
SELECT a.ID, a.FRUIT
FROM STUFF a
WHERE a.FRUIT = 'Apple'
MINUS
SELECT b.ID, 'Apple' AS FRUIT
FROM STUFF b
WHERE b.FRUIT = 'Orange'
Best of luck.

Replace column values

I've got this table,
Name Rating
A 2
B 1
C 5
D 3
E 1
F 4
and I've got a rating system
1-Excellent, 2-Very Good, 3-Good, 4-OK, 5-Poor
I was wondering if i could replace the nueric values in the table to get the following result table.
Name Rating
A Very Good
B Excellent
C Poor
D Good
E Excellent
F OK
Thanks
I don't think it's good idea to update your data inplace, it's better to store id of the rating and not text representation of the data. But you can query your table and replace int with text:
select
Name,
case Rating
when 1 then 'Excellent'
when 2 then 'Very Good,'
when 3 then 'Good'
when 4 then 'OK'
when 5 then 'Poor'
end as Rating
from <your table>
Or you can create a lookup table and join with it
create table Rating (id int, desc nvarchar(128))
insert into Rating (id, desc)
select 1, 'Excellent' union all
select 2, 'Very good' union all
select 3, 'Good' union all
select 4, 'OK' union all
select 5, 'Poor'
select
t.Name,
R.desc as Rating
from <your table> as t
left outer join Rating as R on R.id = t.Rating
Use a CASE statement. Of course, this will only work if your column is not set to a numeric value.
UPDATE tblRatings
SET Rating = CASE WHEN 1 THEN 'Excellent'
WHEN 2 THEN 'Very Good'
WHEN 3 THEN 'Good'
WHEN 4 THEN 'OK'
ELSE 'Poor'
END
If it is, you'll need to use a SELECT statement;
SELECT CASE WHEN 1 THEN 'Excellent'
WHEN 2 THEN 'Very Good'
WHEN 3 THEN 'Good'
WHEN 4 THEN 'OK'
ELSE 'Poor'
END
FROM tblRatings
Try using Choose
IF sql-server 2012
like this:
select Rating,name,choose(Rating,'Excellent','Very Good','Good','OK','Excellent','Poor') from table
Fiddle Demo
You can use update function :
update yourtable set rating = 'Excellent' where rating = '1'
and zou can do this update for all your ratings
Update YOURTABLE
set Rating = case when '2' then ' Very good'
when '3' then ' good' end

Select records based on column priority

First of all, the title of this question is horrible, but I didn't find a better way to describe my issue.
There's probably a very easy way to do this, but I couldn't figure it out. This is very similar to this question, but I'm running on sqlite3 (iOS) so I suspect my options are much more limited.
I have a table with product records. All records have an ID (note: I'm not talking about the row ID, but rather an identification number unique to each product). Some products may have two entries in the table (both with the same ID). The only difference would be in a special column (let's say column COLOUR can be either RED or GREEN).
What I want to do is create a list of unique products based on the value of COLOUR, with priority to GREEN if both GREEN and RED records exist for the same product.
In short, if I have the following case:
id PRODUCT ID COLOUR
1 1001 GREEN
2 1002 GREEN
3 1002 RED
4 1003 RED
I would like my SELECT to return the rows 1, 2 and 4. How can I achieve this?
My current approach is to have separate tables, and do the join manually, but obviously this is a very bad idea..
Note: I've tried to use the same approach from here:
SELECT *
FROM xx
WHERE f_COLOUR = "GREEN"
UNION ALL
SELECT *
FROM xx
WHERE id not in (SELECT distinct id
FROM xx
WHERE f_COLOUR = "GREEN");
But the result I'm getting is rows 1,2,3,4 instead of just 1,2,4. What am I missing?
Edit: One more question please: how can this be made to work with a subset of records, ie. if instead of the entire table I wanted to filter some records?
For example, if I had something like SELECT * FROM table WHERE productID LIKE "1%" ... how can I retrieve each unique product, but still respecting the colour priority (GREEN>RED)?
Your query is nearly correct. Just use PRODUCTID and not ID.
SELECT *
FROM xx
WHERE f_COLOUR = "GREEN"
UNION
SELECT *
FROM xx
WHERE PRODUCTID not in
(SELECT PRODUCTID
FROM xx
WHERE f_COLOUR = "GREEN");
SQLFiddle Demo
Try this
SELECT *
FROM xx
WHERE COLOUR = 'GREEN'
UNION
SELECT *
FROM xx WHERE P_Id not in
(SELECT P_Id
FROM Persons
WHERE COLOUR = 'GREEN');
See ALSO SQL FIDDLE DEMO
I just want to offer that you can do this with a group by:
select (case when sum(case when colour = 'Green' then 1 else 0 end) > 0
then max(case when colour = 'Green' then id end)
else max(case when colour = 'Red' then id end)
end) as id,
product_id
(case when sum(case when colour = 'Green' then 1 else 0 end) > 0 then 'Green'
else 'Red'
end) as colour
from t
group by product_id
You can have it like this
WITH PriorityTable
AS
(
SELECT T.*,
ROW_NUMBER() OVER (PARTITION BY T.ID
ORDER BY PT.ColorPriority ) PriorityColumn
FROM XX AS T
INNER JOIN (
SELECT 'RED' AS f_COLOUR , 1 AS ColorPriority
UNION
SELECT 'GREEN' AS f_COLOUR , 2 AS ColorPriority
) AS PT
ON T.f_COLOUR = PT.f_COLOUR
)
SELECT * FROM PriorityTable
WHERE PriorityColumn = 1