У меня есть запрос SQL Server, в котором один тип адреса может иметь несколько имен местоположений, но мне нужно указать только одно местоположение в зависимости от типа. Приоритет: если AddressType = Facility, вернуть это LocationName. В записи ниже я верну только Орландо.

    LocationName   AddressType  ID
    Orlando        Facility     123
    Phoenix        Billing      345
    LA             Office       678

Если AddressType = Facility отсутствует, но есть для Office, верните это местоположение. В записи ниже он вернет только Лос-Анджелес.

    Location       AddressType    ID
    Phoenix        Billing        345
    LA             Office         678

Если AddressType равен Billing only, то местоположение будет нулевым. В записи ниже это будет null:

    Location       AddressType   ID
    Phoenix        Billing      345

Я пробовал использовать оператор Case:

    select id, location, addresstype,
    CASE when addresstype = 'Facility' then 1
         when addresstype = 'Office' then 2
         when addresstype = 'Billing' then 3 end as addresstype_row_num
         into #test t
         from table

Из этого я создал еще один запрос для выбора минимального addresstype_row_num:

    select *
    from t
    where addresstype_row_num = (select (min addresstype_row_num)
                                 from t2
                                 where t.id = t2.id)

Однако это заняло слишком много времени и также возвращало что-то вроде декартова соединения. Есть ли лучший способ решить эту проблему?

1
jackstraw22 17 Июл 2017 в 22:58
Временная таблица требует, чтобы вы переписали все записи. Для больших наборов данных следует избегать временных сказок. Вы можете просто использовать вложенный подвыбор для того, что вы делаете, и это будет намного быстрее.
 – 
M T Head
18 Июл 2017 в 00:22
У вас есть пример того, что вы предлагаете?
 – 
jackstraw22
18 Июл 2017 в 00:35
Я не могу проверить правильность своего синтаксиса прямо сейчас, но могу опубликовать ответ, чтобы вы изменили синтаксис, если я допущу ошибку. Подходит пример. Он может варьироваться в зависимости от вашей платформы sql, но, скорее всего, будет таким же.
 – 
M T Head
18 Июл 2017 в 01:36

2 ответа

Вы можете использовать row_number():

select id, location, addresstype,
from (select t.*,
             row_number() over (partition by location
                                order by (case when addresstype = 'Facility' then 1
                                               when addresstype = 'Office' then 2
                                               when addresstype = 'Billing' then 3
                                          end)
                               ) as seqnum
      from table t
     ) t
where seqnum = 1;

case может быть немного громоздким. Один из способов обойти это - использовать charindex():

select id, location, addresstype,
from (select t.*,
             row_number() over (partition by location
                                order by charindex(addresstype, 'Facility,Office,Billing')
                               ) as seqnum
      from table t
     ) t
where seqnum = 1;

Примечание. Это накладывает ограничения на названия объектов (как написано). Они не могут перекрываться.

1
Gordon Linoff 17 Июл 2017 в 23:03
select id, location, addresstype, min(addresstype_row_num) as addresstype
from 
( 
select id, location, addresstype,
CASE when addresstype = 'Facility' then 1
     when addresstype = 'Office' then 2
     when addresstype = 'Billing' then 3 end as addresstype_row_num
     from table
) x 
group by id, location, addresstype 

- ## (пропустить временную таблицу)

0
M T Head 18 Июл 2017 в 01:39
Одна команда, делающая все, избегая временной таблицы, выполняется быстрее. Не совсем уверен в том, что вы просили, отвечаю ли я на то, о чем вы просите. Но этот тип sql оптимизирован для Sql. Возможно, вам придется подправить код, чтобы получить точные бизнес-правила, которые вы ищете.
 – 
M T Head
18 Июл 2017 в 01:40