выберите * из таблицы, где column = что-то или, когда недоступно, column = что-то еще - PullRequest
2 голосов
/ 19 сентября 2011

Я ищу что-то более эффективное и / или более легкое для чтения, чем следующий запрос.Лучший способ объяснить этот вопрос - предоставить пример.

Предположим, что следующая структура таблицы в MySQL представляет, скажем, контекст локализации для различных строк в приложении, которое я создаю.

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);

Предположим, что в таблицу введены следующие значения:

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')
;

Я хочу выбрать все доступные записи для немецкого языка ("ge") и, если он недоступен, использовать текст на английском языке ("_")) по умолчанию.В настоящее время я использую следующий запрос:

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 действительно не нравится внешний вид этого запроса, и я уверен, что должно быть что-то более лаконичное, которое можно использовать вместо,Любая помощь или подсказки в правильном направлении будут оценены.Хотя это работает, оно просто не выглядит правильным .

Ответы [ 2 ]

3 голосов
/ 20 сентября 2011

Вы можете указать пользовательский порядок, а затем взять первую строку, например:

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

Объяснение:

Внутренний выбор order by field(LOCALE, 'ge', '_') возвращает строки в порядке,define - в данном случае немецкий сначала, если он существует, затем английский (вы могли бы добавить больше языков в список).

«Уловка» здесь заключается в использовании «нестандартного» поведения mysql GROUP BY, когда not перечисление столбцов, не относящихся к группе (большинство серверов воспринимают это как синтаксическую ошибку) - он просто возвращает первую строку, найденную для каждой группы.Внешний выбор использует group by без агрегата, чтобы получить первую строку для каждой именованной группы по.

Вывод этого запроса с использованием ваших данных:

+----------+--------+-----------+-------------+
| SET_NAME | LOCALE | ENTRY_KEY | ENTRY_VALUE |
+----------+--------+-----------+-------------+
| STD_TEXT | ge     | GOODBYE   | Lebewohl    |
| STD_TEXT | _      | HELLO     | Hello!      |
+----------+--------+-----------+-------------+
1 голос
/ 20 сентября 2011

Я думаю, что ваш запрос в порядке.Но вот другой подход:

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'
...