Değişken olarak case deyiminde 100'den fazla girişe sahip olma


11

Basit bir sorguda 4 yerde aynı ifadeyi kullandığım> 100 seçenekli bir vaka ifadesi yazdım.

Aynı sorgu aralarında birleşme ile iki kez ama aynı zamanda bir sayı yapıyor ve bu nedenle grup tarafından da vaka deyimi içerir.

Bu, aynı şirket için farklı kayıtların farklı şekilde yazıldığı bazı şirket adlarını yeniden etiketlemektir.

Bir değişkeni VarChar (MAX) olarak bildirmeye çalıştım

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

Seçme ifademde kullanmaya gittiğimde, sorgu sadece vaka ifadesini metin olarak döndürdü ve değerlendirmedi.

Ben de tarafından grupta kullanamadı - Bu hata iletisini aldım:

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

İdeal olarak, CASE'in tek bir yerde olmasını istiyorum - böylece bir satırı güncelleme ve başka bir yerde çoğaltmama şansım yok.

Bunu yapmanın bir yolu var mı?

Başka yollara açığım (belki bir fonksiyon gibi - ama bunları nasıl kullanacağımdan emin değilim)

İşte şu anda kullandığım SELECT'in bir örneği

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

Bu BİRLİĞİN amacı bir timeperiod için tüm verileri döndürmek ve ayrıca 12 ay önce aynı timeperiod için veri döndürmek

EDIT: Eksik "CATCH-ALL"
eklendi EDIT2: UNION deyiminin ikinci bir Added
EDIT3 eklendi: GROUP BY, bazı diğer gerekli öğeleri içerecek şekilde düzeltildi


BİRLİĞİN 2 kısmı nasıl farklı? Biraz farklı WHERE koşulları dışında oldukça benzer görünüyorlar.
ypercubeᵀᴹ

Temel fark budur. Tarihte iki farklı WHERE koşulu bugün ve aynı tarihi 12 ay önce vermektedir. Bu, daha sonra o gün ve aynı gün 12 ay önce sunum katmanındaki sayıları karşılaştırabileceğim - ancak tek SQL sorgusunu çalıştırabileceğim anlamına gelir.
kiltannen

3
Neden tek bir SELECT ile değil WHERE a.datecreated = CONVERT(DATE,now()) OR a.datecreated = DATEADD(YEAR,-1,CONVERT(DATE,now()))?
ypercubeᵀᴹ

@ ypercubeᵀᴹ Basit cevap, ilk başta bunu yaparken, BİRLİĞİ kullanan başka bir yerde yaptığım bir yolu kopyalıyordum. Biraz daha karmaşık olanı, tarih sınırlayıcının aslında bugünden ve aynı aydan 12 ay önce olduğundan daha karmaşık olmasıdır. Seçtiğim tarih aralığı 1 Temmuz'dan geçerli tarihe + 1 Temmuz'dan tam olarak 12 ay önceki tarihe kadar. (Bugüne Kadar Mali Yıl VS Son FY YTD 12 ay önce - bu büyüme veya mali yıl için başka bir karşılaştırma sağlar). ANCAK AndryM olarak & sen önermek, ben
sendika

Yanıtlar:


11

CASE ifadesinin tekrarını ortadan kaldırmanın kolay bir yolu, CROSS APPLY'yi şu şekilde kullanmaktır:

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 yardımıyla CASE ifadenize, ifadenizin herhangi bir yerinde referans verilebilecek şekilde bir ad atarsınız. Kesinlikle konuşmak gerekirse, hesaplanmış sütunu iç içe bir SELECT (ÇAPRAZ UYGULAMAYI izleyen FROM'suz SELECT) içinde tanımladığınız için çalışır .

Bu, türetilmiş bir tablonun takma adı sütununa başvurmakla aynıdır - teknik olarak bu yuvalanmış SELECT. Hem ilişkili bir alt sorgu hem de türetilmiş bir tablodur. İlişkili bir alt sorgu olarak, dış kapsamın sütunlarına başvuruda bulunulmasına izin verilir ve türetilmiş bir tablo olarak dış kapsamın tanımladığı sütunlara başvuruda bulunmasına izin verir.

Aynı CASE ifadesini kullanan bir UNION sorgusu için, her bacakta tanımlamanız gerekir, bunun için CASE yerine tamamen farklı bir değiştirme yöntemi kullanmak dışında bir çözüm yoktur. Bununla birlikte, özel durumunuzda sonuçları UNION olmadan almak mümkündür.

İki ayak sadece WHERE durumunda farklılık gösterir. Biri var:

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

ve diğeri bu:

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

Bunları şu şekilde birleştirebilirsiniz:

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

ve bu cevabın başında değiştirilmiş SELECT öğesine uygulayın.


İyi biri Andriy - +1! Sizden ilham aldım :-), cevabım için başka bir yaklaşım ekledim - a CTE- Hangisinin en iyi yaklaşım olduğundan emin değilim!
Vérace

