Как иметь более 100 записей в выписке case в качестве переменной

11

Я написал оператор case с> 100 вариантами, где я использую один и тот же оператор в 4 местах в простом запросе.

Один и тот же запрос дважды с объединением между ними, но также выполняет подсчет, и поэтому в группе также содержится оператор case.

Это делается для того, чтобы переименовать некоторые названия компаний, где разные записи для одной и той же компании пишутся по-разному.

Я пытался объявить переменную как VarChar (MAX)

declare @CaseForAccountConsolidation varchar(max)

SET @CaseForAccountConsolidation = 'CASE 
       WHEN ac.accountName like ''AIR NEW Z%'' THEN ''AIR NEW ZEALAND''
       WHEN ac.accountName LIKE ''AIR BP%'' THEN ''AIR BP''
       WHEN ac.accountName LIKE ''ADDICTION ADVICE%'' THEN ''ADDICTION ADVICE''
       WHEN ac.accountName LIKE ''AIA%'' THEN ''AIA''
       ...

Когда я стал использовать его в своем операторе select - запрос просто возвратил оператор case в виде текста и не оценил его.

Я также не смог использовать его в группе - я получил это сообщение об ошибке:

Each GROUP BY expression must contain at least one column that is not an outer reference.

В идеале я хотел бы, чтобы CASE находился в одном месте - чтобы у меня не было возможности обновить одну строку и не копировать ее в другом месте.

Есть ли способ сделать это?

Я открыт для других способов (как, возможно, функция - но я не уверен, как использовать их, как это)

Вот образец SELECT, который я сейчас использую

SELECT 
   SUM(c.charge_amount) AS GSTExcl
   ,dl.FirstDateOfMonth AS MonthBilled
   ,dl.FirstDateOfWeek AS WeekBilled
   ,CASE 
       WHEN ac.accountName like 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
       WHEN ac.accountName LIKE 'AIR BP%' THEN 'AIR BP'
       WHEN ac.accountName LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
       WHEN ac.accountName LIKE 'AIA%' THEN 'AIA'
       ELSE ac.accountName
   END AS accountName
   ,dl.FinancialYear
   ,CONVERT(Date,c.date_charged) AS date_charged
FROM [accession] a
   LEFT JOIN account_code ac ON a.account_code_id = ac.account_code_id
   LEFT Join charge c ON a.accession_id = c.accession_id
   LEFT JOIN dateLookup dl ON convert(date,c.date_charged) = dl.date
WHERE a.datecreated = CONVERT(DATE,now())
GROUP BY
   dl.FirstDateOfMonth
   ,dl.FinancialYear
   ,dl.FirstDateOfWeek
   ,CONVERT(Date,c.date_charged)
   ,CASE 
       WHEN ac.accountName like 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
       WHEN ac.accountName LIKE 'AIR BP%' THEN 'AIR BP'
       WHEN ac.accountName LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
       WHEN ac.accountName LIKE 'AIA%' THEN 'AIA'
       ELSE ac.accountName
   END

UNION

SELECT 
   SUM(c.charge_amount) AS GSTExcl
   ,dl.FirstDateOfMonth AS MonthBilled
   ,dl.FirstDateOfWeek AS WeekBilled
   ,CASE 
       WHEN ac.accountName like 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
       WHEN ac.accountName LIKE 'AIR BP%' THEN 'AIR BP'
       WHEN ac.accountName LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
       WHEN ac.accountName LIKE 'AIA%' THEN 'AIA'
       ELSE ac.accountName
   END AS accountName
   ,dl.FinancialYear
   ,CONVERT(Date,c.date_charged) AS date_charged
FROM [accession] a
   LEFT JOIN account_code ac ON a.account_code_id = ac.account_code_id
   LEFT Join charge c ON a.accession_id = c.accession_id
   LEFT JOIN dateLookup dl ON convert(date,c.date_charged) = dl.date
