简体   繁体   中英

SQL - selecting only certain rows if they exist

I have a table that contains Home addresses and Mailing addresses. It looks like this:

ID   Name   StNum   StName     City    State   Zip    Type
--   ----   -----   ------     ----    -----   ---    ----
1    Joe    1234    Main St    Waco    TX      76767  HOM
1    Joe    2345    High St    Waco    TX      76763  MLG
2    Amy    3456    Broad St   Athens  GA      34622  HOM
3    Mel    987     Front St   Cary    NC      65331  HOM
3    Mel    1111    Main Ave   Hilo    HI      99779  MLG

I need to write an SQL statement that will only return the Mailing address (MLG record) if it exists, and if not, will return the Home address (HOM record).

The expected results from this table would be:

ID   Name   StNum   StName     City    State   Zip    Type
--   ----   -----   ------     ----    -----   ---    ----
1    Joe    2345    High St    Waco    TX      76763  MLG
2    Amy    3456    Broad St   Athens  GA      34622  HOM
3    Mel    1111    Main Ave   Hilo    HI      99779  MLG

Any help that you can provide would be much appreciated! Thanks!

use correlated subquery

select * from
(
select *,case when Type='MLG' then 1 else 0 end as typeval
from tablename
)A where typeval in (select max(case when Type='MLG' then 1 else 0 end) from tablename b 
where a.name=b.name)

OR if your DB supports row_number() then u can try below -

select * from
(
select *, row_number() over(partition by name order by case when Type='MLG' then 1 else 0 end desc)
from tablename
)A where rn=1

In case you are using SQL Server, i would solve it with the ROW_NUMBER function.

SELECT ID, Name, StNum, StName, City, State, Zip, Type
FROM (
    SELECT *
          ,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Type DESC) AS Rn
      FROM yourtable
      ) 
  WHERE Rn = 1

This can be done using a WHERE clause that exclude the ids of the users who have MLG

Schema (MySQL v5.7)

CREATE TABLE test (
  `ID` INTEGER,
  `Name` VARCHAR(3),
  `StNum` INTEGER,
  `StName` VARCHAR(8),
  `City` VARCHAR(6),
  `State` VARCHAR(2),
  `Zip` INTEGER,
  `Type` VARCHAR(3)
);

INSERT INTO test
  (`ID`, `Name`, `StNum`, `StName`, `City`, `State`, `Zip`, `Type`)
VALUES
  ('1', 'Joe', '1234', 'Main St', 'Waco', 'TX', '76767', 'HOM'),
  ('1', 'Joe', '2345', 'High St', 'Waco', 'TX', '76763', 'MLG'),
  ('2', 'Amy', '3456', 'Broad St', 'Athens', 'GA', '34622', 'HOM'),
  ('3', 'Mel', '987', 'Front St', 'Cary', 'NC', '65331', 'HOM'),
  ('3', 'Mel', '1111', 'Main Ave', 'Hilo', 'HI', '99779', 'MLG');

Query #1

SELECT id,
       name,
       StNum,
       StName,
       City,
       State,
       Zip,
       Type
FROM test t1
WHERE t1.`Type` = 'MLG'
   OR t1.id NOT IN
   (
        SELECT id
        FROM test t2
        WHERE t2.`Type` = 'MLG'
   );

Output :

| id  | name | StNum | StName   | City   | State | Zip   | Type |
| --- | ---- | ----- | -------- | ------ | ----- | ----- | ---- |
| 1   | Joe  | 2345  | High St  | Waco   | TX    | 76763 | MLG  |
| 2   | Amy  | 3456  | Broad St | Athens | GA    | 34622 | HOM  |
| 3   | Mel  | 1111  | Main Ave | Hilo   | HI    | 99779 | MLG  |

View on DB Fiddle


Or, my first dumb version :

This can be done using UNION

Schema (MySQL v5.7)

CREATE TABLE test (
  `ID` INTEGER,
  `Name` VARCHAR(3),
  `StNum` INTEGER,
  `StName` VARCHAR(8),
  `City` VARCHAR(6),
  `State` VARCHAR(2),
  `Zip` INTEGER,
  `Type` VARCHAR(3)
);

INSERT INTO test
  (`ID`, `Name`, `StNum`, `StName`, `City`, `State`, `Zip`, `Type`)
VALUES
  ('1', 'Joe', '1234', 'Main St', 'Waco', 'TX', '76767', 'HOM'),
  ('1', 'Joe', '2345', 'High St', 'Waco', 'TX', '76763', 'MLG'),
  ('2', 'Amy', '3456', 'Broad St', 'Athens', 'GA', '34622', 'HOM'),
  ('3', 'Mel', '987', 'Front St', 'Cary', 'NC', '65331', 'HOM'),
  ('3', 'Mel', '1111', 'Main Ave', 'Hilo', 'HI', '99779', 'MLG');

Query #1

SELECT id,
       name,
       StNum,
       StName,
       City,
       State,
       Zip,
       Type
FROM test t1
WHERE t1.`Type` = 'MLG'
UNION ALL
SELECT id,
       name,
       StNum,
       StName,
       City,
       State,
       Zip,
       Type
FROM test t2
WHERE t2.id NOT IN (SELECT id FROM test t3 WHERE t3.`Type` = 'MLG')
ORDER BY id;

Output

| id  | name | StNum | StName   | City   | State | Zip   | Type |
| --- | ---- | ----- | -------- | ------ | ----- | ----- | ---- |
| 1   | Joe  | 2345  | High St  | Waco   | TX    | 76763 | MLG  |
| 2   | Amy  | 3456  | Broad St | Athens | GA    | 34622 | HOM  |
| 3   | Mel  | 1111  | Main Ave | Hilo   | HI    | 99779 | MLG  |

View on DB Fiddle

This is a prioritization query. With two values, often the simplest method is union all with not exists (or not in ).

That does not generalize well For more values, using row_number() with case is convenient:

select t.*
from (select t.*,
             row_number() over (partition by id
                                order by (case when type = 'MLG' then 1 else 2 end)
                               ) as seqnum
      from t
     ) t
where seqnum = 1;

In your particular case, you could use order by type desc , because the two types happen to be prioritized in reverse alphabetical ordering. However, I recommend using case because the intention is more explicit.

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