How to return distinct rows meeting condition - sql

I have an example table in the format below with sample data:
=cust_table
Customer: Liked_Color:
Adam Blue
Adam Green
Adam Yellow
Adam Red
Bob Yellow
Bob Yellow
Bob Yellow
Bob Yellow
Charlie Red
Charlie Red
Charlie Red
Charlie Red
How can I select distinct customers and only return them if they DO NOT like the color Blue?
So, the returned results would be:
Customer: Bob, Charlie
If I do:
SELECT DISTINCT Customer
FROM cust_table
WHERE Liked_Color NOT LIKE Blue
I get: Adam, Bob, Charlie
How would I make sure the Customer is only returned when Blue is not a Liked_Color for a Customer?

I would use aggregation:
select customer
from cust_table
group by customer
having sum(case when color = 'Blue' then 1 else 0 end) = 0;
However, a customer table should have one row per customer (at least per unit time). If you had such a table and a customer_colors table, then I would use not exists:
select c.*
from customers c
where not exists (select 1
from customer_colors cc
where cc.customer = c.customer and cc.color = 'Blue'
);
This would also return customers with no preferred color at all, which you cannot do with your single table.

Alternatively, see whether sets do any good:
SQL> with cust (customer, liked_color) as
2 (select 'Adam' , 'Blue' from dual union all
3 select 'Adam' , 'Green' from dual union all
4 select 'Bob' , 'Yellow' from dual union all
5 select 'Bob' , 'Red' from dual union all
6 select 'Charlie', 'Red' from dual union all
7 select 'Charlie', 'Red' from dual
8 )
9 select customer from cust where liked_color <> 'Blue'
10 minus
11 select customer from cust where liked_color = 'Blue';
CUSTOME
-------
Bob
Charlie
SQL>

this will work the simplest of them all :
select distinct a from Table1 where a not in
(select distinct a from Table1 where b like 'Blue');
check fiddle:http://sqlfiddle.com/#!9/7034f/10

Related

Selecting MAX of a Value from multiple categories from a table

I am looking to get the max weight of Apple, Orange, Mango - there could be any number of fruits. Bold items from the table is what i would like to have my query response.
I know this can be done by partitioning the table for example:
SELECT fruits,max(weight) OVER(PARTITION BY fruits)
FROM fruitstat
GROUP BY fruits;
But this is not getting my expected results. I need the ones which are the max weight fruits in its category.
Fruits
Color
Weight
Apple
red
23
Orange
orange
6
Mango
yellow
13
Apple
red
15
Orange
orange
19
Mango
yellow
16
Apple
red
44
Orange
orange
31
Mango
yellow
12
Apple
red
14
Orange
orange
22
Mango
yellow
11
Just group the MAX(weight) by fruits:
WITH fruit AS
(
SELECT 'Apple' as fruits,'red' as color ,23 as weight FROM dual UNION ALL
SELECT 'Orange','orange',6 FROM dual UNION ALL
SELECT 'Mango','yellow',13 FROM dual UNION ALL
SELECT 'Apple','red',15 FROM dual UNION ALL
SELECT 'Orange','orange',19 FROM dual UNION ALL
SELECT 'Mango','yellow',16 FROM dual UNION ALL
SELECT 'Apple','red',44 FROM dual UNION ALL
SELECT 'Orange','orange',31 FROM dual UNION ALL
SELECT 'Mango','yellow',12 FROM dual UNION ALL
SELECT 'Apple','red',14 FROM dual UNION ALL
SELECT 'Orange','orange',22 FROM dual UNION ALL
SELECT 'Mango','yellow',11 FROM dual
)
SELECT fruits, MAX(weight)
FROM fruit
GROUP BY fruits;
P.S. MAX for the apple is 44, not 23, at least in your sample data
You don't need to use group by on window functions. You can do this, instead.
First, sort the fruits weight by using rank.
select rank() over (partition by fruits order by weight) as rank, fruits, weight
from fruitstat
After that, you can use subquery to return the first value only.
select fruits, weight
from (select rank() over (partition by fruits order by weight) as rank, fruits, weight from fruitstat) a
where rank = 1

what is wrong with my query in oracle live sql?