WHERE a.datecreated = DATEADD(YEAR,-1,CONVERT(DATE,now()))
GROUP BY
   dl.FirstDateOfMonth
   ,dl.FinancialYear
   ,dl.FirstDateOfWeek
   ,CONVERT(Date,c.date_charged)
   ,CASE 
       WHEN ac.accountName like 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
       WHEN ac.accountName LIKE 'AIR BP%' THEN 'AIR BP'
       WHEN ac.accountName LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
       WHEN ac.accountName LIKE 'AIA%' THEN 'AIA'
       ELSE ac.accountName
   END

Цель этого СОЮЗА - вернуть все данные за период времени, а ТАКЖЕ вернуть данные за тот же период времени за 12 месяцев ранее.

РЕДАКТИРОВАТЬ: Добавлен недостающий «CATCH-ALL»
РЕДАКТИРОВАТЬ2: Добавлен второй ½ оператора UNION.
РЕДАКТИРОВАТЬ3: Исправлен GROUP BY, чтобы включить некоторые другие необходимые элементы

kiltannen
источник
Чем отличаются 2 части UNION? Они выглядят довольно схожими, за исключением немного отличающихся ГДЕ условий.
ypercubeᵀᴹ
Это ключевое отличие. Два разных условия WHERE на дату дают сегодня и ту же дату 12 месяцев назад. Это означает, что я могу затем сравнить числа для этого дня и того же дня 12 месяцев назад на уровне представления, но с помощью одного запроса SQL.
килтаннен
3
Почему не один SELECT с WHERE a.datecreated = CONVERT(DATE,now()) OR a.datecreated = DATEADD(YEAR,-1,CONVERT(DATE,now()))?
ypercubeᵀᴹ
@ ypercubeᵀᴹ Простой ответ заключается в том, что при создании этого файла я сначала копировал способ, которым я делал это где-то еще, используя UNION. Немного сложнее то, что ограничитель даты на самом деле довольно сложный, чем сегодня, и та же дата, что и 12 месяцев назад. Диапазон дат, который я выбираю, - с 1 июля до текущей даты + с 1 июля до этого и до даты, которая ровно 12 месяцев назад. (Финансовый год до даты против последнего финансового года с начала года 12 месяцев назад - это дает сравнение роста или другого финансового года). НО, как AndryM & вы предлагаете, я собираюсь попробовать минус СОЮЗ
kiltannen

Ответы:

11

Один из простых способов избежать повторения выражения CASE - использовать CROSS APPLY следующим образом:

SELECT 
   SUM(c.charge_amount) AS GSTExcl
   ,dl.FirstDateOfMonth AS MonthBilled
   ,dl.FirstDateOfWeek AS WeekBilled
   ,x.accountName
   ,dl.FinancialYear
   ,CONVERT(Date,c.date_charged) AS date_charged
FROM [accession] a
   LEFT JOIN account_code ac ON a.account_code_id = ac.account_code_id
   CROSS APPLY
   (
    SELECT 
       CASE 
           WHEN ac.accountName like 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
           WHEN ac.accountName LIKE 'AIR BP%' THEN 'AIR BP'
           WHEN ac.accountName LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
           WHEN ac.accountName LIKE 'AIA%' THEN 'AIA'
       END AS accountName
   ) AS x
   LEFT Join charge c ON a.accession_id = c.accession_id
   LEFT JOIN dateLookup dl ON convert(date,c.date_charged) = dl.date
GROUP BY
   dl.FirstDateOfMonth
   ,x.AccountName

С помощью CROSS APPLY вы назначаете имя своему выражению CASE таким образом, чтобы на него можно было ссылаться в любом месте вашего оператора. Это работает, потому что, строго говоря, вы определяете вычисляемый столбец во вложенном SELECT - безотборном SELECT, который следует за CROSS APPLY.

Это то же самое, что ссылаться на столбец с псевдонимом в производной таблице - технически это вложенный SELECT. Это и коррелированный подзапрос, и производная таблица. В качестве коррелированного подзапроса разрешается ссылаться на столбцы внешней области, а в качестве производной таблицы он позволяет внешней области ссылаться на столбцы, которые он определяет.

Для запроса UNION, использующего одно и то же выражение CASE, вы должны определить его в каждой ветви, для этого нет обходного пути, кроме использования совершенно другого метода замены вместо CASE. Тем не менее, в вашем конкретном случае можно получить результаты без UNION.

Две ноги отличаются только условием ГДЕ. Один имеет это:

WHERE a.datecreated = CONVERT(DATE,now())

а другой это:

WHERE a.datecreated = DATEADD(YEAR,-1,CONVERT(DATE,now()))

Вы можете объединить их так:

WHERE a.datecreated IN (
                        CONVERT(DATE,now()),
                        DATEADD(YEAR,-1,CONVERT(DATE,now()))
                       )

и примените его к измененному SELECT в начале этого ответа.

Андрей М
источник
Милый Андрей - +1! Вдохновленный вами :-), я добавил другой подход к своему ответу - а CTE- я не уверен, что это лучший подход!
Верас
Привет Андрей, мне нравится внешний вид этого решения. Я упоминал, что у меня был СОЮЗ, но я был достаточно глуп, чтобы не включать его в мой пример. Я сделал это сейчас. Я подозреваю, что этот x из CROSS APPLY вряд ли будет доступен для второй половины UNION, не так ли? Так что это будет означать, что я все еще застрял с двумя копиями CASE, верно? (Я проверю это завтра, когда вернусь на работу)
kiltannen
@kiltannen Удалите UNIONи просто включите datecreatedстолбец в ваше GROUP BYпредложение (и обновите WHEREпредложение, включив в него обе интересующие вас даты).
Скотт М
@ ScottM: я не думаю, что OP должен включать datecreatedстолбец в GROUP BY. Помимо этого, я полностью согласен, они могут просто объединить предложения WHERE и отказаться от UNION.
Андрей М
@ scott-m Мне придется попробовать это завтра, но я подозреваю, что это не так хорошо работает. Это на самом деле не один день - потенциально несколько месяцев. Я думаю, что я столкнулся с тем, что имел до 11 месяцев ежедневных данных - так, где было начало и конец И затем я должен был провести операционную операцию за тот же период 12 месяцев назад. Я думаю, что это закончилось ударом производительности. Мне пришлось бы попробовать еще раз - но я помню, что столкнулся с проблемами, которых у меня не было при запуске UNION. Конечно, это приносит свои проблемы. Как тот, с которым я в настоящее время
борюсь
22

Положите данные в таблицу

CREATE TABLE AccountTranslate (wrong VARCHAR(50), translated(VARCHAR(50));

INSERT INTO AccountTranslate VALUES ('ADDICTION ADVICE%','ADDICTION ADVICE');
INSERT INTO AccountTranslate VALUES ('AIR BP%','AIR BP');
INSERT INTO AccountTranslate VALUES ('AIR NEW Z%', 'AIR NEW ZEALAND');

и присоединиться к нему.

SELECT ...,COALESCE(AccountTranslate.translated, ac.accountName) AS accountName
FROM
...., 
account_code ac left outer join 
AccountTranslate at on ac.accountName LIKE AccountTranslate.wrong

Таким образом, вы можете избежать обновления данных в нескольких местах. Просто используйте там, COALESCEгде вам это нужно. Вы можете включить это в CTE или VIEWs согласно другим предложениям.

LoztInSpace
источник
4

Я думаю, что еще один вариант, если вам нужно использовать его несколько раз, будет полезной функция с табличными таблицами.

CREATE FUNCTION dbo.itvf_CaseForAccountConsolidation
    ( @au_lname VARCHAR(8000) ) 
RETURNS TABLE 
RETURN 
SELECT  
  CASE
    WHEN UPPER(@au_lname) LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
    WHEN UPPER(@au_lname) LIKE 'AIR BP%'  THEN 'AIR BP'
    WHEN UPPER(@au_lname) LIKE 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
    ELSE '****ERROR****'  -- you may or may not need this! 
                         -- If converting every record, then yes, if not, then no!
                         -- Errors should stand out on browsing and it's easy to search for!
  END AS wrong

--Copied from verace

Ваш выбор будет таким.

  SELECT 
   SUM(c.charge_amount) AS GSTExcl
   ,dl.FirstDateOfMonth AS MonthBilled
   ,dl.FirstDateOfWeek AS WeekBilled
   ,dd.wrong AS accountName
   ,dl.FinancialYear
   ,CONVERT(Date,c.date_charged) AS date_charged
FROM [accession] a
   LEFT JOIN account_code ac ON a.account_code_id = ac.account_code_id
   LEFT Join charge c ON a.accession_id = c.accession_id
   LEFT JOIN dateLookup dl ON convert(date,c.date_charged) = dl.date
   CROSS APPLY  dbo.itvf_CaseForAccountConsolidation( ac.accountName)dd
GROUP BY
   dl.FirstDateOfMonth 
   ,dl.FirstDateOfWeek 
   ,wrong 
   ,dl.FinancialYear
   ,CONVERT(Date,c.date_charged)

Кроме того, я не проверял это, и производительность кода также должна быть определена.

РЕДАКТИРОВАТЬ 1 : Я думаю, что Андрей уже дал один, который использует Cross App, который редактирует код. Ну, это можно централизовать, так как любые изменения в функции будут отражаться во всех, так как вы повторяете то же самое в других частях кода.

Бижу Хосе
источник
3

Я бы использовал, VIEWчтобы сделать то, что вы пытаетесь сделать. Конечно, вы могли бы исправить исходные данные, но часто на этом сайте те, кто задает вопросы (consultants / dbas /), не имеют полномочий делать это. Использование VIEWможет решить эту проблему! Я также использовал UPPERфункцию - дешевый способ устранения ошибок в подобных случаях.

Теперь вы объявляете только VIEWодин раз и можете использовать его где угодно! Таким образом, у вас есть только одно место, в котором хранится и работает ваш алгоритм преобразования данных, что повышает надежность и надежность вашей системы.

Вы также можете использовать CTE ( Common Table Expression ) - см. Нижнюю часть ответа!

Чтобы ответить на ваш вопрос, я сделал следующее:

Создайте образец таблицы:

CREATE TABLE my_error (wrong VARCHAR(50));

Вставьте пару примеров записей:

INSERT INTO my_error VALUES ('Addiction Advice Services Ltd.');
INSERT INTO my_error VALUES ('AIR BP_and-mistake');
INSERT INTO my_error VALUES ('AIR New Zealand Airlines');

Затем создайте VIEWкак предложено:

CREATE VIEW my_error_view AS 
SELECT 
  CASE
    WHEN UPPER(wrong) LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
    WHEN UPPER(wrong) LIKE 'AIR BP%'  THEN 'AIR BP'
    WHEN UPPER(wrong) LIKE 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
    ELSE '***ERROR****' -- You may or may not need this.
                        -- It's attention grabbing (report) and easy to search for (SQL)!
  END AS wrong
FROM my_error;

Тогда SELECT из вашего VIEW:

SELECT * FROM my_error_view
ORDER BY wrong;

Результат:

ADDICTION ADVICE
AIR BP
AIR NEW ZEALAND

И вуаля!

Вы можете найти все это на скрипке здесь .

CTEПодход:

То же, что и выше, за исключением того, CTEчто заменяется VIEWследующим:

WITH my_cte AS
(
  SELECT 
  CASE
    WHEN UPPER(wrong) LIKE 'ADDICTION ADVICE%' THEN 'ADDICTION ADVICE'
    WHEN UPPER(wrong) LIKE 'AIR BP%'  THEN 'AIR BP'
    WHEN UPPER(wrong) LIKE 'AIR NEW Z%' THEN 'AIR NEW ZEALAND'
    ELSE '****ERROR****'  -- you may or may not need this! 
                         -- If converting every record, then yes, if not, then no!
                         -- Errors should stand out on browsing and it's easy to search for!
  END AS wrong
  FROM my_error
)
SELECT * FROM my_cte;

Результат тот же. Затем вы можете обращаться с CTEтаблицей так же, как с любой другой таблицей - SELECTтолько для s! Скрипка доступна здесь .

В целом, я думаю, что VIEWподход лучше в этом случае!

Verace
источник
0

Встроенный стол

select id, tag, trans.val 
  from [consecutive] c
  join ( values ('AIR NEW Z%', 'AIR NEW ZEALAND'),
                ('AIR BP%',    'AIR BP')
       ) trans (lk, val)
    on c.description like trans.lk 

Пропустите союз и используйте там, ORгде это предложено другими.

папараццо
источник