case 문에 100 개가 넘는 항목을 변수로 갖는 방법


11

간단한 쿼리로 4 곳에서 동일한 문장을 사용하는 100 개가 넘는 선택 사항이있는 사례 진술을 작성했습니다.

동일한 쿼리를 두 번의 통합으로 두 번 수행하지만 카운트를 수행하므로 그룹에 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

이 UNION의 목적은 한 기간 동안 모든 데이터를 반환하고 12 개월 동안 동일한 기간 동안 데이터를 반환하는 것입니다.

편집 : 누락 된 "CATCH-ALL"
EDIT2 추가 : UNION 문의 두 번째 1/2 추가
EDIT3 : 다른 필요한 요소를 포함하도록 GROUP BY 수정


UNION의 두 부분은 어떻게 다릅니 까? 그들은 약간 다른 조건을 제외하고는 상당히 비슷하게 보입니다.
ypercubeᵀᴹ

이것이 중요한 차이점입니다. 날짜의 두 가지 다른 조건은 오늘과 같은 날짜를 12 개월 전에 제공합니다. 즉, 12 개월 전에 프레젠테이션 계층에서 해당 날짜와 같은 날의 숫자를 비교할 수 있지만 단일 SQL 쿼리를 실행한다는 의미입니다.
kiltannen

3
왜 단일 SELECT를 사용하지 WHERE a.datecreated = CONVERT(DATE,now()) OR a.datecreated = DATEADD(YEAR,-1,CONVERT(DATE,now()))않습니까?
ypercubeᵀᴹ

@ ypercubeᵀᴹ 간단한 대답은 처음에 이것을 만들 때 UNION을 사용한 다른 곳에서 복사 한 방법을 복사하는 것입니다. 약간 더 복잡한 것은 날짜 제한 기가 실제로 오늘보다 12 개월 전에 같은 날짜보다 훨씬 복잡하다는 것입니다. 선택한 날짜 범위는 7 월 1 일부터 현재 날짜 +7 월 1 일 이전부터 정확히 12 개월 전까지입니다. 12 개월 전의 회계 연도부터 현재까지의 마지막 회계 연도 – 이것은 회계 연도의 성장 또는 그 밖의 비교를 제공합니다. 당신이 제안 및 AndryM 그러나, 나는 뺀 UNION하려고 하겠어
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-CROSS APPLY를 따르는 FROM-less SELECT 에서 계산 열을 정의하기 때문에 작동합니다 .

이것은 기술적으로이 중첩 된 SELECT 인 파생 테이블의 별칭 열을 참조하는 것과 같습니다. 상관 서브 쿼리와 파생 테이블입니다. 상관 서브 쿼리로서 외부 범위의 열을 참조 할 수 있으며 파생 된 테이블로서 외부 범위가 정의 된 열을 참조 할 수 있습니다.

동일한 CASE 표현식을 사용하는 UNION 쿼리의 경우 각 레그에서이를 정의해야하며 CASE 대신 완전히 다른 대체 방법을 사용하는 것 외에는 해결 방법이 없습니다. 그러나 특정 경우 UNION없이 결과를 가져올 수 있습니다.

두 다리는 WHERE 상태에서만 다릅니다. 하나는 이것을 가지고있다 :

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.
Vérace

안녕 Andriy, 나는이 솔루션의 모습을 좋아합니다. 나는 UNION을 가지고 있다고 언급했지만 내 예제에 포함시키지 않을 정도로 바보였습니다. 나는 지금 그렇게했다. CROSS APPLY 에서이 x가 UNION의 후반에 사용 가능하지 않을 것으로 생각됩니까? 이것은 CASE 사본 2 개가 여전히 붙어 있다는 것을 의미합니다. (내가 일을 다시 시작하면 내일 확인하겠습니다)
kiltannen

@kiltannen을 삭제하고 절에 열을 UNION포함시키고 관심있는 두 날짜를 모두 포함 하도록 절을 업데이트하십시오 . datecreatedGROUP BYWHERE
Scott M

@ ScottM : OP가 datecreatedGROUP BY에 열 을 포함해야한다고 생각하지 않습니다 . 그 외에도 WHERE 절을 결합하고 UNION을 버릴 수 있다고 전적으로 동의합니다.
Andriy M

@ scott-m 내일 이것을 시도해야하지만 그렇게 잘 작동하지 않는 것 같습니다. 실제로 하루는 아니지만 수개월이 소요될 수 있습니다. 나는 내가 겪은 일이 최대 11 개월의 일일 데이터를 가지고 있다고 생각합니다. 그래서 시작 및 종료 위치와 12 개월 전에 동일한 기간 동안 OR을 실행해야했습니다. 나는 이것이 성능 저하로 끝났다고 생각합니다. 다시 시도해야하지만 UNION을 실행할 때 없었던 문제가 발생했습니다. 물론 그것은 그 자체의 문제를 가져옵니다. 내가 현재 씨름하고있는 것과 같이 ..
kiltannen

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. VIEW다른 제안에 따라 이것을 CTE 또는 s에 통합 할 수 있습니다 .


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)

또한 이것을 테스트하지 않았으므로 코드의 성능도 결정해야합니다.

EDIT1 : andriy는 코드를 수정하는 cross apply를 사용하는 것을 이미 제공했다고 생각합니다. 글쎄, 이것은 코드의 다른 부분에서 똑같이 반복하기 때문에 함수의 변경 사항이 모두 반영되므로 중앙 집중화 될 수 있습니다.


3

나는 VIEW당신이하려는 일을하기 위해 a 를 사용할 것입니다. 물론 기본 데이터를 수정할 수는 있지만이 사이트에서 자주 질문하는 사람 (컨설턴트 / dbas /)은이 작업을 수행 할 권한이 없습니다. 를 사용하면 VIEW이 문제를 해결할 수 있습니다! 또한이 UPPER기능을 사용 했습니다.이 경우 오류를 해결하는 저렴한 방법입니다.

이제 VIEW한 번만 선언하면 어디서나 사용할 수 있습니다! 이렇게하면 데이터 변환 알고리즘이 저장되고 실행되는 위치가 한 곳만 있으므로 시스템의 안정성과 견고성이 향상됩니다.

CTE ( 공통 테이블 식 )를 사용할 수도 있습니다. 답변 하단을 참조하십시오!

귀하의 질문에 대답하기 위해 다음을 수행했습니다.

샘플 테이블을 작성하십시오.

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

vo!

이 모든 것을 바이올린 에서 찾을 수 있습니다 .

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. 여기에서 바이올린을 사용할 수 있습니다 .

전반적 VIEW으로이 경우 접근 방식이 더 좋다고 생각합니다 !


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다른 사람들이 제안한 곳에서를 사용하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.