I ran the following code in oracle live SQL but it gives an error. What is the problem?
select * from bricks full join
((select count(*) from bricks) as "counts"
inner join (select count(*) from bricks group by colour) as "colours"
on counts.colour=colours.colour)
on bricks.colour=bricks.colour; --a dummy condition just for connecting tables without a specific column
output:
ORA-00907: missing right parenthesis
The problems are:
AS before a table/sub-query alias is invalid syntax in Oracle. Remove the AS keywords.
The aliases you are using are quoted identifiers which are case-sensitive and you do not use quoted identifiers with the same case when you refer to the aliases. Remove the double quotes.
Neither the counts nor colours sub-query has a colour column in the SELECT clause and you cannot subsequently refer to non-existent columns in the ON clause of the join condition.
You can fix it using:
select *
from bricks
CROSS JOIN (select count(*) AS cnt from bricks) counts
LEFT OUTER JOIN (
select colour, count(*) AS colour_cnt
from bricks
group by colour
) colours
on bricks.colour=colours.colour
Which, for the sample data:
CREATE TABLE bricks (id, colour) AS
SELECT 1, 'red' FROM DUAL UNION ALL
SELECT 2, 'red' FROM DUAL UNION ALL
SELECT 3, 'red' FROM DUAL UNION ALL
SELECT 4, 'green' FROM DUAL UNION ALL
SELECT 5, 'green' FROM DUAL UNION ALL
SELECT 6, 'blue' FROM DUAL;
Outputs:
ID
COLOUR
CNT
COLOUR
COLOUR_CNT
1
red
6
red
3
2
red
6
red
3
3
red
6
red
3
4
green
6
green
2
5
green
6
green
2
6
blue
6
blue
1
However, you probably want to simplify things and use the analytic COUNT() OVER (...) function and eliminate the self-joins:
select b.*,
COUNT(*) OVER () AS cnt,
COUNT(*) OVER (PARTITION BY colour) AS colour_cnt
from bricks b;
Which outputs:
ID
COLOUR
CNT
COLOUR_CNT
6
blue
6
1
5
green
6
2
4
green
6
2
2
red
6
3
1
red
6
3
3
red
6
3
(identical other than not duplicating the colours column)
db<>fiddle here

Count the number of times word appears in a single column

I'm attempting to count the number of times apples and oranges appear in my fruit column.
The table looks like this:
Fruit
-------
Apples
Apples Oranges
Apples Oranges
Apples
Oranges
Expected output:
Apples 4
Oranges 3
My code thus far. I'm not sure how to do it when both appear and how to add them to the totals. I'm sure there is an easier way that this.
SELECT
COUNT (CASE WHEN Fruit LIKE '%Apples%' THEN '1' END) AS Apples
COUNT (CASE WHEN Fruit LIKE '%Oranges%' THEN '1' END) AS Oranges
FROM Fruits
Cheers
If those fruits are single-worded and separated by a space, then such a generic approach might be interesting for you.
Lines #1 - 8 represent sample data; you already have that so you don't type it. Code you might need starts at line #10.
SQL> with fruit (fruit) as
2 -- sample data; you have that in a table
3 (select 'Apples' from dual union all
4 select 'Apples Oranges' from dual union all
5 select 'Apples Oranges' from dual union all
6 select 'Apples Lemon' from dual union all
7 select 'Oranges Plums' from dual
8 ),
9 -- split fruits to rows
10 temp as
11 (select regexp_substr(fruit, '[^ ]+', 1, column_value) fruit
12 from fruit cross join
13 table(cast(multiset(select level from dual
14 connect by level <= regexp_count(fruit, ' ') + 1
15 ) as sys.odcinumberlist))
16 )
17 select fruit, count(*)
18 from temp
19 group by fruit
20 order by fruit;
FRUIT COUNT(*)
-------------------------------------------------------- ----------
Apples 4
Lemon 1
Oranges 3
Plums 1
SQL>
Either with sum or count, it works
Connected to:
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
SQL> with t as
(
select 'Apples' as fruits from dual union all
select 'Apples Oranges' as fruits from dual union all
select 'Apples Oranges' as fruits from dual union all
select 'Apples' as fruits from dual union all
select 'Oranges' as fruits 6 from dual
) select
SUM (CASE WHEN fruits LIKE '%Apples%' THEN '1' END) AS Apples ,
SUM (CASE WHEN fruits LIKE '%Oranges%' THEN '1' END) AS Oranges
FROM t
;
APPLES ORANGES
---------- ----------
4 3
SQL> with t as
(
select 'Apples' as fruits from dual union all
select 'Apples Oranges' as fruits from dual union all
select 'Apples Oranges' as fruits from dual union all
select 'Apples' as fruits from dual union all
select 'Oranges' as fruits from dual
) select
COUNT (CASE WHEN fruits LIKE '%Apples%' THEN '1' END) AS Apples ,
COUNT (CASE WHEN fruits LIKE '%Oranges%' THEN '1' END) AS Oranges
FROM t ;
APPLES ORANGES
---------- ----------
4 3
SQL>

SELECTING aggregate data using CASE statement issue

I'm trying to create a query to obtain zone data in preparation to moving the column to another table. How our DB is set up is that we have assets and cables. One asset can have many cables but a cable cannot have multiple assets. We're currently trying to move the Zone field from the Cable table (ex. Below) to the Asset table.
A_ID C_ID Zone
--------------------------
1 1 Green
1 2 Green
1 3 Yellow
2 4 Green
2 5 Red
3 6 Yellow
3 7 Yellow
3 8 Yellow
3 9 Red
The way I want it to be set up for the Asset table is if the Asset contains multiple cables with different zones, if one of the zones is Yellow it defaults to Yellow (ex. 3 green cables, 1 yellow cable - Asset_ID has Yellow zone). Next if it doesn't have any Yellows but has at least 1 red, it defaults to Red (ex. 2 green cables, 3 red cables - Asset_ID has Red Zone). Only if it just has Green zones then it defaults to Green.
Using the sample table above these are the results I would expect.
Expected Results
A_ID Zone
-------------
1 Yellow
2 Red
3 Yellow
I'm trying to use CASE statements but I'm having difficulty formulating the query to group the results correctly.
Any help would be greatly appreciated, thank you in advance.
One way to do it using conditional aggregation and a case expression.
select a_id
,case when yellow_count >=1 then 'Yellow'
when yellow_count = 0 and red_count >=1 then 'Red'
when yellow_count = 0 and red_count = 0 and green_count >=1 then 'Green'
end zone
from (select a_id,
count(case when zone = 'Yellow' then 1 end) yellow_count,
count(case when zone = 'Red' then 1 end) red_count
count(case when zone = 'Green' then 1 end) green_count
from cable_table
group by a_id) t
No case or if statement needed. Think in sets. You need to assign a priority join to the priority and select the one with the highest priority first. Like this
WITH Priority AS
(
SELECT * FROM (
VALUES
('Yellow', 1),
('Red', 2),
('Green', 3)
) AS P(Zone,P)
)
SELECT A_ID, Zone
FROM (
SELECT A_ID, Zone
ROW_NUMBER() OVER (PARTITION BY A_ID ORDER BY P.P ASC) AS RN
FROM AssetTable A
LEFT JOIN Priority P ON A.Zone = P.Zone
) SUB
WHERE RN = 1
Not sure if my syntax is right for VALUES in CTE for Oracle. if that gives an error replace with this:
WITH Priority AS
(
SELECT 'Yellow' AS Zone, 1 AS P
UNION ALL
SELECT 'Red' AS Zone, 2 AS P
UNION ALL
SELECT 'Green' AS Zone, 3 AS P
)
Why just don't do a simple:
SELECT A_ID, MAX( Zone )
FROM table
GROUP BY A_ID
if some A_ID has Yellow then max( Zone ) returns Yellow
if some A_ID hasn't Yellow, but has Red then max( Zone ) returns Red<br>
otherwise (noYellownorRed) max( Zone ) returnsGreen`
and example:
with data as (
select 1 a_id, 1 c_id , 'Green' zone from dual union all
select 1 , 2 , 'Green' from dual union all
select 1 , 3, 'Yellow' from dual union all
select 2 , 4 , 'Green' from dual union all
select 2 , 5 , 'Red' from dual union all
select 3 , 6 , 'Yellow' from dual union all
select 3 , 7 , 'Yellow' from dual union all
select 3 , 8 , 'Yellow' from dual union all
select 3 , 9 , 'Red' from dual
)
select a_id, max( zone )
from data
group by a_id
A_ID MAX(ZO
---------- ------
1 Yellow
2 Red
3 Yellow

Oracle - update , decode and set value

I have a table e.g.
**Fruit Number**
Apple 5
Grape 9
Orange 1
Coconut 54
Mango 22
I want to :
Select the whole list
Put 'Apple, Mango,Coconut' at top of the list.
Update the current order, where number = rownum
The list should look as follows :
**Fruit Number**
Apple 1
Mango 2
Coconut 3
Grape 4
Orange 5
I tried the following however getting syntax issues ..
update tablename
set id = rownum
where fruit in (select fruit from table order by decode(fruit,'Apple',1,'Mango',2,'Coconut',3))
UPDATE (
SELECT ROWNUM+5 AS r, fruit, number FROM TABLE
WHERE fruit NOT IN ('Apple', 'Mango', 'Coconut', 'Grape', 'Orange')
UNION
SELECT 1 AS r, 'Apple' AS fruit, number FROM TABLE
UNION
SELECT 2 AS r, 'Mango' AS fruit, number FROM TABLE
UNION
SELECT 3 AS r, 'Coconut' AS fruit, number FROM TABLE
UNION
SELECT 4 AS r, 'Grape' AS fruit, number FROM TABLE
UNION
SELECT 5 AS r, 'Orange' AS fruit, number FROM TABLE
)
SET number = r;
Following query will do that for you.
UPDATE table SET number =
CASE fruit
WHEN 'Apple' THEN 1
WHEN 'Mango' THEN 2
WHEN 'Coconut' THEN 3
WHEN 'Grape' THEN 4
WHEN 'Orange' THEN 5
END;