Merhaba Andriy, bu çözümün görünümünü seviyorum. BİRLİĞİM olduğunu söylemiştim - ama bunu örneğime dahil etmeyecek kadar aptaldım. Şimdi yaptım. Bu x'in ÇAPRAZ UYGULAMADAN ŞİDDET'in ikinci yarısında bulunmadığından şüpheleniyorum, değil mi? Yani bu, CASE'in 2 kopyasıyla hala sıkışıp kaldığım anlamına gelir, doğru mu? (İşe döndüğümde yarın kontrol edeceğim)
kiltannen

@kiltannen Sütunu bırakın UNIONve yan tümcesinize datecreatedekleyin GROUP BY(ve yan tümceyi WHEREilgilendiğiniz her iki tarihi de içerecek şekilde güncelleyin ).
Scott M

@ScottM: OP'nin datecreatedGROUP BY sütununu içermesi gerektiğini düşünmüyorum . Bunun dışında, tamamen katılıyorum, sadece WHERE hükümlerini birleştirip BİRLİĞİ terk edebilirler.
Andriy M

@ scott-m Bunu yarın denemek zorunda kalacağım ama bunun pek işe yaramadığından şüpheleniyorum. Aslında bir gün değil - potansiyel olarak birkaç aydır. Karşılaştığım şeyin 11 aya kadar günlük verilere sahip olduğunu düşünüyorum - bu yüzden nerede başlayıp bitti ve 12 ay önce aynı dönem için bir OR çalıştırmak zorunda kaldım. Bence bu bir performans isabetiyle sonuçlandı. Tekrar denemek zorunda kalacağım - ama BİRLİĞİ yürütürken sahip olmadığım sorunlarla karşılaştığımı hatırlıyorum. Tabii ki bu kendi sorunlarını getiriyor. Şu anda güreştiğim gibi ..
kiltannen

22

Verileri bir tabloya yerleştirin

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

ve ona katılın.

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

Bu şekilde verileri birden fazla yerde güncel tutmaktan kaçınabilirsiniz. COALESCEİhtiyacınız olan yeri kullanın . Bunu VIEWdiğer önerilere göre CTE veya s'ye dahil edebilirsiniz .


4

Başka bir seçenek, birkaç yerde yeniden kullanmanız gerekiyorsa, Inline tablo değerli bir fonksiyonun iyi olacağını düşünüyorum.

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

Seçiminiz böyle olacak.

  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)

Ayrıca, bunu test etmedim ve kodun performansı da belirlenmelidir.

EDIT1 : Sanırım andriy zaten kod redacts çapraz uygulama kullanan bir verdik. Kodun diğer bölümlerinde aynı işlemi tekrarladığınız için, işlevdeki herhangi bir değişiklik her şeyi yansıtacağından, bu bir merkezileştirilebilir.


3

Bir kullanmak VIEWyapmaya çalışıyorsun ne yapmak. Tabii ki, temel verileri düzeltebilirsiniz, ancak bu sitede sık sık soru soranların (danışmanlar / dbas /) bunu yapma yetkisi yoktur. Bir VIEWkullanarak bu sorunu çözebilirsiniz! Bu UPPERfonksiyonu da kullandım - bu gibi durumlarda hataları çözmenin ucuz bir yolu.

Şimdi, sadece bir VIEWkez beyan ve her yerde kullanabilirsiniz! Bu şekilde, veri dönüştürme algoritmanızın depolandığı ve çalıştırıldığı yalnızca bir yeriniz olur ve böylece sisteminizin güvenilirliğini ve sağlamlığını artırır.

Ayrıca bir CTE ( Ortak Tablo İfadesi ) de kullanabilirsiniz - cevabın altına bakınız!

Sorunuzu cevaplamak için aşağıdakileri yaptım:

Örnek bir tablo oluşturun:

CREATE TABLE my_error (wrong VARCHAR(50));

Birkaç örnek kayıt ekleyin:

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

Ardından VIEWönerilen şekilde bir oluşturun :

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;

Ardından SELECT , hesabınızla ilgili VIEW:

SELECT * FROM my_error_view
ORDER BY wrong;

Sonuç:

ADDICTION ADVICE
AIR BP
AIR NEW ZEALAND

Et voilà!

Tüm bunları burada kemanda bulabilirsiniz .

CTEyaklaşım:

Yukarıdaki ile aynı, aşağıdaki gibi CTEikame VIEWedilir:

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;

Sonuç aynı. Daha sonra CTEbaşka bir tablo gibi tedavi edebilirsiniz - SELECTsadece s! Burada keman var .

Genel olarak, VIEWbu durumda yaklaşımın daha iyi olduğunu düşünüyorum !


0

Gömülü tablo

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 

Birliği atlayın ve ORbaşkalarının önerdiği yerlerde bir kullanın .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.