简体   繁体   中英

Joining and filtering one-to-many relationship

I need some help about optimal structuring of SQL query. I have model like this:
数据库模型段

I'm trying some kind of join between tables NON_NATURAL_PERSON and NNP_NAME . Because I have many names in table NNP_NAME for one person I can't do one-to-one SELECT * from NON_NATURAL_PERSON inner join NNP_NAME etc. That way I'll get extra rows for every name one person has.

Data in tables:
人物资料 NON_NATURAL_PERSON 数据 NNP_NAME 数据
How to extend this query to get rows marked red on picture shown below? My wannabe query criteria is: Always join name of typeA only if exists. If not, join name of typeB. If neither exists join name of typeC.

SELECT nnp.ID, name.NAME, name.TYPE 
FROM NON_NATURAL_PERSON nnp
INNER JOIN NNP_NAME name ON (name.NON_NATURAL_PERSON = nnp.ID)

查询结果

If type is spelled exactly as it's written (typeA, typeB, typeC) then you can use MIN() function:

SELECT NON_NATURAL_PERSON, MIN(type) AS min_type
FROM NNP_NAME
GROUP BY NON_NATURAL_PERSON

if you also want the username you can use this query:

SELECT
  n1.NON_NATURAL_PERSON AS ID,
  n1.Name,
  n1.Type
FROM
  NNP_NAME n1 LEFT JOIN NNP_NAME n2
  ON n1.NON_NATURAL_PERSON = n2.NON_NATURAL_PERSON
     AND n1.Type > n2.type
WHERE
  n2.type IS NULL

Please see this fiddle . If Types are not literally sorted, change this line:

     AND n1.Type > n2.type

with this:

     AND FIELD(n1.Type, 'TypeA', 'TypeB', 'TypeC') >
         FIELD(n2.type, 'TypeA', 'TypeB', 'TypeC')

MySQL FIELD(str, str1, str2, ...) function returns the index (position) of str in the str1, str2, ... list, and 0 if str is not found. You want to get the "first" record, ordered by type, for every NON_NATURAL_PERSON. There are multiple ways to get this info, I chose a self join:

ON n1.NON_NATURAL_PERSON = n2.NON_NATURAL_PERSON
   AND n1.Type > n2.type -- or filed function

with the WHERE condition:

WHERE n2.type IS NULL

this will return all rows where the join didn't succeed - the join won't succeed when there is not n2.type that is less than n1.type - it will return the first record.

Edit

If you want a platform independent solution, avoiding the creation of new tables, you could use CASE WHEN, just change

AND n1.Type > n2.Type

with

AND
  CASE
    WHEN n1.Type='TypeA' THEN 1
    WHEN n1.Type='TypeB' THEN 2
    WHEN n1.Type='TypeC' THEN 3
  END
  >
  CASE
    WHEN n2.Type='TypeA' THEN 1
    WHEN n2.Type='TypeB' THEN 2
    WHEN n2.Type='TypeC' THEN 3
  END

There is a piece of information missing. You say:

Always join name of typeA only if exists. If not, join name of typeB. If neither exists join name of typeC.

But you do not indicate why you prefer typeA over typeB. This information is not included in your data. In the answer of @fthiella, either lexicographical is assumed, or an arbitrary order is given using FIELD . This is also the reason why two joins with the table nnp_name is necessary.

You can solve this problem by adding a table name_type (id, name, order) and changing the type column to contain the id. This will allow you to add the missing information in a clean way.

With an additional join with this new table, you will be able get the preferred nnp_name for each row.

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