If I have a structure like this:
CREATE TABLE things (
id,
personA varchar2,
personB varchar2,
attribute ...,
)
And I want to find, for a given attribute, if I have at least 1 common person for all my things, how would I go about it?
So if my data is (and it could be more than 2 per attribute):
1, John, Steve, Apple
2, Steve, Larry, Apple
3, Paul, Larry, Orange
4, Paul, Larry, Orange
5, Chris, Michael, Tomato
6, Steve, Larry, Tomato
For Apple, Steve is my common person, For Orange both Paul and Larry are, and for Tomato I have no common people. I don't need a query that returns all of these at once, however. I have one of these attributes and want 0, 1, or 2 rows depending on what kind of commonality I have. I've been trying to come up with something but can't quite figure out.
This will give you your common person / attribute list. I ran it against your sample data and got the expected result. Hope it's at least pointing in the right direction :)
WITH NormNames AS (
SELECT PersonA AS Person, Attribute FROM things
UNION ALL SELECT PersonB AS Person, Attribute FROM things
)
SELECT
Person, Attribute, COUNT(*)
FROM NormNames
GROUP BY Person, Attribute
HAVING COUNT(*) >= 2
If you're on 11gR2 you could also use the unpivot operator to avoid the self-join:
select person, attribute
from (
select *
from things
unpivot (person for which_person in (persona as 'A', personb as 'B'))
)
group by person, attribute
having count(*) > 1;
PERSON ATTRIBUTE
---------- ----------
Steve Apple
Paul Orange
Larry Orange
3 rows selected.
Or to just the the people who match the attribute, which I think is what the end of your question is looking for:
select person
from (
select *
from things
unpivot (person for which_person in (persona as 'A', personb as 'B'))
)
where attribute = 'Apple'
group by person, attribute
having count(*) > 1;
PERSON
----------
Steve
1 row selected.
The unpivot translates columns into rows. Run on its own it transforms your original six rows into twelve, replacing the original persona/personb columns with a single person and an additional column indicating which column the new row was formed from, which we don't really care about here:
select *
from things
unpivot (person for which_person in (persona as 'A', personb as 'B'));
ID ATTRIBUTE W PERSON
---------- ---------- - ----------
1 Apple A John
1 Apple B Steve
2 Apple A Steve
2 Apple B Larry
3 Orange A Paul
3 Orange B Larry
4 Orange A Paul
4 Orange B Larry
5 Tomato A Chris
5 Tomato B Michael
6 Tomato A Steve
6 Tomato B Larry
12 rows selected.
The outer query is then doing a simple group.
Here's one method.
It implements an unpivot method by cross-joining to a list of numbers (you could use the unpivot method Alex uses) and then joins the result set, hopefully with a hash join for added goodness.
with
row_generator as (
select 1 counter from dual union all
select 2 counter from dual),
data_generator as (
select
attribute,
id ,
case counter
when 1 then persona
when 2 then personb
end person
from
things,
row_generator)
select
t1.attribute,
t1.person
from
row_generator t1,
row_generator t2
where
t1.attribute = t2.attribute and
t1.person = t2.person and
t1.id != t2.id;
Related
I cannot quite find what I'm looking for, so here goes:
I'm looking for a way to get a count of the number of times an item occurs in an array across the entire table.
Imagine you have a table child_names with two columns - user_id and children
I know it's unusual to have two children with same name, but bear with me
.
user_id children
1 Bob, Jane, Bob
2 Jeff, Jane
3 Bob, Matt
4 Jane, John
I am looking for a result that would have two columns
Bob 3
Jane 3
Jeff 1
Matt 1
John 1
So far I have this
SELECT
ARRAY(
SELECT AS STRUCT child, `count`
FROM t.children child
LEFT JOIN (
SELECT AS STRUCT child, COUNT(1) `count`
FROM t.children child
GROUP BY child
) stats
USING(child)
) hashtag
FROM `child_names` t,
UNNEST(children)
But this gives me a count of how many children have that name per parent, not per table.
I get
Bob 2
Jane 1
Jeff 1
Jane 1
Bob 1
Matt 1
etc.
I hope that makes sense. Any help would be appreciated.
Use below
SELECT name, COUNT(*) cnt
FROM child_names,
UNNEST(children) name
GROUP BY name
if applied to sample data in your question - output is
So lets say I have a table
ID Car
1 Polo
2 Jetta
And a one-many relationship like
Id Driver Car
1 Jason 1
2 Fred 1
2 Fred 2
3 Batman 1
4 Frodo 2
How can I get records from car that Only has Fred and Frodo driving it?
I.e. the query needs to return only car 2.
Assuming that only has Fred and Frodo driving it means Fred, Frodo and no other, you would write:
SELECT car
FROM car_driver
GROUP BY car
HAVING COUNT(CASE WHEN driver IN ('Fred', 'Frodo') THEN 1 END) = 2
AND COUNT(*) = 2
You can use the set operator INTERSECT:
SELECT car FROM drivers WHERE driver = 'Fred'
INTERSECT
SELECT car FROM drivers WHERE driver = 'Frodo'
db<>fiddle
maybe something like this
select c.car
from cardriver cd join cars c
on c.id=cd.car and cd.Driver in ('Fred','Frodo')
group by c.car
having count(distinct cd.Driver)=2
db fiddle here
I have two tables.
The first table, named DATA, looks like:
DATE NAME DESCRIPTION
5/2 5 Orange Juice
5/4 2 Apple Juice
5/5 1 Cranberry Juice
5/6 1 Lemon Juice
The second table is called NAMES and looks like:
NAME_ID NAME
5 Bob
4 Frank
3 Megan
2 Tim
1 Brian
I want to, query all columns from the DATA table and perform a lookup on the NAME column, have it grab that number, look that number up in the NAMES table and replace that data in the DATA table, NAME column, so I would get something like:
DATE NAME DESCRIPTION
5/2 Bob Orange Juice
5/4 Tim Apple Juice
5/5 Brian Cranberry Juice
5/6 Brian Lemon Juice
I am a total SQL noob, so my attempts so far have been google the crap out of this, and I keep getting lead to subqueries, but that doesn't seem to solve the problem.
No, you don't need subqueries - but you do need joins. Sample data from line #1 - 10; query that does the job begins at line #12. I guess that "date" column actually is DATE datatype, but - for simplicity - I left it as a string.
SQL> with
2 -- sample data
3 data (datum, name, description) as
4 (select '5/2', 5, 'Orange Juice' from dual union all
5 select '5/4', 2, 'Apple Juice' from dual
6 ),
7 names (name_id, name) as
8 (select 5, 'Bob' from dual union all
9 select 2, 'Tim' from dual
10 )
11 -- query you probably need
12 select d.datum,
13 n.name,
14 d.description
15 from data d join names n on n.name_id = d.name
16 order by d.datum;
DATUM NAME DESCRIPTION
----- ----- ------------
5/2 Bob Orange Juice
5/4 Tim Apple Juice
SQL>
I have written a statement that retrieves the person name, as well as the food they've eaten.
Person | Food Eaten
John | Cake
Jack | Ice Cream
Louis | Hot Dog
John | Pineapple Pizza
Now that I've retrieved what foods people have eaten, I would like to remove anyone who has eaten Pineapple Pizza from my list.
What type of clause would I want to use to create a unique list of people who haven't eaten Pineapple Pizza?
You can use not exists :
select t.*
from table t
where not exists (select 1
from table t1
where t1.person = t.person and t1.food = 'Pineapple Pizza'
);
If you know the exact value of the food people have eaten, you can exclude it when you write your WHERE clause.
SELECT t.person, t.food_eaten
FROM eaten_tbl t
WHERE t.food_eaten <> 'Pineapple Pizza';
Say I have a table showing the type of fruit consumed by an individual over a 24 hour period that looks like this:
Name Fruit
Tim Apple
Tim Orange
Tim Orange
Tim Orange
Lisa Peach
Lisa Apple
Lisa Peach
Eric Plum
Eric Orange
Eric Plum
How would I get a table that shows only the most consumed fruit for each person, as well as the number of fruits consumed. In other words, a table that looks like this:
Name Fruit Number
Tim Orange 3
Lisa Peach 2
Eric Plum 2
I tried
SELECT Name, Fruit, Count(Fruit)
FROM table
GROUP BY Name
But that returns an error because Name needs to be in the GROUP BY statement as well. Every other method I've tried returns the counts for ALL values rather than just the maximum values. MAX(COUNT()) doesn't appear to be a valid statement, so I'm not sure what else to do.
This is a pain, but you can do it. Start with your query and then use join:
SELECT n.Name, n.Fruit
FROM (SELECT Name, Fruit, Count(Fruit) as cnt
FROM table as t
GROUP BY Name, Fruit
) as t INNER JOIN
(SELECT Name, max(cnt) as maxcnt
FROM (SELECT Name, Fruit, Count(Fruit) as cnt
FROM table
GROUP BY Name, Fruit
) as t
GROUP BY Name
) as n
ON t.name = n.name and t.cnt = n.maxcnt;