简体   繁体   中英

Oracle find common value in two different columns

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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM