select * from table where column = something or, when unavailable, column = something else

68,619

Solution 1

You can provide a custom ordering, then take the first row, like this:

select * from (
    select *
    from LOCALIZATION_TABLE
    where SET_NAME = 'STD_TEXT'
    order by field(LOCALE, 'ge', '_') -- this provides the custom ordering
) x
group by ENTRY_KEY; -- this captures the first row for each ENTRY_KEY

Explanation:

The inner select's order by field(LOCALE, 'ge', '_') gets you the rows in the order you define - in this case German first if it exists, then English (you could add more languages to the list).

The "trick" here is using mysql's "non-standard" GROUP BY behaviour when not listing the non-group columns (most servers treat this as a syntax error) - it simply returns the first row found for every group. The outer select uses group by without an aggregate to get the first row for each named group by.

Output of this query using your data:

+----------+--------+-----------+-------------+
| SET_NAME | LOCALE | ENTRY_KEY | ENTRY_VALUE |
+----------+--------+-----------+-------------+
| STD_TEXT | ge     | GOODBYE   | Lebewohl    |
| STD_TEXT | _      | HELLO     | Hello!      |
+----------+--------+-----------+-------------+

Solution 2

I think your query is fine. But here's another approach:

SELECT
      en.SET_NAME 
    , COALESCE(ge.LOCALE, en.LOCALE) 
    , en.ENTRY_KEY 
    , COALESCE(ge.ENTRY_VALUE, en.ENTRY_VALUE)
FROM 
    LOCALIZATION_TABLE AS en
  LEFT JOIN 
    LOCALIZATION_TABLE AS ge
      ON  ge.ENTRY_KEY = en.ENTRY_KEY
      AND ge.LOCALE = 'ge'
      AND ge.SET_NAME = 'STD_TEXT'
WHERE
      en.LOCALE = '_' 
  AND en.SET_NAME = 'STD_TEXT'
Share:
68,619
Admin
Author by

Admin

Updated on December 15, 2020

Comments

  • Admin
    Admin over 3 years

    I am looking for something more efficient and/or easier to read than the query that follows. The best way to explain the question is to provide a sample.

    Assume the following table structure in MySQL that represents, say, a localization context for various strings in the application I am creating.

    create table LOCALIZATION_TABLE (
       SET_NAME    varchar(36) not null,
       LOCALE      varchar(8)  not null default '_',
       ENTRY_KEY   varhcar(36) not null,
       ENTRY_VALUE text            null
    );
    
    alter table LOCALIZATION_TABLE
      add constraint UQ_ENTRY
      unique (SET_NAME, LOCALE, ENTRY_KEY);
    

    Assume the following values are entered in the table:

    insert into LOCALIZATION_TABLE (SET_NAME, LOCALE, ENTRY_KEY, ENTRY_VALUE)
    values
      ('STD_TEXT', '_',  'HELLO', 'Hello!'),
      ('STD_TEXT', '_',  'GOODBYE', 'Goodbye.'),
      ('STD_TEXT', 'ge', 'GOODBYE', 'Lebewohl')
    ;
    

    I want to select all the available entries for German ("ge"), and if not available use the English text ("_") by default. The query I am currently using is as follows:

    select * from LOCALIZATION_TABLE where SET_NAME = 'STD_TEXT' and LOCALE = 'ge'
    union
    select * from LOCALIZATION_TABLE where SET_NAME = 'STD_TEXT' and LOCALE = '_'
      and ENTRY_KEY not in (
        select ENTRY_KEY from LOCALIZATION_TABLE where BUNDLE = 'STD_TEXT' and LOCALE = 'ge'
      )
    

    I really do not like the look of this query and I am certain there must be something more concise that can be utilized instead. Any help or clues to the right direction would be appreciated. While this works it just does not seem proper.

  • Admin
    Admin over 12 years
    That looks much more readable than the quick and dirty query I came up with. Thanks.